From 58197e125b2974040c5952e232d4ec3574e3cbff Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Thu, 11 Apr 2019 15:14:23 +0800 Subject: [PATCH 001/476] +) singleton --- .../main/java/syntax/enums/SingletonDemo.java | 5 ++-- .../src/main/java/syntax/package-info.java | 2 -- java-class/src/main/resources/logback.xml | 3 ++- .../github/kuangcp/singleton/DoubleCheck.java | 23 +++++++++++++++++ .../singleton/DoubleCheckWithVolatile.java | 25 +++++++++++++++++++ .../com/github/kuangcp/singleton/Enum.java | 14 +++++++++++ .../com/github/kuangcp/singleton/Readme.md | 7 ++++++ .../github/kuangcp/singleton/StaticBlock.java | 22 ++++++++++++++++ .../github/kuangcp/singleton/StaticFinal.java | 17 +++++++++++++ .../github/kuangcp/singleton/StaticInit.java | 21 ++++++++++++++++ .../kuangcp/singleton/StaticInitWithSync.java | 21 ++++++++++++++++ .../kuangcp/singleton/StaticInnerClass.java | 22 ++++++++++++++++ 12 files changed, 177 insertions(+), 5 deletions(-) create mode 100644 java-pattern/src/main/java/com/github/kuangcp/singleton/DoubleCheck.java create mode 100644 java-pattern/src/main/java/com/github/kuangcp/singleton/DoubleCheckWithVolatile.java create mode 100644 java-pattern/src/main/java/com/github/kuangcp/singleton/Enum.java create mode 100644 java-pattern/src/main/java/com/github/kuangcp/singleton/Readme.md create mode 100644 java-pattern/src/main/java/com/github/kuangcp/singleton/StaticBlock.java create mode 100644 java-pattern/src/main/java/com/github/kuangcp/singleton/StaticFinal.java create mode 100644 java-pattern/src/main/java/com/github/kuangcp/singleton/StaticInit.java create mode 100644 java-pattern/src/main/java/com/github/kuangcp/singleton/StaticInitWithSync.java create mode 100644 java-pattern/src/main/java/com/github/kuangcp/singleton/StaticInnerClass.java diff --git a/java-class/src/main/java/syntax/enums/SingletonDemo.java b/java-class/src/main/java/syntax/enums/SingletonDemo.java index 38bb3aca..d59a1012 100644 --- a/java-class/src/main/java/syntax/enums/SingletonDemo.java +++ b/java-class/src/main/java/syntax/enums/SingletonDemo.java @@ -5,6 +5,7 @@ /** * created by https://gitee.com/gin9 * use enum create singleton + * * @author kuangcp on 2/17/19-9:22 AM */ @Slf4j @@ -12,8 +13,8 @@ public enum SingletonDemo { INSTANCE; - public void doA() { - log.info("invoke doA"); + public void invoke() { + log.info("invoke {}", this); } } diff --git a/java-class/src/main/java/syntax/package-info.java b/java-class/src/main/java/syntax/package-info.java index 1c924821..3762dfc0 100644 --- a/java-class/src/main/java/syntax/package-info.java +++ b/java-class/src/main/java/syntax/package-info.java @@ -2,7 +2,5 @@ * Created by https://github.com/kuangcp * 1. 基本常用数据类型的学习 * 2. 异常处理 - * @author kuangcp - * @date 18-4-14 下午6:39 */ package syntax; \ No newline at end of file diff --git a/java-class/src/main/resources/logback.xml b/java-class/src/main/resources/logback.xml index 35ff2b46..c32ae9fe 100644 --- a/java-class/src/main/resources/logback.xml +++ b/java-class/src/main/resources/logback.xml @@ -5,7 +5,8 @@ - %d{HH:mm:ss.SSS} %highlight(%-5level) [%thread] %logger{36}:%yellow(%-3L) %msg%n + %d{HH:mm:ss.SSS} %highlight(%-5level) [%thread] %logger{36}:%yellow(%-3L) %msg%n + DEBUG diff --git a/java-pattern/src/main/java/com/github/kuangcp/singleton/DoubleCheck.java b/java-pattern/src/main/java/com/github/kuangcp/singleton/DoubleCheck.java new file mode 100644 index 00000000..73476de5 --- /dev/null +++ b/java-pattern/src/main/java/com/github/kuangcp/singleton/DoubleCheck.java @@ -0,0 +1,23 @@ +package com.github.kuangcp.singleton; + +/** + * 双重校验 (线程安全,同步代码块)[不可用] + * + * @author kuangcp on 2019-04-11 12:41 PM + */ +public class DoubleCheck { + + private static DoubleCheck singleton; + + private DoubleCheck() { + } + + public static DoubleCheck getInstance() { + if (singleton == null) { + synchronized (DoubleCheck.class) { + singleton = new DoubleCheck(); + } + } + return singleton; + } +} diff --git a/java-pattern/src/main/java/com/github/kuangcp/singleton/DoubleCheckWithVolatile.java b/java-pattern/src/main/java/com/github/kuangcp/singleton/DoubleCheckWithVolatile.java new file mode 100644 index 00000000..f4203e26 --- /dev/null +++ b/java-pattern/src/main/java/com/github/kuangcp/singleton/DoubleCheckWithVolatile.java @@ -0,0 +1,25 @@ +package com.github.kuangcp.singleton; + +/** + * 双重检查[推荐用] + * + * @author kuangcp on 2019-04-11 12:41 PM + */ +public class DoubleCheckWithVolatile { + + private static volatile DoubleCheckWithVolatile singleton; + + private DoubleCheckWithVolatile() { + } + + public static DoubleCheckWithVolatile getInstance() { + if (singleton == null) { + synchronized (DoubleCheckWithVolatile.class) { + if (singleton == null) { + singleton = new DoubleCheckWithVolatile(); + } + } + } + return singleton; + } +} diff --git a/java-pattern/src/main/java/com/github/kuangcp/singleton/Enum.java b/java-pattern/src/main/java/com/github/kuangcp/singleton/Enum.java new file mode 100644 index 00000000..982f0917 --- /dev/null +++ b/java-pattern/src/main/java/com/github/kuangcp/singleton/Enum.java @@ -0,0 +1,14 @@ +package com.github.kuangcp.singleton; + +/** + * 枚举 推荐用 + * @author kuangcp on 2019-04-11 12:42 PM + */ +public enum Enum { + INSTANCE; + + public void whateverMethod() { + + } +} + diff --git a/java-pattern/src/main/java/com/github/kuangcp/singleton/Readme.md b/java-pattern/src/main/java/com/github/kuangcp/singleton/Readme.md new file mode 100644 index 00000000..fcdd6fb3 --- /dev/null +++ b/java-pattern/src/main/java/com/github/kuangcp/singleton/Readme.md @@ -0,0 +1,7 @@ +# 单例模式 +- 双重校验加上 volatile +- 静态内部类 +- 枚举 + +https://www.cnblogs.com/zhaoyan001/p/6365064.html + diff --git a/java-pattern/src/main/java/com/github/kuangcp/singleton/StaticBlock.java b/java-pattern/src/main/java/com/github/kuangcp/singleton/StaticBlock.java new file mode 100644 index 00000000..f5278e45 --- /dev/null +++ b/java-pattern/src/main/java/com/github/kuangcp/singleton/StaticBlock.java @@ -0,0 +1,22 @@ +package com.github.kuangcp.singleton; + +/** + * 饿汉式(静态代码块) + * + * @author kuangcp on 2019-04-11 12:38 PM + */ +public class StaticBlock { + + private static StaticBlock instance; + + static { + instance = new StaticBlock(); + } + + private StaticBlock() { + } + + public static StaticBlock getInstance() { + return instance; + } +} diff --git a/java-pattern/src/main/java/com/github/kuangcp/singleton/StaticFinal.java b/java-pattern/src/main/java/com/github/kuangcp/singleton/StaticFinal.java new file mode 100644 index 00000000..689210d4 --- /dev/null +++ b/java-pattern/src/main/java/com/github/kuangcp/singleton/StaticFinal.java @@ -0,0 +1,17 @@ +package com.github.kuangcp.singleton; + +/** + * 饿汉式(静态常量) + * @author kuangcp on 2019-04-11 12:37 PM + */ +public class StaticFinal { + + private static final StaticFinal instance = new StaticFinal(); + + private StaticFinal() { + } + + public static StaticFinal getInstance() { + return instance; + } +} diff --git a/java-pattern/src/main/java/com/github/kuangcp/singleton/StaticInit.java b/java-pattern/src/main/java/com/github/kuangcp/singleton/StaticInit.java new file mode 100644 index 00000000..0c8fb6bc --- /dev/null +++ b/java-pattern/src/main/java/com/github/kuangcp/singleton/StaticInit.java @@ -0,0 +1,21 @@ +package com.github.kuangcp.singleton; + +/** + * 懒汉式(线程不安全)[不可用] + * + * @author kuangcp on 2019-04-11 12:39 PM + */ +public class StaticInit { + + private static StaticInit singleton; + + private StaticInit() { + } + + public static StaticInit getInstance() { + if (singleton == null) { + singleton = new StaticInit(); + } + return singleton; + } +} diff --git a/java-pattern/src/main/java/com/github/kuangcp/singleton/StaticInitWithSync.java b/java-pattern/src/main/java/com/github/kuangcp/singleton/StaticInitWithSync.java new file mode 100644 index 00000000..a1aabaeb --- /dev/null +++ b/java-pattern/src/main/java/com/github/kuangcp/singleton/StaticInitWithSync.java @@ -0,0 +1,21 @@ +package com.github.kuangcp.singleton; + +/** + * 懒汉式(线程安全,同步方法)[不推荐用] + * + * @author kuangcp on 2019-04-11 12:40 PM + */ +public class StaticInitWithSync { + + private static StaticInitWithSync singleton; + + private StaticInitWithSync() { + } + + public synchronized static StaticInitWithSync getInstance() { + if (singleton == null) { + singleton = new StaticInitWithSync(); + } + return singleton; + } +} diff --git a/java-pattern/src/main/java/com/github/kuangcp/singleton/StaticInnerClass.java b/java-pattern/src/main/java/com/github/kuangcp/singleton/StaticInnerClass.java new file mode 100644 index 00000000..a16b1fec --- /dev/null +++ b/java-pattern/src/main/java/com/github/kuangcp/singleton/StaticInnerClass.java @@ -0,0 +1,22 @@ +package com.github.kuangcp.singleton; + +/** + * 静态内部类[推荐用] + * + * @author kuangcp on 2019-04-11 12:42 PM + */ +public class StaticInnerClass { + + private StaticInnerClass() { + } + + private static class SingletonInstance { + + private static final StaticInnerClass INSTANCE = new StaticInnerClass(); + } + + public static StaticInnerClass getInstance() { + return SingletonInstance.INSTANCE; + } + +} From f5db76a57e61321586c3940170687749885c6f14 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Thu, 11 Apr 2019 20:21:50 +0800 Subject: [PATCH 002/476] *) update inherit --- java-8/Readme.md | 5 +++ .../github/kuangcp/inherit/SameFieldTest.java | 40 +++++++++++++++++++ .../java/syntax/doubles/DoubleConstTest.java | 1 - .../java/syntax/innerclass/ServerConfig.java | 3 -- .../abstractfactory/FactoryProducer.java | 4 +- 5 files changed, 47 insertions(+), 6 deletions(-) create mode 100644 java-8/Readme.md create mode 100644 java-class/src/test/java/com/github/kuangcp/inherit/SameFieldTest.java diff --git a/java-8/Readme.md b/java-8/Readme.md new file mode 100644 index 00000000..0bba2cfb --- /dev/null +++ b/java-8/Readme.md @@ -0,0 +1,5 @@ +# Java8 +> [Java8 笔记](https://github.com/Kuangcp/Memo/blob/master/Java/AdvancedLearning/Java8.md) + +- [Java 8 in action](https://github.com/java8/Java8InAction) +- [Java 8 ](https://github.com/brianway/java-learning/tree/master/java8) diff --git a/java-class/src/test/java/com/github/kuangcp/inherit/SameFieldTest.java b/java-class/src/test/java/com/github/kuangcp/inherit/SameFieldTest.java new file mode 100644 index 00000000..f04d91b4 --- /dev/null +++ b/java-class/src/test/java/com/github/kuangcp/inherit/SameFieldTest.java @@ -0,0 +1,40 @@ +package com.github.kuangcp.inherit; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.IsEqual.equalTo; + +import org.junit.Test; + +/** + * RTTI C++ 中的概念, Java中没有, 但是 Thinking in java 里面用了这个概念, 实际上是有歧义的 + * 多态是隐式地利用RTTI,反射则是显式地使用RTTI + * + * 运行时类型识别(RTTI, Run-Time Type Identification)是Java中非常有用的机制,在Java运行时,RTTI维护类的相关信息。 + * 多态(polymorphism)是基于RTTI实现的。RTTI的功能主要是由Class类实现的。 + * + * @author kuangcp on 2019-04-11 12:54 PM + */ +public class SameFieldTest { + + public static class A { + + int age = 1; + } + + public static class B extends A { + + int age = 3; + } + + @Test + public void testSame() { + A a = new B(); + B b = new B(); + + System.out.println(a.getClass()); + System.out.println(b.getClass()); + + // TODO ? + assertThat(a.age + b.age, equalTo(4)); + } +} diff --git a/java-class/src/test/java/syntax/doubles/DoubleConstTest.java b/java-class/src/test/java/syntax/doubles/DoubleConstTest.java index f87cb8e6..e43331f5 100644 --- a/java-class/src/test/java/syntax/doubles/DoubleConstTest.java +++ b/java-class/src/test/java/syntax/doubles/DoubleConstTest.java @@ -15,5 +15,4 @@ public class DoubleConstTest { public void testExtremum(){ log.info("{} {}", ShowBinary.toBinaryString(Double.MIN_VALUE), ShowBinary.toBinaryString(Double.MIN_NORMAL)); } - } diff --git a/java-class/src/test/java/syntax/innerclass/ServerConfig.java b/java-class/src/test/java/syntax/innerclass/ServerConfig.java index 9298ed82..1ec6b3fe 100644 --- a/java-class/src/test/java/syntax/innerclass/ServerConfig.java +++ b/java-class/src/test/java/syntax/innerclass/ServerConfig.java @@ -18,8 +18,6 @@ public ServerConfig(ServerConfig.Builder builder) { this.port = builder.port; } - // 如果没有这个方法, 内部类就无需 static 声明 - // TODO 原因 public static ServerConfig defaultConfig() { return new ServerConfig.Builder().build(); } @@ -43,5 +41,4 @@ public ServerConfig build() { return new ServerConfig(this); } } - } diff --git a/java-pattern/src/main/java/com/github/kuangcp/abstractfactory/FactoryProducer.java b/java-pattern/src/main/java/com/github/kuangcp/abstractfactory/FactoryProducer.java index 023a495d..b7e5a89a 100644 --- a/java-pattern/src/main/java/com/github/kuangcp/abstractfactory/FactoryProducer.java +++ b/java-pattern/src/main/java/com/github/kuangcp/abstractfactory/FactoryProducer.java @@ -5,9 +5,9 @@ /** * @author kuangcp on 2019-04-07 12:40 AM */ -public class FactoryProducer { +class FactoryProducer { - public static Optional getFactory(String choice) { + static Optional getFactory(String choice) { if (choice.equalsIgnoreCase("SHAPE")) { return Optional.of(new ShapeFactory()); } else if (choice.equalsIgnoreCase("COLOR")) { From aef643bc42aebfd6ad806a043eff7e1d4373fe36 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Sun, 14 Apr 2019 17:03:18 +0800 Subject: [PATCH 003/476] *) change crlf to lf --- .gitignore | 46 +- README.md | 7 +- build.gradle | 96 +- .../github/kuangcp/found/BinarySearch.java | 68 +- .../java/com/github/kuangcp/sort/Box.java | 46 +- .../java/com/github/kuangcp/sort/Bubble.java | 54 +- .../java/com/github/kuangcp/sort/Insert.java | 60 +- .../java/com/github/kuangcp/sort/Quick.java | 256 +++--- .../java/com/github/kuangcp/sort/Select.java | 50 +- .../java/com/github/kuangcp/sort/Shell.java | 138 +-- .../reflects/ReflectTargetObjectTest.java | 3 +- .../java/com/github/kuangcp/ArrayMaker.java | 64 +- .../java/com/github/kuangcp/file/CutTxt.java | 254 +++--- .../com/github/kuangcp/inout/CopyFile.java | 322 +++---- .../bio/chattingroom/ClientThread.java | 78 +- .../github/kuangcp/bio/chattingroom/README.md | 14 +- .../bio/chattingroom/ServerThread.java | 118 +-- .../bio/chattingroom/SocketClient.java | 92 +- .../bio/chattingroom/SocketServer.java | 64 +- .../kuangcp/bio/onechatone/ChatMaps.java | 102 +-- .../kuangcp/bio/onechatone/ChatProtocol.java | 42 +- .../github/kuangcp/bio/onechatone/Client.java | 190 ++-- .../kuangcp/bio/onechatone/ClientThread.java | 72 +- .../github/kuangcp/bio/onechatone/README.md | 24 +- .../github/kuangcp/bio/onechatone/Server.java | 62 +- .../kuangcp/bio/onechatone/ServerThread.java | 152 ++-- .../com/github/kuangcp/nio/NioClient.java | 168 ++-- .../com/github/kuangcp/nio/NioServer.java | 402 ++++----- .../com/github/kuangcp/port/LogicThread.java | 120 +-- .../github/kuangcp/port/LogicThreadTest.java | 44 +- .../github/kuangcp/port/MulSocketServer.java | 100 +-- .../kuangcp/runable/GreetingClient.java | 154 ++-- .../kuangcp/runable/GreetingServer.java | 148 ++-- .../kuangcp/selfclose/SocketSelfClose.java | 70 +- .../simpleMethod/SimplexMethod/Equality.java | 124 +-- .../SimplexMethod/SimplexMethod.java | 590 ++++++------- .../simpleMethod/SimplexMethod/Table.java | 170 ++-- .../SimplexMethodQuarter/Equality.java | 96 +- .../SimplexMethodQuarter/SimplexMethod.java | 818 +++++++++--------- .../SimplexMethodQuarter/Table.java | 92 +- .../simpleMethod/number/ReadProperties.java | 104 +-- .../resources/math/SimplexMethod.properties | 104 +-- .../src/main/resources/math/log4j.xml | 126 +-- .../SimplexMethodQuarter/MethodTest.java | 94 +- settings.gradle | 38 +- 45 files changed, 3019 insertions(+), 3017 deletions(-) diff --git a/.gitignore b/.gitignore index 449fe624..4a0ddc46 100644 --- a/.gitignore +++ b/.gitignore @@ -1,24 +1,24 @@ -# projcet # -*.log -logs/ - -# maven # -target/ - -# IDEA # -.idea/ -*.iml -out/ - -# eclipse # -bin/ -.settings/ -.metadata/ -.classpath -.project -Servers/ - -# Gradle # -.gradle -build/ +# projcet # +*.log +logs/ + +# maven # +target/ + +# IDEA # +.idea/ +*.iml +out/ + +# eclipse # +bin/ +.settings/ +.metadata/ +.classpath +.project +Servers/ + +# Gradle # +.gradle +build/ .gradletasknamecache \ No newline at end of file diff --git a/README.md b/README.md index bfbfe59f..9a58ee20 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,10 @@ # JavaBase -[![Maintainability](https://api.codeclimate.com/v1/badges/23134c0d2348845fecec/maintainability)](https://codeclimate.com/github/Kuangcp/JavaBase/maintainability) -[![codebeat badge](https://codebeat.co/badges/9145f9a8-a1aa-4c67-bb2b-f9dd12e924d4)](https://codebeat.co/projects/github-com-kuangcp-javabase-master) [![Java Version](https://img.shields.io/badge/JDK-Java%208-red.svg)](https://www.java.com/zh_CN/download/) -[![Gradle 5.2](https://img.shields.io/badge/Gradle-5.2-green.svg)](https://docs.gradle.org/5.2/userguide/userguide.html) +[![Gradle 5.3](https://img.shields.io/badge/Gradle-5.2-green.svg)](https://docs.gradle.org/5.3/userguide/userguide.html) +[![996.icu](https://img.shields.io/badge/link-996.icu-red.svg)](https://996.icu) +[![codebeat badge](https://codebeat.co/badges/9145f9a8-a1aa-4c67-bb2b-f9dd12e924d4)](https://codebeat.co/projects/github-com-kuangcp-javabase-master) +[![Maintainability](https://api.codeclimate.com/v1/badges/23134c0d2348845fecec/maintainability)](https://codeclimate.com/github/Kuangcp/JavaBase/maintainability) | 基础 | 进阶 | 应用框架 | |:----|:----|:----| diff --git a/build.gradle b/build.gradle index 68e21d8e..a55457b7 100644 --- a/build.gradle +++ b/build.gradle @@ -1,48 +1,48 @@ -apply from: 'dependency.gradle' - -// parent and child project all use this config -allprojects { - apply plugin: 'java' - apply plugin: 'maven' - - group = 'com.github.kuangcp' - - - // default build dir is build/ but idea compile dir is out/, i'm dislike this way - // dir out/ just have production/ and test/ dir - buildDir = 'out/build' - - sourceCompatibility = 1.8 - targetCompatibility = 1.8 - - repositories { - mavenLocal() - - def aliYun = "http://maven.aliyun.com/nexus/content/groups/public/" - def abroad = "http://central.maven.org/maven2/" - maven { - url = aliYun - artifactUrls abroad - } - maven { - url = "https://gitee.com/gin9/MavenRepos/raw/master" - } - jcenter() - } - - // All sub-modules add the following dependencies - dependencies { - - annotationProcessor rootProject.libs['lombok'] - compileOnly rootProject.libs['lombok'] - testAnnotationProcessor rootProject.libs['lombok'] - testCompileOnly rootProject.libs['lombok'] - - implementation rootProject.libs['logback-classic'] - implementation rootProject.libs['kcp-tool'] - - testImplementation rootProject.libs['junit'] - testImplementation rootProject.libs['hamcrest-core'] - testImplementation rootProject.libs['hamcrest-lib'] - } -} +apply from: 'dependency.gradle' + +// parent and child project all use this config +allprojects { + apply plugin: 'java' + apply plugin: 'maven' + + group = 'com.github.kuangcp' + + + // default build dir is build/ but idea compile dir is out/, i'm dislike this way + // dir out/ just have production/ and test/ dir + buildDir = 'out/build' + + sourceCompatibility = 1.8 + targetCompatibility = 1.8 + + repositories { + mavenLocal() + + def aliYun = "http://maven.aliyun.com/nexus/content/groups/public/" + def abroad = "http://central.maven.org/maven2/" + maven { + url = aliYun + artifactUrls abroad + } + maven { + url = "https://gitee.com/gin9/MavenRepos/raw/master" + } + jcenter() + } + + // All sub-modules add the following dependencies + dependencies { + + annotationProcessor rootProject.libs['lombok'] + compileOnly rootProject.libs['lombok'] + testAnnotationProcessor rootProject.libs['lombok'] + testCompileOnly rootProject.libs['lombok'] + + implementation rootProject.libs['logback-classic'] + implementation rootProject.libs['kcp-tool'] + + testImplementation rootProject.libs['junit'] + testImplementation rootProject.libs['hamcrest-core'] + testImplementation rootProject.libs['hamcrest-lib'] + } +} diff --git a/java-algorithms/src/main/java/com/github/kuangcp/found/BinarySearch.java b/java-algorithms/src/main/java/com/github/kuangcp/found/BinarySearch.java index ef373d03..011216c0 100644 --- a/java-algorithms/src/main/java/com/github/kuangcp/found/BinarySearch.java +++ b/java-algorithms/src/main/java/com/github/kuangcp/found/BinarySearch.java @@ -1,34 +1,34 @@ -package com.github.kuangcp.found; - -/** - * TODO 当数据有重复的时候,这个算法并不能做到返回数据第一次出现的地方 - */ -public class BinarySearch { - private static int index = -2; - - private static void found(int[] arr, int left, int right, int data) { - - int mid = (left + right) / 2; - if (arr[mid] == data) { - index = mid; - } - - if (arr[mid] > data && left < mid - 1) { - found(arr, left, mid, data); - } - - if (arr[mid] < data && right > mid + 1) { - found(arr, mid, right, data); - } - } - - /** - * find num from arr - * - * @return index not found then return -1 - */ - public int find(int[] arr, int data) { - found(arr, 0, arr.length, data); - return index + 1; - } -} +package com.github.kuangcp.found; + +/** + * TODO 当数据有重复的时候,这个算法并不能做到返回数据第一次出现的地方 + */ +public class BinarySearch { + private static int index = -2; + + private static void found(int[] arr, int left, int right, int data) { + + int mid = (left + right) / 2; + if (arr[mid] == data) { + index = mid; + } + + if (arr[mid] > data && left < mid - 1) { + found(arr, left, mid, data); + } + + if (arr[mid] < data && right > mid + 1) { + found(arr, mid, right, data); + } + } + + /** + * find num from arr + * + * @return index not found then return -1 + */ + public int find(int[] arr, int data) { + found(arr, 0, arr.length, data); + return index + 1; + } +} diff --git a/java-algorithms/src/main/java/com/github/kuangcp/sort/Box.java b/java-algorithms/src/main/java/com/github/kuangcp/sort/Box.java index 6be4a4a9..72a4f516 100644 --- a/java-algorithms/src/main/java/com/github/kuangcp/sort/Box.java +++ b/java-algorithms/src/main/java/com/github/kuangcp/sort/Box.java @@ -1,23 +1,23 @@ -package com.github.kuangcp.sort; - -/** - * 箱排序, - * - * @author Myth - */ -public enum Box implements SortAlgorithm { - - INSTANCE; - - public void sort(int[] arr) { - //盒子个数 - for (int i = 0; i < 1000; i++) { - for (int anArr : arr) { - if (anArr == i) { - // TODO 放入链队列中 - } - } - } - } - -} +package com.github.kuangcp.sort; + +/** + * 箱排序, + * + * @author Myth + */ +public enum Box implements SortAlgorithm { + + INSTANCE; + + public void sort(int[] arr) { + //盒子个数 + for (int i = 0; i < 1000; i++) { + for (int anArr : arr) { + if (anArr == i) { + // TODO 放入链队列中 + } + } + } + } + +} diff --git a/java-algorithms/src/main/java/com/github/kuangcp/sort/Bubble.java b/java-algorithms/src/main/java/com/github/kuangcp/sort/Bubble.java index eac9edef..ccc53c67 100644 --- a/java-algorithms/src/main/java/com/github/kuangcp/sort/Bubble.java +++ b/java-algorithms/src/main/java/com/github/kuangcp/sort/Bubble.java @@ -1,27 +1,27 @@ -package com.github.kuangcp.sort; - -/** - * 冒泡排序,从小到大 - * 最坏的情况:O(n^2) - * - * @author kcp - */ -public enum Bubble implements SortAlgorithm { - - INSTANCE; - - public void sort(int[] arr) { - - for (int i = 1; i < arr.length; i++) { - //用来冒泡的语句,0到已排序的部分 - for (int j = 0; j < arr.length - i; j++) { - //大就交换,把最大的沉入最后 - if (arr[j] > arr[j + 1]) { - int temp = arr[j + 1]; - arr[j + 1] = arr[j]; - arr[j] = temp; - } - } - } - } -} +package com.github.kuangcp.sort; + +/** + * 冒泡排序,从小到大 + * 最坏的情况:O(n^2) + * + * @author kcp + */ +public enum Bubble implements SortAlgorithm { + + INSTANCE; + + public void sort(int[] arr) { + + for (int i = 1; i < arr.length; i++) { + //用来冒泡的语句,0到已排序的部分 + for (int j = 0; j < arr.length - i; j++) { + //大就交换,把最大的沉入最后 + if (arr[j] > arr[j + 1]) { + int temp = arr[j + 1]; + arr[j + 1] = arr[j]; + arr[j] = temp; + } + } + } + } +} diff --git a/java-algorithms/src/main/java/com/github/kuangcp/sort/Insert.java b/java-algorithms/src/main/java/com/github/kuangcp/sort/Insert.java index 345b3751..75db2980 100644 --- a/java-algorithms/src/main/java/com/github/kuangcp/sort/Insert.java +++ b/java-algorithms/src/main/java/com/github/kuangcp/sort/Insert.java @@ -1,30 +1,30 @@ -package com.github.kuangcp.sort; - -/** - * 插入法排序,由小到大 - * 最坏的情况就是数列是有序的大到小,那么需要比较和移动 n(n+1)/2 次 时间复杂度是O(n^2) - * - * 指针从第二个数开始,后移,发现当前数比前面一个数小就把前面那个数后移,往前比较,知道找到那个数小于当前数为止,指针后移 - * 直至到最后一个 - * 思想是,指针位置之前的数都是有序的 - */ -public enum Insert implements SortAlgorithm { - - INSTANCE; - - public void sort(int[] arr) { - int i, j; - int data; - for (i = 1; i < arr.length; i++) { - data = arr[i]; - j = i - 1; - //比较,如果是比前面还要小,就将数字往后移动一位。将小的那一位插入到合适位置 - while (j >= 0 && data < arr[j]) { - arr[j + 1] = arr[j]; - j--; - } - arr[j + 1] = data; - } - } - -} +package com.github.kuangcp.sort; + +/** + * 插入法排序,由小到大 + * 最坏的情况就是数列是有序的大到小,那么需要比较和移动 n(n+1)/2 次 时间复杂度是O(n^2) + * + * 指针从第二个数开始,后移,发现当前数比前面一个数小就把前面那个数后移,往前比较,知道找到那个数小于当前数为止,指针后移 + * 直至到最后一个 + * 思想是,指针位置之前的数都是有序的 + */ +public enum Insert implements SortAlgorithm { + + INSTANCE; + + public void sort(int[] arr) { + int i, j; + int data; + for (i = 1; i < arr.length; i++) { + data = arr[i]; + j = i - 1; + //比较,如果是比前面还要小,就将数字往后移动一位。将小的那一位插入到合适位置 + while (j >= 0 && data < arr[j]) { + arr[j + 1] = arr[j]; + j--; + } + arr[j + 1] = data; + } + } + +} diff --git a/java-algorithms/src/main/java/com/github/kuangcp/sort/Quick.java b/java-algorithms/src/main/java/com/github/kuangcp/sort/Quick.java index 2038c3fe..d5103651 100644 --- a/java-algorithms/src/main/java/com/github/kuangcp/sort/Quick.java +++ b/java-algorithms/src/main/java/com/github/kuangcp/sort/Quick.java @@ -1,129 +1,129 @@ -package com.github.kuangcp.sort; - -/** - * 快速排序 - * 最坏的情况:当数列是一样的数据 O(n^2) - * 理想:O(n*log n) - * - * @author Myth - */ -public enum Quick implements SortAlgorithm { - - INSTANCE; - - @Override - public void sort(int[] data) { - sortData(data, 0, data.length - 1); - } - - /** - * 高效的写法 - */ - public void sortData(int[] data, int low, int high) { - if (low >= high) { - return; - } - - int lowIndex = low; - int highIndex = high; - - int value = data[low]; - while (lowIndex < highIndex) { - // 找出右边小于低位所在的标识值 - while (lowIndex < highIndex && data[highIndex] >= value) { - highIndex -= 1; - } - data[lowIndex] = data[highIndex]; - - // 找出左边大于标识值 - while (lowIndex < highIndex && data[lowIndex] <= value) { - lowIndex += 1; - } - data[highIndex] = data[lowIndex]; - } - - data[lowIndex] = value; - - sortData(data, low, lowIndex - 1); - sortData(data, highIndex + 1, high); - } - - public void sortData2(int[] data, int low, int high) { - int lowIndex = low; - int highIndex = high; - int index = data[low]; - - while (lowIndex < highIndex) { - while (lowIndex < highIndex && data[highIndex] >= index) { - highIndex--; - } - if (lowIndex < highIndex) { - int temp = data[highIndex]; - data[highIndex] = data[lowIndex]; - data[lowIndex] = temp; - lowIndex++; - } - - while (lowIndex < highIndex && data[lowIndex] <= index) { - lowIndex++; - } - if (lowIndex < highIndex) { - int temp = data[highIndex]; - data[highIndex] = data[lowIndex]; - data[lowIndex] = temp; - highIndex--; - } - } - - if (lowIndex > low) { - sortData2(data, low, lowIndex - 1); - } - if (highIndex < high) { - sortData2(data, lowIndex + 1, high); - } - } - - public > T[] quickSort(T[] targetArr, - int start, int end) { - int i = start + 1, j = end; - T key = targetArr[start]; - // SortUtilsUtil=newSortUtil(); - - if (start >= end) { - return (targetArr); - } - /* - * 从i++和j--两个方向搜索不满足条件的值并交换 - * - * 条件为:i++方向小于key,j--方向大于key - */ - while (true) { - while (targetArr[j].compareTo(key) > 0) { - j--; - } - while (targetArr[i].compareTo(key) < 0 && i < j) { - i++; - } - if (i >= j) { - break; - } - if (targetArr[i] == key) { - j--; - } else { - i++; - } - } - - /* 关键数据放到‘中间’ */ - // sUtil.swap(targetArr,start,j); - - if (start < i - 1) { - this.quickSort(targetArr, start, i - 1); - } - if (j + 1 < end) { - this.quickSort(targetArr, j + 1, end); - } - - return targetArr; - } +package com.github.kuangcp.sort; + +/** + * 快速排序 + * 最坏的情况:当数列是一样的数据 O(n^2) + * 理想:O(n*log n) + * + * @author Myth + */ +public enum Quick implements SortAlgorithm { + + INSTANCE; + + @Override + public void sort(int[] data) { + sortData(data, 0, data.length - 1); + } + + /** + * 高效的写法 + */ + public void sortData(int[] data, int low, int high) { + if (low >= high) { + return; + } + + int lowIndex = low; + int highIndex = high; + + int value = data[low]; + while (lowIndex < highIndex) { + // 找出右边小于低位所在的标识值 + while (lowIndex < highIndex && data[highIndex] >= value) { + highIndex -= 1; + } + data[lowIndex] = data[highIndex]; + + // 找出左边大于标识值 + while (lowIndex < highIndex && data[lowIndex] <= value) { + lowIndex += 1; + } + data[highIndex] = data[lowIndex]; + } + + data[lowIndex] = value; + + sortData(data, low, lowIndex - 1); + sortData(data, highIndex + 1, high); + } + + public void sortData2(int[] data, int low, int high) { + int lowIndex = low; + int highIndex = high; + int index = data[low]; + + while (lowIndex < highIndex) { + while (lowIndex < highIndex && data[highIndex] >= index) { + highIndex--; + } + if (lowIndex < highIndex) { + int temp = data[highIndex]; + data[highIndex] = data[lowIndex]; + data[lowIndex] = temp; + lowIndex++; + } + + while (lowIndex < highIndex && data[lowIndex] <= index) { + lowIndex++; + } + if (lowIndex < highIndex) { + int temp = data[highIndex]; + data[highIndex] = data[lowIndex]; + data[lowIndex] = temp; + highIndex--; + } + } + + if (lowIndex > low) { + sortData2(data, low, lowIndex - 1); + } + if (highIndex < high) { + sortData2(data, lowIndex + 1, high); + } + } + + public > T[] quickSort(T[] targetArr, + int start, int end) { + int i = start + 1, j = end; + T key = targetArr[start]; + // SortUtilsUtil=newSortUtil(); + + if (start >= end) { + return (targetArr); + } + /* + * 从i++和j--两个方向搜索不满足条件的值并交换 + * + * 条件为:i++方向小于key,j--方向大于key + */ + while (true) { + while (targetArr[j].compareTo(key) > 0) { + j--; + } + while (targetArr[i].compareTo(key) < 0 && i < j) { + i++; + } + if (i >= j) { + break; + } + if (targetArr[i] == key) { + j--; + } else { + i++; + } + } + + /* 关键数据放到‘中间’ */ + // sUtil.swap(targetArr,start,j); + + if (start < i - 1) { + this.quickSort(targetArr, start, i - 1); + } + if (j + 1 < end) { + this.quickSort(targetArr, j + 1, end); + } + + return targetArr; + } } \ No newline at end of file diff --git a/java-algorithms/src/main/java/com/github/kuangcp/sort/Select.java b/java-algorithms/src/main/java/com/github/kuangcp/sort/Select.java index a9051df5..d4e4cdbc 100644 --- a/java-algorithms/src/main/java/com/github/kuangcp/sort/Select.java +++ b/java-algorithms/src/main/java/com/github/kuangcp/sort/Select.java @@ -1,25 +1,25 @@ -package com.github.kuangcp.sort; - -/** - * 选择排序,小到大 - * 原理是:第一个依次与后面所有元素进行比较,遇到比自己小的就交换直到最后,第二轮就拿第二个元素去依次比较 - * 最坏的情况是: 时间复杂度是O(n^2) - */ -public enum Select implements SortAlgorithm { - - INSTANCE; - - public void sort(int[] arr) { - for (int i = 0; i < arr.length - 1; i++) { - //这个循环就是把较小数堆叠在数组头,依次与后面比较再交换 - for (int j = i + 1; j < arr.length; j++) { - if (arr[i] > arr[j]) { - int temp = arr[i]; - arr[i] = arr[j]; - arr[j] = temp; - } - } - } - } - -} +package com.github.kuangcp.sort; + +/** + * 选择排序,小到大 + * 原理是:第一个依次与后面所有元素进行比较,遇到比自己小的就交换直到最后,第二轮就拿第二个元素去依次比较 + * 最坏的情况是: 时间复杂度是O(n^2) + */ +public enum Select implements SortAlgorithm { + + INSTANCE; + + public void sort(int[] arr) { + for (int i = 0; i < arr.length - 1; i++) { + //这个循环就是把较小数堆叠在数组头,依次与后面比较再交换 + for (int j = i + 1; j < arr.length; j++) { + if (arr[i] > arr[j]) { + int temp = arr[i]; + arr[i] = arr[j]; + arr[j] = temp; + } + } + } + } + +} diff --git a/java-algorithms/src/main/java/com/github/kuangcp/sort/Shell.java b/java-algorithms/src/main/java/com/github/kuangcp/sort/Shell.java index 0635c0ff..880225e6 100644 --- a/java-algorithms/src/main/java/com/github/kuangcp/sort/Shell.java +++ b/java-algorithms/src/main/java/com/github/kuangcp/sort/Shell.java @@ -1,69 +1,69 @@ -package com.github.kuangcp.sort; - -import java.util.ArrayList; -import java.util.List; - -/** - * shell排序,从小到大 - * 算法思想: - * 对一个线性数据,先取一个随机数d1(d1<数据总数)将所有数据分成d组,将所有距离为d倍数的分成一组 - * 对各组进行直接插入排序,然后再取第二个数d2,直到dn=1(dn<……d3> dataList = new ArrayList<>(); - List arrs = new ArrayList<>(); - for (int anArr : arr) { - arrs.add(anArr); - } - dataList.add(arrs); - int last = arr.length; - int randomNum; - - boolean flag = true; - while (flag) { - randomNum = (int) (Math.random() * last - 1) + 1;//取得随机数.比上一次取得值要小 -// System.out.println("\n随机数 : "+d); - List> temp = new ArrayList<>(); -// int index = 0; - //将数据分组 - for (int i = 0; i < randomNum; i++) { - List dat = new ArrayList<>(); - for (List temps : dataList) { - for (int k = i; k < temps.size(); k += randomNum) { -// System.out.println(temps.get(k)); - if (temps.get(k) != null) { - dat.add(temps.get(k)); - } - } - } - int[] arrdat = new int[dat.size()]; - for (int h = 0; h < dat.size(); h++) { - arrdat[h] = dat.get(h); - } - Insert.INSTANCE.sort(arrdat);//直接插入排序 - if (randomNum == 1) { - System.arraycopy(arrdat, 0, arr, 0, arr.length); - flag = false; - } -// TestSortTime.display(arrdat); - temp.add(dat);//加入集合 -// System.out.println("缓存数组内存长度:"+dat.size()); - - } - dataList = temp; - last = randomNum; - } - } -} +package com.github.kuangcp.sort; + +import java.util.ArrayList; +import java.util.List; + +/** + * shell排序,从小到大 + * 算法思想: + * 对一个线性数据,先取一个随机数d1(d1<数据总数)将所有数据分成d组,将所有距离为d倍数的分成一组 + * 对各组进行直接插入排序,然后再取第二个数d2,直到dn=1(dn<……d3> dataList = new ArrayList<>(); + List arrs = new ArrayList<>(); + for (int anArr : arr) { + arrs.add(anArr); + } + dataList.add(arrs); + int last = arr.length; + int randomNum; + + boolean flag = true; + while (flag) { + randomNum = (int) (Math.random() * last - 1) + 1;//取得随机数.比上一次取得值要小 +// System.out.println("\n随机数 : "+d); + List> temp = new ArrayList<>(); +// int index = 0; + //将数据分组 + for (int i = 0; i < randomNum; i++) { + List dat = new ArrayList<>(); + for (List temps : dataList) { + for (int k = i; k < temps.size(); k += randomNum) { +// System.out.println(temps.get(k)); + if (temps.get(k) != null) { + dat.add(temps.get(k)); + } + } + } + int[] arrdat = new int[dat.size()]; + for (int h = 0; h < dat.size(); h++) { + arrdat[h] = dat.get(h); + } + Insert.INSTANCE.sort(arrdat);//直接插入排序 + if (randomNum == 1) { + System.arraycopy(arrdat, 0, arr, 0, arr.length); + flag = false; + } +// TestSortTime.display(arrdat); + temp.add(dat);//加入集合 +// System.out.println("缓存数组内存长度:"+dat.size()); + + } + dataList = temp; + last = randomNum; + } + } +} diff --git a/java-class/src/test/java/com/github/kuangcp/reflects/ReflectTargetObjectTest.java b/java-class/src/test/java/com/github/kuangcp/reflects/ReflectTargetObjectTest.java index d7f7aee5..8b314393 100644 --- a/java-class/src/test/java/com/github/kuangcp/reflects/ReflectTargetObjectTest.java +++ b/java-class/src/test/java/com/github/kuangcp/reflects/ReflectTargetObjectTest.java @@ -14,7 +14,8 @@ /** * Created by https://github.com/kuangcp on 17-10-24 上午9:50 - * 使用反射得到对象的 成员属性 + * 使用反射 修改对象的属性 static, final, 原始类型 等等 + * * https://stackoverflow.com/questions/3301635/change-private-static-final-field-using-java-reflection * http://www.cnblogs.com/noKing/p/9038234.html * diff --git a/java-generic/src/main/java/com/github/kuangcp/ArrayMaker.java b/java-generic/src/main/java/com/github/kuangcp/ArrayMaker.java index 2e98e0aa..a43f97af 100644 --- a/java-generic/src/main/java/com/github/kuangcp/ArrayMaker.java +++ b/java-generic/src/main/java/com/github/kuangcp/ArrayMaker.java @@ -1,32 +1,32 @@ -package com.github.kuangcp; - -import java.lang.reflect.Array; -import java.util.Arrays; - -/** - * 泛型类中创建, 类型变量类型的数组,使用Array.newInstance()是推荐的方式 - * 那么create方法就能创建类型变量约束的数组, 初始化为空 - * 暂时想到的用途就是泛型约束的创建 一个数组出来 - */ -public class ArrayMaker { - - private Class kind; - - private ArrayMaker(Class kind) { - this.kind = kind; - } - - @SuppressWarnings("unchecked") - private T[] create(int size) { - return (T[]) Array.newInstance(kind, size); - } - - public static void main(String[] args) { - ArrayMaker stringMaker = new ArrayMaker<>(String.class); - String[] stringArray = stringMaker.create(9); - System.out.println(Arrays.toString(stringArray)); - } -} -/* Output: -[null, null, null, null, null, null, null, null, null] -*/ +package com.github.kuangcp; + +import java.lang.reflect.Array; +import java.util.Arrays; + +/** + * 泛型类中创建, 类型变量类型的数组,使用Array.newInstance()是推荐的方式 + * 那么create方法就能创建类型变量约束的数组, 初始化为空 + * 暂时想到的用途就是泛型约束的创建 一个数组出来 + */ +public class ArrayMaker { + + private Class kind; + + private ArrayMaker(Class kind) { + this.kind = kind; + } + + @SuppressWarnings("unchecked") + private T[] create(int size) { + return (T[]) Array.newInstance(kind, size); + } + + public static void main(String[] args) { + ArrayMaker stringMaker = new ArrayMaker<>(String.class); + String[] stringArray = stringMaker.create(9); + System.out.println(Arrays.toString(stringArray)); + } +} +/* Output: +[null, null, null, null, null, null, null, null, null] +*/ diff --git a/java-io/src/main/java/com/github/kuangcp/file/CutTxt.java b/java-io/src/main/java/com/github/kuangcp/file/CutTxt.java index 26f912fc..beaf3970 100644 --- a/java-io/src/main/java/com/github/kuangcp/file/CutTxt.java +++ b/java-io/src/main/java/com/github/kuangcp/file/CutTxt.java @@ -1,127 +1,127 @@ -package com.github.kuangcp.file; - -import java.io.*; -import java.nio.charset.Charset; -import java.util.ArrayList; -import java.util.List; - -/** - * 搞定,成功在于找到特点,然后拆分,保存 - * Created by Myth on 2017/1/10 0010 - 22:25 - */ -public class CutTxt { - - //拆分文件根目录 - static String PATH = "E:\\GCDD\\"; - //源文件目录 - static String realFile="E:\\GCD.txt"; - static List rows = new ArrayList(); - static List titles = new ArrayList(); - public static void main(String[] a) { - ReadFile(); - for(int i=0;i chapters = new ArrayList(); - String content = null; - - //读取直到文件最末尾 - while ((content = br.readLine()) != null) { - count++; - //截取失败就返回原字符串 所以长度是1 -// String [] s = content.split("javascript"); -// System.out.println(count+" : "+s.length); - String test[] = content.split("javascript"); - if(test.length==1){ - chapters.add(content); -// System.out.println(count); - }else{ - //截取到了章节位置 - if(count>2) - //SaveFile(chapters); - chapters.clear(); -// System.out.println(count+"截取成功"); - chapters.add(count+""); - rows.add(count+""); - titles.add("【"+test[0]+" 】 "+test[1]); - //chapters.add(count+" "+runable[0]+" "+runable[1]); - } - -// System.out.println(count + "." + content); -// new String(content.getBytes("GBK"),"UTF-8") - } -// System.out.println(count+"循环退出,到达文件尾"); - //SaveFile(chapters); - } catch (IOException e) { - e.printStackTrace(); - } finally {//从里到外的封装关系 来关闭流 一旦顺序错了就有莫名奇妙的错误 - try { - input.close(); - ids.close(); - br.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - } - //将数组转存成文件 - public static void SaveFile(List datas){ - BufferedWriter bw = null; - OutputStream out = null; - OutputStreamWriter os = null; -// for(String e:dataStack){ -// System.out.println(e); -// } - if(datas.size()>1) { - try { - String filename = PATH + datas.get(0) + ".txt"; - filename.split(""); - System.out.println("文件名:"+filename); - if(filename.length()<40) { - out = new FileOutputStream(filename.trim()); - } - os = new OutputStreamWriter(out); - bw = new BufferedWriter(os); - - for (int i = 0; i < datas.size(); i++) { - bw.write(datas.get(i)+"\n"); - } - } catch (Exception e) { - e.printStackTrace(); - System.out.println("IO有异常"); - } finally { - try { - if (bw != null) bw.close(); - if (os != null) os.close(); - if (out != null) out.close(); - } catch (Exception e2) { - e2.printStackTrace(); - System.out.println("资源关闭异常"); - } - } - } - } -} +package com.github.kuangcp.file; + +import java.io.*; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.List; + +/** + * 搞定,成功在于找到特点,然后拆分,保存 + * Created by Myth on 2017/1/10 0010 - 22:25 + */ +public class CutTxt { + + //拆分文件根目录 + static String PATH = "E:\\GCDD\\"; + //源文件目录 + static String realFile="E:\\GCD.txt"; + static List rows = new ArrayList(); + static List titles = new ArrayList(); + public static void main(String[] a) { + ReadFile(); + for(int i=0;i chapters = new ArrayList(); + String content = null; + + //读取直到文件最末尾 + while ((content = br.readLine()) != null) { + count++; + //截取失败就返回原字符串 所以长度是1 +// String [] s = content.split("javascript"); +// System.out.println(count+" : "+s.length); + String test[] = content.split("javascript"); + if(test.length==1){ + chapters.add(content); +// System.out.println(count); + }else{ + //截取到了章节位置 + if(count>2) + //SaveFile(chapters); + chapters.clear(); +// System.out.println(count+"截取成功"); + chapters.add(count+""); + rows.add(count+""); + titles.add("【"+test[0]+" 】 "+test[1]); + //chapters.add(count+" "+runable[0]+" "+runable[1]); + } + +// System.out.println(count + "." + content); +// new String(content.getBytes("GBK"),"UTF-8") + } +// System.out.println(count+"循环退出,到达文件尾"); + //SaveFile(chapters); + } catch (IOException e) { + e.printStackTrace(); + } finally {//从里到外的封装关系 来关闭流 一旦顺序错了就有莫名奇妙的错误 + try { + input.close(); + ids.close(); + br.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + //将数组转存成文件 + public static void SaveFile(List datas){ + BufferedWriter bw = null; + OutputStream out = null; + OutputStreamWriter os = null; +// for(String e:dataStack){ +// System.out.println(e); +// } + if(datas.size()>1) { + try { + String filename = PATH + datas.get(0) + ".txt"; + filename.split(""); + System.out.println("文件名:"+filename); + if(filename.length()<40) { + out = new FileOutputStream(filename.trim()); + } + os = new OutputStreamWriter(out); + bw = new BufferedWriter(os); + + for (int i = 0; i < datas.size(); i++) { + bw.write(datas.get(i)+"\n"); + } + } catch (Exception e) { + e.printStackTrace(); + System.out.println("IO有异常"); + } finally { + try { + if (bw != null) bw.close(); + if (os != null) os.close(); + if (out != null) out.close(); + } catch (Exception e2) { + e2.printStackTrace(); + System.out.println("资源关闭异常"); + } + } + } + } +} diff --git a/java-io/src/main/java/com/github/kuangcp/inout/CopyFile.java b/java-io/src/main/java/com/github/kuangcp/inout/CopyFile.java index a1b0ec95..7089f38e 100644 --- a/java-io/src/main/java/com/github/kuangcp/inout/CopyFile.java +++ b/java-io/src/main/java/com/github/kuangcp/inout/CopyFile.java @@ -1,161 +1,161 @@ -package com.github.kuangcp.inout; - -import com.github.kuangcp.io.ResourceTool; -import java.io.BufferedReader; -import java.io.BufferedWriter; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.FileReader; -import java.io.FileWriter; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.nio.file.Files; -import java.nio.file.Path; -import lombok.extern.slf4j.Slf4j; - -/** - * 关于相对路径文件的复制问题 - * - * InputStream 所有输入流基类(建立流)(字节流) - * InputStreamReader 将字节流 转换成 字符流 - * BufferedReader 从字符流输入流读取文件 - * - * 注意: 如果输出流尚未关闭 内存的数据尚未刷新到硬盘上, 会导致不一致的情况 - */ -@Slf4j -class CopyFile { - - // 字节流 字节流转换为字符流 缓冲字符流 -> - void copyFileWithSixChannel(String from, String dest) { - InputStream inputStream = null; - InputStreamReader inputStreamReader = null; - BufferedReader bufferedReader = null; - - OutputStream outputStream = null; - OutputStreamWriter outputStreamWriter = null; - BufferedWriter bufferedWriter = null; - - try { - // 在 idea 中就是 project(工作目录) 根目录+path - inputStream = new FileInputStream(from); - inputStreamReader = new InputStreamReader(inputStream); - bufferedReader = new BufferedReader(inputStreamReader); - - outputStream = new FileOutputStream(dest); - outputStreamWriter = new OutputStreamWriter(outputStream); - bufferedWriter = new BufferedWriter(outputStreamWriter); - - //刷新缓存到硬盘中 - bufferedWriter.flush(); - - String L; - while ((L = bufferedReader.readLine()) != null) { - bufferedWriter.write(L + "\r\n"); - } - } catch (Exception e) { - log.error(e.getMessage(), e); - } finally { - try { - // 先打开后关闭 - ResourceTool.close(bufferedReader, inputStreamReader, inputStream); - log.info("close all input stream"); - - ResourceTool.close(bufferedWriter, outputStreamWriter, outputStream); - log.info("close all output stream"); - } catch (Exception e) { - log.error(e.getMessage(), e); - } - } - } - - // 字节流 - void copyFileByByte(String from, String dest) { - FileInputStream inputStream = null; - FileOutputStream outputStream = null; - - try { - inputStream = new FileInputStream(from); - outputStream = new FileOutputStream(dest); - - // 字节缓冲 数组 - byte[] buffer = new byte[1024]; - while (inputStream.read(buffer) != -1) { - outputStream.write(buffer); - } - } catch (Exception e) { - e.printStackTrace(); - } finally { - try { - ResourceTool.close(inputStream, outputStream); - } catch (IOException e) { - log.error(e.getMessage(), e); - } - } - } - - // 字符流 - void copyFileByChar(String from, String dest) { - FileReader fileReader = null; //输入流 - FileWriter fileWriter = null; //输出流 - - try { - fileReader = new FileReader(from); - fileWriter = new FileWriter(dest); - - //读入内存 - char[] p = new char[512]; - int n; - while ((n = fileReader.read(p)) != -1) { -// fileWriter.write(p);//不足512字符的话后面会乱码 - fileWriter.write(p, 0, n);//指定0-n长度 - - String cacheContent = new String(p, 0, n); - System.out.println(cacheContent); - } - - } catch (Exception e) { - e.printStackTrace(); - } finally { - try { - ResourceTool.close(fileReader, fileWriter); - } catch (Exception e) { - log.error(e.getMessage(), e); - } - } - } - - // 缓冲字符流 按行读取 - void copyFileByCharBuffer(String from, String dest) { - BufferedReader reader = null; - BufferedWriter writer = null; - - try { - reader = new BufferedReader(new FileReader(from)); - writer = new BufferedWriter(new FileWriter(dest)); - - String s; - while ((s = reader.readLine()) != null) { - log.info("line: {}", s); - //输出 - writer.write(s + "\r\n"); - } - log.info("成功读取并写入"); - } catch (Exception e) { - log.error(e.getMessage(), e); - } finally { - try { - ResourceTool.close(reader, writer); - } catch (Exception e) { - log.error(e.getMessage(), e); - } - } - } - - // Java7 中引入的 Files - void copyByFiles(Path from, Path dest) throws IOException { - Files.copy(from, dest); - } -} +package com.github.kuangcp.inout; + +import com.github.kuangcp.io.ResourceTool; +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.nio.file.Files; +import java.nio.file.Path; +import lombok.extern.slf4j.Slf4j; + +/** + * 关于相对路径文件的复制问题 + * + * InputStream 所有输入流基类(建立流)(字节流) + * InputStreamReader 将字节流 转换成 字符流 + * BufferedReader 从字符流输入流读取文件 + * + * 注意: 如果输出流尚未关闭 内存的数据尚未刷新到硬盘上, 会导致不一致的情况 + */ +@Slf4j +class CopyFile { + + // 字节流 字节流转换为字符流 缓冲字符流 -> + void copyFileWithSixChannel(String from, String dest) { + InputStream inputStream = null; + InputStreamReader inputStreamReader = null; + BufferedReader bufferedReader = null; + + OutputStream outputStream = null; + OutputStreamWriter outputStreamWriter = null; + BufferedWriter bufferedWriter = null; + + try { + // 在 idea 中就是 project(工作目录) 根目录+path + inputStream = new FileInputStream(from); + inputStreamReader = new InputStreamReader(inputStream); + bufferedReader = new BufferedReader(inputStreamReader); + + outputStream = new FileOutputStream(dest); + outputStreamWriter = new OutputStreamWriter(outputStream); + bufferedWriter = new BufferedWriter(outputStreamWriter); + + //刷新缓存到硬盘中 + bufferedWriter.flush(); + + String L; + while ((L = bufferedReader.readLine()) != null) { + bufferedWriter.write(L + "\r\n"); + } + } catch (Exception e) { + log.error(e.getMessage(), e); + } finally { + try { + // 先打开后关闭 + ResourceTool.close(bufferedReader, inputStreamReader, inputStream); + log.info("close all input stream"); + + ResourceTool.close(bufferedWriter, outputStreamWriter, outputStream); + log.info("close all output stream"); + } catch (Exception e) { + log.error(e.getMessage(), e); + } + } + } + + // 字节流 + void copyFileByByte(String from, String dest) { + FileInputStream inputStream = null; + FileOutputStream outputStream = null; + + try { + inputStream = new FileInputStream(from); + outputStream = new FileOutputStream(dest); + + // 字节缓冲 数组 + byte[] buffer = new byte[1024]; + while (inputStream.read(buffer) != -1) { + outputStream.write(buffer); + } + } catch (Exception e) { + e.printStackTrace(); + } finally { + try { + ResourceTool.close(inputStream, outputStream); + } catch (IOException e) { + log.error(e.getMessage(), e); + } + } + } + + // 字符流 + void copyFileByChar(String from, String dest) { + FileReader fileReader = null; //输入流 + FileWriter fileWriter = null; //输出流 + + try { + fileReader = new FileReader(from); + fileWriter = new FileWriter(dest); + + //读入内存 + char[] p = new char[512]; + int n; + while ((n = fileReader.read(p)) != -1) { +// fileWriter.write(p);//不足512字符的话后面会乱码 + fileWriter.write(p, 0, n);//指定0-n长度 + + String cacheContent = new String(p, 0, n); + System.out.println(cacheContent); + } + + } catch (Exception e) { + e.printStackTrace(); + } finally { + try { + ResourceTool.close(fileReader, fileWriter); + } catch (Exception e) { + log.error(e.getMessage(), e); + } + } + } + + // 缓冲字符流 按行读取 + void copyFileByCharBuffer(String from, String dest) { + BufferedReader reader = null; + BufferedWriter writer = null; + + try { + reader = new BufferedReader(new FileReader(from)); + writer = new BufferedWriter(new FileWriter(dest)); + + String s; + while ((s = reader.readLine()) != null) { + log.info("line: {}", s); + //输出 + writer.write(s + "\r\n"); + } + log.info("成功读取并写入"); + } catch (Exception e) { + log.error(e.getMessage(), e); + } finally { + try { + ResourceTool.close(reader, writer); + } catch (Exception e) { + log.error(e.getMessage(), e); + } + } + } + + // Java7 中引入的 Files + void copyByFiles(Path from, Path dest) throws IOException { + Files.copy(from, dest); + } +} diff --git a/java-network/src/main/java/com/github/kuangcp/bio/chattingroom/ClientThread.java b/java-network/src/main/java/com/github/kuangcp/bio/chattingroom/ClientThread.java index 17e0d0f5..3da1ef09 100644 --- a/java-network/src/main/java/com/github/kuangcp/bio/chattingroom/ClientThread.java +++ b/java-network/src/main/java/com/github/kuangcp/bio/chattingroom/ClientThread.java @@ -1,39 +1,39 @@ -package com.github.kuangcp.bio.chattingroom; - -import java.io.BufferedReader; -import java.io.InputStreamReader; -import java.net.Socket; -import lombok.extern.slf4j.Slf4j; - -/** - * Created by Myth on 2017/4/2 0002 - */ -@Slf4j -public class ClientThread implements Runnable { - - private String name; - private BufferedReader br; - - ClientThread(String name, Socket s) throws Exception { - this.name = name; - br = new BufferedReader(new InputStreamReader(s.getInputStream())); - } - - @Override - public void run() { - try { - String content; - //不断读取输入流中内容并打印输出 - while ((content = br.readLine()) != null) { - if (content.startsWith(name)) { - log.info("receive: self={}", content); - continue; - } - - log.info("receive: content={}", content); - } - } catch (Exception e) { - log.error(e.getMessage(), e); - } - } -} +package com.github.kuangcp.bio.chattingroom; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.net.Socket; +import lombok.extern.slf4j.Slf4j; + +/** + * Created by Myth on 2017/4/2 0002 + */ +@Slf4j +public class ClientThread implements Runnable { + + private String name; + private BufferedReader br; + + ClientThread(String name, Socket s) throws Exception { + this.name = name; + br = new BufferedReader(new InputStreamReader(s.getInputStream())); + } + + @Override + public void run() { + try { + String content; + //不断读取输入流中内容并打印输出 + while ((content = br.readLine()) != null) { + if (content.startsWith(name)) { + log.info("receive: self={}", content); + continue; + } + + log.info("receive: content={}", content); + } + } catch (Exception e) { + log.error(e.getMessage(), e); + } + } +} diff --git a/java-network/src/main/java/com/github/kuangcp/bio/chattingroom/README.md b/java-network/src/main/java/com/github/kuangcp/bio/chattingroom/README.md index fe14ede5..317bab22 100644 --- a/java-network/src/main/java/com/github/kuangcp/bio/chattingroom/README.md +++ b/java-network/src/main/java/com/github/kuangcp/bio/chattingroom/README.md @@ -1,8 +1,8 @@ - -- 使用简单的Socket通信,实现一个服务端,多个客户端来进行群聊的通信 -- 流程: - - 服务器启动 ServerSocket 并一直循环等待 - - 客户端启动与对应IP端口的serverSocket连接 服务端启动一个线程用于处理客户端请求 一对一方式 - - 从Socket对象得到输入输出流就可以实现通信了 - + +- 使用简单的Socket通信,实现一个服务端,多个客户端来进行群聊的通信 +- 流程: + - 服务器启动 ServerSocket 并一直循环等待 + - 客户端启动与对应IP端口的serverSocket连接 服务端启动一个线程用于处理客户端请求 一对一方式 + - 从Socket对象得到输入输出流就可以实现通信了 + 但是 客户端和服务端不知道对方的通断? \ No newline at end of file diff --git a/java-network/src/main/java/com/github/kuangcp/bio/chattingroom/ServerThread.java b/java-network/src/main/java/com/github/kuangcp/bio/chattingroom/ServerThread.java index 48e8868b..989ffbf5 100644 --- a/java-network/src/main/java/com/github/kuangcp/bio/chattingroom/ServerThread.java +++ b/java-network/src/main/java/com/github/kuangcp/bio/chattingroom/ServerThread.java @@ -1,59 +1,59 @@ -package com.github.kuangcp.bio.chattingroom; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.PrintStream; -import java.net.Socket; -import lombok.extern.slf4j.Slf4j; - -/** - * Created by Myth on 2017/4/2 0002 - * 负责处理每个线程通信的线程类 - */ -@Slf4j -public class ServerThread implements Runnable { - - //定义当前线程处理的Socket - private Socket s; - //该线程处理的Socket对应的输入流 - private BufferedReader br; - - ServerThread(Socket s) throws IOException { - this.s = s; - //初始化该输入流 - br = new BufferedReader(new InputStreamReader(s.getInputStream())); - } - - @Override - public void run() { - try { - String content; - //不断的从Socket中读取客户端发送过来的数据 - while ((content = readFromClient()) != null) { - log.info("#Chat# {}", content); - //遍历socketList中的每个Socket - for (Socket socket : SocketServer.socketList) { - //将读到的内容向所有 Socket 发送 - PrintStream printStream = new PrintStream(socket.getOutputStream()); - printStream.println(content); - } - } - } catch (Exception e) { - log.error(e.getMessage(), e); - } - } - - //定义读取客户端数据的方法 - private String readFromClient() { - try { - return br.readLine(); - //如果捕获到异常,则表明该Socket对应的客户端已经关闭 - } catch (Exception e) { - log.info("remove socket: socket={}", s); - SocketServer.socketList.remove(s); - log.error(e.getMessage(), e); - } - return null; - } -} +package com.github.kuangcp.bio.chattingroom; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintStream; +import java.net.Socket; +import lombok.extern.slf4j.Slf4j; + +/** + * Created by Myth on 2017/4/2 0002 + * 负责处理每个线程通信的线程类 + */ +@Slf4j +public class ServerThread implements Runnable { + + //定义当前线程处理的Socket + private Socket s; + //该线程处理的Socket对应的输入流 + private BufferedReader br; + + ServerThread(Socket s) throws IOException { + this.s = s; + //初始化该输入流 + br = new BufferedReader(new InputStreamReader(s.getInputStream())); + } + + @Override + public void run() { + try { + String content; + //不断的从Socket中读取客户端发送过来的数据 + while ((content = readFromClient()) != null) { + log.info("#Chat# {}", content); + //遍历socketList中的每个Socket + for (Socket socket : SocketServer.socketList) { + //将读到的内容向所有 Socket 发送 + PrintStream printStream = new PrintStream(socket.getOutputStream()); + printStream.println(content); + } + } + } catch (Exception e) { + log.error(e.getMessage(), e); + } + } + + //定义读取客户端数据的方法 + private String readFromClient() { + try { + return br.readLine(); + //如果捕获到异常,则表明该Socket对应的客户端已经关闭 + } catch (Exception e) { + log.info("remove socket: socket={}", s); + SocketServer.socketList.remove(s); + log.error(e.getMessage(), e); + } + return null; + } +} diff --git a/java-network/src/main/java/com/github/kuangcp/bio/chattingroom/SocketClient.java b/java-network/src/main/java/com/github/kuangcp/bio/chattingroom/SocketClient.java index 8b775411..f1642bc5 100644 --- a/java-network/src/main/java/com/github/kuangcp/bio/chattingroom/SocketClient.java +++ b/java-network/src/main/java/com/github/kuangcp/bio/chattingroom/SocketClient.java @@ -1,46 +1,46 @@ -package com.github.kuangcp.bio.chattingroom; - -import java.io.BufferedReader; -import java.io.InputStreamReader; -import java.io.PrintStream; -import java.net.Socket; -import lombok.extern.slf4j.Slf4j; - -/** - * Created by Myth on 2017/4/2 - * 单线程的客户端 - */ -@Slf4j -public class SocketClient { - - private String name; - - - public static void main(String[] args) throws Exception { - SocketClient client = new SocketClient(); - client.setName(); - client.start(); - } - - private void start() throws Exception { - Socket socket = new Socket("127.0.0.1", 30000); - //客户端启动线程不断的读取来自服务器的数据 - new Thread(new ClientThread(this.name, socket)).start(); - - PrintStream printStream = new PrintStream(socket.getOutputStream()); - String line; - //不断读取键盘输入 - BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); - while ((line = br.readLine()) != null) { - //将键盘输入写入 Socket 输入流中 - printStream.println(this.name + " : " + line); - } - } - - private void setName() throws Exception { - log.info("input name"); - BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); - name = br.readLine(); - log.info("your name: {}", name); - } -} +package com.github.kuangcp.bio.chattingroom; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.io.PrintStream; +import java.net.Socket; +import lombok.extern.slf4j.Slf4j; + +/** + * Created by Myth on 2017/4/2 + * 单线程的客户端 + */ +@Slf4j +public class SocketClient { + + private String name; + + + public static void main(String[] args) throws Exception { + SocketClient client = new SocketClient(); + client.setName(); + client.start(); + } + + private void start() throws Exception { + Socket socket = new Socket("127.0.0.1", 30000); + //客户端启动线程不断的读取来自服务器的数据 + new Thread(new ClientThread(this.name, socket)).start(); + + PrintStream printStream = new PrintStream(socket.getOutputStream()); + String line; + //不断读取键盘输入 + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + while ((line = br.readLine()) != null) { + //将键盘输入写入 Socket 输入流中 + printStream.println(this.name + " : " + line); + } + } + + private void setName() throws Exception { + log.info("input name"); + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + name = br.readLine(); + log.info("your name: {}", name); + } +} diff --git a/java-network/src/main/java/com/github/kuangcp/bio/chattingroom/SocketServer.java b/java-network/src/main/java/com/github/kuangcp/bio/chattingroom/SocketServer.java index 92e54b68..1a56c068 100644 --- a/java-network/src/main/java/com/github/kuangcp/bio/chattingroom/SocketServer.java +++ b/java-network/src/main/java/com/github/kuangcp/bio/chattingroom/SocketServer.java @@ -1,32 +1,32 @@ -package com.github.kuangcp.bio.chattingroom; - -import java.net.ServerSocket; -import java.net.Socket; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import lombok.extern.slf4j.Slf4j; - -/** - * Created by Myth on 2017/4/2 0002 - * 服务器端 - * 使用多线程来是实现简单的CS架构的公共聊天室 - */ -@Slf4j -public class SocketServer { - - //定义保存所有的socket的集合,并包装成线程安全的 - static List socketList = Collections.synchronizedList(new ArrayList<>()); - - public static void main(String[] s) throws Exception { - ServerSocket server = new ServerSocket(30000); - while (true) { - Socket socket = server.accept(); - socketList.add(socket); - log.info("establish connect: socket={}", socket); - - //每当客户端连接后启动一个ServerThread 线程为该客户服务 - new Thread(new ServerThread(socket)).start(); - } - } -} +package com.github.kuangcp.bio.chattingroom; + +import java.net.ServerSocket; +import java.net.Socket; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import lombok.extern.slf4j.Slf4j; + +/** + * Created by Myth on 2017/4/2 0002 + * 服务器端 + * 使用多线程来是实现简单的CS架构的公共聊天室 + */ +@Slf4j +public class SocketServer { + + //定义保存所有的socket的集合,并包装成线程安全的 + static List socketList = Collections.synchronizedList(new ArrayList<>()); + + public static void main(String[] s) throws Exception { + ServerSocket server = new ServerSocket(30000); + while (true) { + Socket socket = server.accept(); + socketList.add(socket); + log.info("establish connect: socket={}", socket); + + //每当客户端连接后启动一个ServerThread 线程为该客户服务 + new Thread(new ServerThread(socket)).start(); + } + } +} diff --git a/java-network/src/main/java/com/github/kuangcp/bio/onechatone/ChatMaps.java b/java-network/src/main/java/com/github/kuangcp/bio/onechatone/ChatMaps.java index 7205ec5b..3e159569 100644 --- a/java-network/src/main/java/com/github/kuangcp/bio/onechatone/ChatMaps.java +++ b/java-network/src/main/java/com/github/kuangcp/bio/onechatone/ChatMaps.java @@ -1,51 +1,51 @@ -package com.github.kuangcp.bio.onechatone; - -import java.util.*; - -/** - * Created by Myth on 2017/4/2 - * - */ -public class ChatMaps { - //创建一个线程安全的HashMap - public Map map = Collections.synchronizedMap(new HashMap()); - - //根据Value来删除指定项 - public synchronized void removeByValue(Object value){ - for(Object key:map.keySet()){ - if(map.get(key) == value){ - map.remove(key); - break; - } - } - } - //获取所有Value组成的set集合中 - public synchronized Set valueSet(){ - Set result = new HashSet(); - //将Map中所有Value添加到result集合中 - //java8表达式: - map.forEach((key,value)->result.add(value)); - return result; - } - //根据value查找key - public synchronized K getKeyByValue(V val){ - //遍历所有key组成的集合 - for (K key:map.keySet()){ - //如果指定key对应的value与被搜索的value相同,则返回相同的key - if(map.get(key) == val || map.get(key).equals(val)){ - return key; - } - } - return null; - } - //实现put方法,该方法不允许value重复 - public synchronized V put(K key,V value){ - for(V val:valueSet()){ - //如果放入的value是有重复的,就抛出异常 - if(val.equals(value) && val.hashCode() == value.hashCode()){ - throw new RuntimeException("MyMap实例中不允许有重复value!"); - } - } - return map.put(key,value); - } -} +package com.github.kuangcp.bio.onechatone; + +import java.util.*; + +/** + * Created by Myth on 2017/4/2 + * + */ +public class ChatMaps { + //创建一个线程安全的HashMap + public Map map = Collections.synchronizedMap(new HashMap()); + + //根据Value来删除指定项 + public synchronized void removeByValue(Object value){ + for(Object key:map.keySet()){ + if(map.get(key) == value){ + map.remove(key); + break; + } + } + } + //获取所有Value组成的set集合中 + public synchronized Set valueSet(){ + Set result = new HashSet(); + //将Map中所有Value添加到result集合中 + //java8表达式: + map.forEach((key,value)->result.add(value)); + return result; + } + //根据value查找key + public synchronized K getKeyByValue(V val){ + //遍历所有key组成的集合 + for (K key:map.keySet()){ + //如果指定key对应的value与被搜索的value相同,则返回相同的key + if(map.get(key) == val || map.get(key).equals(val)){ + return key; + } + } + return null; + } + //实现put方法,该方法不允许value重复 + public synchronized V put(K key,V value){ + for(V val:valueSet()){ + //如果放入的value是有重复的,就抛出异常 + if(val.equals(value) && val.hashCode() == value.hashCode()){ + throw new RuntimeException("MyMap实例中不允许有重复value!"); + } + } + return map.put(key,value); + } +} diff --git a/java-network/src/main/java/com/github/kuangcp/bio/onechatone/ChatProtocol.java b/java-network/src/main/java/com/github/kuangcp/bio/onechatone/ChatProtocol.java index 6c98f602..b9ea4230 100644 --- a/java-network/src/main/java/com/github/kuangcp/bio/onechatone/ChatProtocol.java +++ b/java-network/src/main/java/com/github/kuangcp/bio/onechatone/ChatProtocol.java @@ -1,21 +1,21 @@ -package com.github.kuangcp.bio.onechatone; - -/** - * Created by Myth on 2017/4/2 - * 信息的特定内容前后的的特殊字符,称为协议字符 - * TODO 但是这种做法很容易出现问题, 当数据内容出现协议字符时 - */ -public interface ChatProtocol { - - int SERVER_PORT = 30000; - //定义协议字符串的长度 - int PROTOCOL_LEN = 2; - - //协议字符串,服务器和客户端交互信息的前后都要加这种特殊字符串 - String MSG_ROUND = "§□"; - String USER_ROUND = "∏∑"; - String LOGIN_SUCCESS = "1"; - String NAME_REP = "-1"; - String PRIVATE_ROUND = "★【"; - String SPLIT_SIGN = "※"; -} +package com.github.kuangcp.bio.onechatone; + +/** + * Created by Myth on 2017/4/2 + * 信息的特定内容前后的的特殊字符,称为协议字符 + * TODO 但是这种做法很容易出现问题, 当数据内容出现协议字符时 + */ +public interface ChatProtocol { + + int SERVER_PORT = 30000; + //定义协议字符串的长度 + int PROTOCOL_LEN = 2; + + //协议字符串,服务器和客户端交互信息的前后都要加这种特殊字符串 + String MSG_ROUND = "§□"; + String USER_ROUND = "∏∑"; + String LOGIN_SUCCESS = "1"; + String NAME_REP = "-1"; + String PRIVATE_ROUND = "★【"; + String SPLIT_SIGN = "※"; +} diff --git a/java-network/src/main/java/com/github/kuangcp/bio/onechatone/Client.java b/java-network/src/main/java/com/github/kuangcp/bio/onechatone/Client.java index ab1b2d8a..d2b6b9d6 100644 --- a/java-network/src/main/java/com/github/kuangcp/bio/onechatone/Client.java +++ b/java-network/src/main/java/com/github/kuangcp/bio/onechatone/Client.java @@ -1,95 +1,95 @@ -package com.github.kuangcp.bio.onechatone; - -import com.github.kuangcp.io.ResourceTool; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.PrintStream; -import java.net.Socket; -import java.net.UnknownHostException; -import javax.swing.JOptionPane; - -/** - * Created by Myth on 2017/4/3 0003 - */ -public class Client { - - private Socket socket; - private PrintStream ps; - private BufferedReader brServer; - private BufferedReader keyIn; - - public static void main(String[] args) { - Client client = new Client(); - client.init(); - client.readAndSend(); - } - - public void init() { - try { - keyIn = new BufferedReader(new InputStreamReader(System.in)); - socket = new Socket("127.0.0.1", ChatProtocol.SERVER_PORT); - ps = new PrintStream(socket.getOutputStream()); - brServer = new BufferedReader(new InputStreamReader(socket.getInputStream())); - String tip = ""; - while (true) { - //循环弹窗提示输入用户名 - String userName = JOptionPane.showInputDialog(tip + "请输入用户名"); - //在用户的输入前后加上协议符 - ps.println(ChatProtocol.USER_ROUND + userName + ChatProtocol.USER_ROUND); - String result = brServer.readLine(); - - if (result.equals(ChatProtocol.NAME_REP)) { - tip = "用户名重复,请重新输入"; - continue; - } - if (result.equals(ChatProtocol.LOGIN_SUCCESS)) { - break; - } - } - //捕获到异常,关闭资源,退出程序 - } catch (UnknownHostException e) { - System.out.println("找不到服务器"); - closeAllResources(); - System.exit(1); - } catch (IOException es) { - System.out.println("网络异常,请重试"); - closeAllResources(); - System.exit(1); - } - //以该Socket对应的输入流启动ClientThread线程 - new ClientThread(brServer).start(); - } - - //定义一个读取键盘输出,向网络发送的方法 - private void readAndSend() { - try { - String line; - while ((line = keyIn.readLine()) != null) { - //如果消息中含 : 并且是//用户名 开头,则是判断为私聊 - if (line.indexOf(":") > 0 && line.startsWith("//")) { - line = line.substring(2); - ps.println(ChatProtocol.PRIVATE_ROUND + line.split(":")[0] + ChatProtocol.SPLIT_SIGN - + line.split(":")[1] + ChatProtocol.PRIVATE_ROUND); - } else { - ps.println(ChatProtocol.MSG_ROUND + line + ChatProtocol.MSG_ROUND); - } - } - } catch (Exception io) { - System.out.println("网络异常,请重试"); - closeAllResources(); - System.exit(1); - } - } - - //关闭所有资源 - private void closeAllResources() { - try { - - ResourceTool.close(keyIn, brServer, ps, socket); - } catch (Exception e) { - e.printStackTrace(); - } - } - -} +package com.github.kuangcp.bio.onechatone; + +import com.github.kuangcp.io.ResourceTool; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintStream; +import java.net.Socket; +import java.net.UnknownHostException; +import javax.swing.JOptionPane; + +/** + * Created by Myth on 2017/4/3 0003 + */ +public class Client { + + private Socket socket; + private PrintStream ps; + private BufferedReader brServer; + private BufferedReader keyIn; + + public static void main(String[] args) { + Client client = new Client(); + client.init(); + client.readAndSend(); + } + + public void init() { + try { + keyIn = new BufferedReader(new InputStreamReader(System.in)); + socket = new Socket("127.0.0.1", ChatProtocol.SERVER_PORT); + ps = new PrintStream(socket.getOutputStream()); + brServer = new BufferedReader(new InputStreamReader(socket.getInputStream())); + String tip = ""; + while (true) { + //循环弹窗提示输入用户名 + String userName = JOptionPane.showInputDialog(tip + "请输入用户名"); + //在用户的输入前后加上协议符 + ps.println(ChatProtocol.USER_ROUND + userName + ChatProtocol.USER_ROUND); + String result = brServer.readLine(); + + if (result.equals(ChatProtocol.NAME_REP)) { + tip = "用户名重复,请重新输入"; + continue; + } + if (result.equals(ChatProtocol.LOGIN_SUCCESS)) { + break; + } + } + //捕获到异常,关闭资源,退出程序 + } catch (UnknownHostException e) { + System.out.println("找不到服务器"); + closeAllResources(); + System.exit(1); + } catch (IOException es) { + System.out.println("网络异常,请重试"); + closeAllResources(); + System.exit(1); + } + //以该Socket对应的输入流启动ClientThread线程 + new ClientThread(brServer).start(); + } + + //定义一个读取键盘输出,向网络发送的方法 + private void readAndSend() { + try { + String line; + while ((line = keyIn.readLine()) != null) { + //如果消息中含 : 并且是//用户名 开头,则是判断为私聊 + if (line.indexOf(":") > 0 && line.startsWith("//")) { + line = line.substring(2); + ps.println(ChatProtocol.PRIVATE_ROUND + line.split(":")[0] + ChatProtocol.SPLIT_SIGN + + line.split(":")[1] + ChatProtocol.PRIVATE_ROUND); + } else { + ps.println(ChatProtocol.MSG_ROUND + line + ChatProtocol.MSG_ROUND); + } + } + } catch (Exception io) { + System.out.println("网络异常,请重试"); + closeAllResources(); + System.exit(1); + } + } + + //关闭所有资源 + private void closeAllResources() { + try { + + ResourceTool.close(keyIn, brServer, ps, socket); + } catch (Exception e) { + e.printStackTrace(); + } + } + +} diff --git a/java-network/src/main/java/com/github/kuangcp/bio/onechatone/ClientThread.java b/java-network/src/main/java/com/github/kuangcp/bio/onechatone/ClientThread.java index f4c883ab..c02fb834 100644 --- a/java-network/src/main/java/com/github/kuangcp/bio/onechatone/ClientThread.java +++ b/java-network/src/main/java/com/github/kuangcp/bio/onechatone/ClientThread.java @@ -1,36 +1,36 @@ -package com.github.kuangcp.bio.onechatone; - -import java.io.BufferedReader; - -/** - * Created by Myth on 2017/4/3 0003 - */ -public class ClientThread extends Thread{ - BufferedReader br = null; - public ClientThread(BufferedReader br){ - this.br = br; - } - public void run(){ - try{ - String line = null; - while((line=br.readLine())!=null){ - //显示服务器读取到的内容 - System.out.println(line); - /* - 此处的业务可以根据需求复杂化,并不只是简单的打印输出, - 例如QQ客户端,就要在有好友上线下线时接受好友在线列表,接受群聊消息或私聊消息,就要有对应的协议字符(特殊字符不容易出错) - 例如 在线五子棋,就要传输下子的坐标,如果要聊天,又要接受消息 - 需求总是提不尽的 - */ - } - }catch (Exception r){ - r.printStackTrace(); - }finally { - try{ - if(br!=null) br.close(); - }catch (Exception e){ - e.printStackTrace(); - } - } - } -} +package com.github.kuangcp.bio.onechatone; + +import java.io.BufferedReader; + +/** + * Created by Myth on 2017/4/3 0003 + */ +public class ClientThread extends Thread{ + BufferedReader br = null; + public ClientThread(BufferedReader br){ + this.br = br; + } + public void run(){ + try{ + String line = null; + while((line=br.readLine())!=null){ + //显示服务器读取到的内容 + System.out.println(line); + /* + 此处的业务可以根据需求复杂化,并不只是简单的打印输出, + 例如QQ客户端,就要在有好友上线下线时接受好友在线列表,接受群聊消息或私聊消息,就要有对应的协议字符(特殊字符不容易出错) + 例如 在线五子棋,就要传输下子的坐标,如果要聊天,又要接受消息 + 需求总是提不尽的 + */ + } + }catch (Exception r){ + r.printStackTrace(); + }finally { + try{ + if(br!=null) br.close(); + }catch (Exception e){ + e.printStackTrace(); + } + } + } +} diff --git a/java-network/src/main/java/com/github/kuangcp/bio/onechatone/README.md b/java-network/src/main/java/com/github/kuangcp/bio/onechatone/README.md index cddb2be5..9b8bb4d2 100644 --- a/java-network/src/main/java/com/github/kuangcp/bio/onechatone/README.md +++ b/java-network/src/main/java/com/github/kuangcp/bio/onechatone/README.md @@ -1,13 +1,13 @@ -- 2017-4-3 17:05:59 -- 使用简单的网络编程知识,Socket通信,使用协议符实现私聊 -- 流程 - - 一个接口放各种情况对应的协议符 - - 一个处理Socket和用户对应的映射 - - - - 服务端 启动后就循环等待,通过启动的线程来判断收到的消息中的协议符是公聊还是私聊 - - 根据公聊私聊做不同的处理 - - 客户端 启动并与服务端连接后,循环接受键盘输入的字符串并发送给服务端 - - 接受服务端的消息,判断并做对应的业务处理 - -- 缺点是 服务端面对每一个客户端都要建立一个单独的线程来处理交互,资源消耗很大,所以就有了NIO AIO +- 2017-4-3 17:05:59 +- 使用简单的网络编程知识,Socket通信,使用协议符实现私聊 +- 流程 + - 一个接口放各种情况对应的协议符 + - 一个处理Socket和用户对应的映射 + - + - 服务端 启动后就循环等待,通过启动的线程来判断收到的消息中的协议符是公聊还是私聊 + - 根据公聊私聊做不同的处理 + - 客户端 启动并与服务端连接后,循环接受键盘输入的字符串并发送给服务端 + - 接受服务端的消息,判断并做对应的业务处理 + +- 缺点是 服务端面对每一个客户端都要建立一个单独的线程来处理交互,资源消耗很大,所以就有了NIO AIO - NIO 不会面临程序输入输出的线程阻塞问题 \ No newline at end of file diff --git a/java-network/src/main/java/com/github/kuangcp/bio/onechatone/Server.java b/java-network/src/main/java/com/github/kuangcp/bio/onechatone/Server.java index f664bcbf..e074aaf2 100644 --- a/java-network/src/main/java/com/github/kuangcp/bio/onechatone/Server.java +++ b/java-network/src/main/java/com/github/kuangcp/bio/onechatone/Server.java @@ -1,31 +1,31 @@ -package com.github.kuangcp.bio.onechatone; - -import java.io.PrintStream; -import java.net.ServerSocket; -import java.net.Socket; - -/** - * Created by Myth on 2017/4/3 0003 - */ -public class Server { - - static ChatMaps clients = new ChatMaps<>(); - - private void init() { - try { - ServerSocket ss = new ServerSocket(ChatProtocol.SERVER_PORT); - while (true) { - Socket socket = ss.accept(); - new ServerThread(socket).start(); - } - } catch (Exception r) { - System.out.println("服务启动失败,是否端口" + ChatProtocol.SERVER_PORT + "被占用"); - r.printStackTrace(); - } - } - - public static void main(String[] s) { - Server server = new Server(); - server.init(); - } -} +package com.github.kuangcp.bio.onechatone; + +import java.io.PrintStream; +import java.net.ServerSocket; +import java.net.Socket; + +/** + * Created by Myth on 2017/4/3 0003 + */ +public class Server { + + static ChatMaps clients = new ChatMaps<>(); + + private void init() { + try { + ServerSocket ss = new ServerSocket(ChatProtocol.SERVER_PORT); + while (true) { + Socket socket = ss.accept(); + new ServerThread(socket).start(); + } + } catch (Exception r) { + System.out.println("服务启动失败,是否端口" + ChatProtocol.SERVER_PORT + "被占用"); + r.printStackTrace(); + } + } + + public static void main(String[] s) { + Server server = new Server(); + server.init(); + } +} diff --git a/java-network/src/main/java/com/github/kuangcp/bio/onechatone/ServerThread.java b/java-network/src/main/java/com/github/kuangcp/bio/onechatone/ServerThread.java index aa41fb77..d3a4a986 100644 --- a/java-network/src/main/java/com/github/kuangcp/bio/onechatone/ServerThread.java +++ b/java-network/src/main/java/com/github/kuangcp/bio/onechatone/ServerThread.java @@ -1,76 +1,76 @@ -package com.github.kuangcp.bio.onechatone; - -import java.io.BufferedReader; -import java.io.InputStreamReader; -import java.io.PrintStream; -import java.net.Socket; - -/** - * Created by Myth on 2017/4/3 0003 - */ -public class ServerThread extends Thread{ - private Socket socket; - BufferedReader br = null; - PrintStream ps = null; - - public ServerThread(Socket socket){ - this.socket = socket; - } - public void run(){ - try{ - br = new BufferedReader(new InputStreamReader(socket.getInputStream())); - ps = new PrintStream(socket.getOutputStream()); - String line = null; - while((line=br.readLine())!=null){ - //如果读到的行以标识符 USER_ROUND 开始的,并以这个结束,就可以确定读到的是用户登录的登录名 - if(line.startsWith(ChatProtocol.USER_ROUND) && line.endsWith(ChatProtocol.USER_ROUND)){ - String userName = getRealMsg(line); - if(Server.clients.map.containsKey(userName)){ - System.out.println("用户名重复"); - ps.println(ChatProtocol.NAME_REP); - }else{ - System.out.println("成功"); - ps.println(ChatProtocol.LOGIN_SUCCESS); - Server.clients.put(userName,ps); - } - //如果标记为私聊信息,私聊信息只向特定的输出流发送 - }else if(line.startsWith(ChatProtocol.PRIVATE_ROUND) && line.endsWith(ChatProtocol.PRIVATE_ROUND)){ - String userAndMsg = getRealMsg(line); - //正则信息,分出用户,信息 - String user = userAndMsg.split(ChatProtocol.SPLIT_SIGN)[0]; - String msg = userAndMsg.split(ChatProtocol.SPLIT_SIGN)[1]; - //获取私聊用户对应的输出流,发送私聊信息 - Server.clients.map.get(user).println(Server.clients.getKeyByValue(ps)+"悄悄对你说 : "+msg); - //公聊就对每个Socket发送 - }else{ - //得到真实消息 - String msg = getRealMsg(line); - //遍历所有输出流 - for (PrintStream clientPs : Server.clients.valueSet()) { - clientPs.println(Server.clients.getKeyByValue(ps)+" 说 "+msg); - } - - } - - } - //有异常说明 Socket对应的客户端出现了问题 就要删除它 - }catch (Exception e){ - Server.clients.removeByValue(ps); - System.out.println(Server.clients.map.size()); - try{ - if(br!=null) br.close(); - if(ps!=null) ps.close(); - if(socket!=null) socket.close(); - }catch (Exception es){ - es.printStackTrace(); - } - e.printStackTrace(); - } - } - - //去除标识符,得到真实的信息 - private String getRealMsg(String line) { - return line.substring(ChatProtocol.PROTOCOL_LEN,line.length()-ChatProtocol.PROTOCOL_LEN); - } - -} +package com.github.kuangcp.bio.onechatone; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.io.PrintStream; +import java.net.Socket; + +/** + * Created by Myth on 2017/4/3 0003 + */ +public class ServerThread extends Thread{ + private Socket socket; + BufferedReader br = null; + PrintStream ps = null; + + public ServerThread(Socket socket){ + this.socket = socket; + } + public void run(){ + try{ + br = new BufferedReader(new InputStreamReader(socket.getInputStream())); + ps = new PrintStream(socket.getOutputStream()); + String line = null; + while((line=br.readLine())!=null){ + //如果读到的行以标识符 USER_ROUND 开始的,并以这个结束,就可以确定读到的是用户登录的登录名 + if(line.startsWith(ChatProtocol.USER_ROUND) && line.endsWith(ChatProtocol.USER_ROUND)){ + String userName = getRealMsg(line); + if(Server.clients.map.containsKey(userName)){ + System.out.println("用户名重复"); + ps.println(ChatProtocol.NAME_REP); + }else{ + System.out.println("成功"); + ps.println(ChatProtocol.LOGIN_SUCCESS); + Server.clients.put(userName,ps); + } + //如果标记为私聊信息,私聊信息只向特定的输出流发送 + }else if(line.startsWith(ChatProtocol.PRIVATE_ROUND) && line.endsWith(ChatProtocol.PRIVATE_ROUND)){ + String userAndMsg = getRealMsg(line); + //正则信息,分出用户,信息 + String user = userAndMsg.split(ChatProtocol.SPLIT_SIGN)[0]; + String msg = userAndMsg.split(ChatProtocol.SPLIT_SIGN)[1]; + //获取私聊用户对应的输出流,发送私聊信息 + Server.clients.map.get(user).println(Server.clients.getKeyByValue(ps)+"悄悄对你说 : "+msg); + //公聊就对每个Socket发送 + }else{ + //得到真实消息 + String msg = getRealMsg(line); + //遍历所有输出流 + for (PrintStream clientPs : Server.clients.valueSet()) { + clientPs.println(Server.clients.getKeyByValue(ps)+" 说 "+msg); + } + + } + + } + //有异常说明 Socket对应的客户端出现了问题 就要删除它 + }catch (Exception e){ + Server.clients.removeByValue(ps); + System.out.println(Server.clients.map.size()); + try{ + if(br!=null) br.close(); + if(ps!=null) ps.close(); + if(socket!=null) socket.close(); + }catch (Exception es){ + es.printStackTrace(); + } + e.printStackTrace(); + } + } + + //去除标识符,得到真实的信息 + private String getRealMsg(String line) { + return line.substring(ChatProtocol.PROTOCOL_LEN,line.length()-ChatProtocol.PROTOCOL_LEN); + } + +} diff --git a/java-network/src/main/java/com/github/kuangcp/nio/NioClient.java b/java-network/src/main/java/com/github/kuangcp/nio/NioClient.java index 47a8d877..34c6b386 100644 --- a/java-network/src/main/java/com/github/kuangcp/nio/NioClient.java +++ b/java-network/src/main/java/com/github/kuangcp/nio/NioClient.java @@ -1,84 +1,84 @@ -package com.github.kuangcp.nio; - -import java.net.InetSocketAddress; -import java.nio.ByteBuffer; -import java.nio.channels.SelectionKey; -import java.nio.channels.Selector; -import java.nio.channels.SocketChannel; -import java.nio.charset.Charset; -import java.util.Scanner; - -/** - * Created by Myth on 2017/4/5 0005 - * NIO实现的客户端,相对于普通的CS结构节省资源 - */ -public class NioClient { - - //定义检测SocketChannel的Selector对象 - private Selector selector = null; - private Charset charset = Charset.forName("UTF-8"); - - private boolean stop = false; - - public static void main(String[] s) throws Exception { - new NioClient().init(); - } - - private void init() throws Exception { - selector = Selector.open(); - InetSocketAddress isa = new InetSocketAddress(NioServer.PORT); - SocketChannel sc = SocketChannel.open(isa); - sc.configureBlocking(false); - sc.register(selector, SelectionKey.OP_READ); - new ClientThread().start(); - Scanner scan = new Scanner(System.in); -// while (scan.hasNextLine()) { - while (!stop) { - String line = scan.nextLine(); - if ("exit".equalsIgnoreCase(line)) { - // 只是停掉了通信线程, 但是该方法的输入线程还是被阻塞了 - stop(); - } - sc.write(charset.encode(line)); - } - } - - private void stop() { - this.stop = true; - } - - //定义读取服务器数据的线程 - private class ClientThread extends Thread { - - public void run() { - try { - while (!stop) { - int result = selector.select(); - if (result <= 0) { - continue; - } - System.out.println(stop + "准备好的数量:" + result); - for (SelectionKey sk : selector.selectedKeys()) { - selector.selectedKeys().remove(sk); - if (sk.isReadable()) { - SocketChannel sc = (SocketChannel) sk.channel(); - ByteBuffer buff = ByteBuffer.allocate(1024); - StringBuilder content = new StringBuilder(); - while (sc.read(buff) > 0) { - sc.read(buff); - buff.flip(); - content.append(charset.decode(buff)); - } - System.out.println("聊天信息" + content.toString()); - sk.interestOps(SelectionKey.OP_READ); - } - - } - } - } catch (Exception e) { - e.printStackTrace(); - } - } - } - -} +package com.github.kuangcp.nio; + +import java.net.InetSocketAddress; +import java.nio.ByteBuffer; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.nio.channels.SocketChannel; +import java.nio.charset.Charset; +import java.util.Scanner; + +/** + * Created by Myth on 2017/4/5 0005 + * NIO实现的客户端,相对于普通的CS结构节省资源 + */ +public class NioClient { + + //定义检测SocketChannel的Selector对象 + private Selector selector = null; + private Charset charset = Charset.forName("UTF-8"); + + private boolean stop = false; + + public static void main(String[] s) throws Exception { + new NioClient().init(); + } + + private void init() throws Exception { + selector = Selector.open(); + InetSocketAddress isa = new InetSocketAddress(NioServer.PORT); + SocketChannel sc = SocketChannel.open(isa); + sc.configureBlocking(false); + sc.register(selector, SelectionKey.OP_READ); + new ClientThread().start(); + Scanner scan = new Scanner(System.in); +// while (scan.hasNextLine()) { + while (!stop) { + String line = scan.nextLine(); + if ("exit".equalsIgnoreCase(line)) { + // 只是停掉了通信线程, 但是该方法的输入线程还是被阻塞了 + stop(); + } + sc.write(charset.encode(line)); + } + } + + private void stop() { + this.stop = true; + } + + //定义读取服务器数据的线程 + private class ClientThread extends Thread { + + public void run() { + try { + while (!stop) { + int result = selector.select(); + if (result <= 0) { + continue; + } + System.out.println(stop + "准备好的数量:" + result); + for (SelectionKey sk : selector.selectedKeys()) { + selector.selectedKeys().remove(sk); + if (sk.isReadable()) { + SocketChannel sc = (SocketChannel) sk.channel(); + ByteBuffer buff = ByteBuffer.allocate(1024); + StringBuilder content = new StringBuilder(); + while (sc.read(buff) > 0) { + sc.read(buff); + buff.flip(); + content.append(charset.decode(buff)); + } + System.out.println("聊天信息" + content.toString()); + sk.interestOps(SelectionKey.OP_READ); + } + + } + } + } catch (Exception e) { + e.printStackTrace(); + } + } + } + +} diff --git a/java-network/src/main/java/com/github/kuangcp/nio/NioServer.java b/java-network/src/main/java/com/github/kuangcp/nio/NioServer.java index 84d6301e..f619ca0d 100644 --- a/java-network/src/main/java/com/github/kuangcp/nio/NioServer.java +++ b/java-network/src/main/java/com/github/kuangcp/nio/NioServer.java @@ -1,201 +1,201 @@ -package com.github.kuangcp.nio; - -import java.net.InetSocketAddress; -import java.nio.ByteBuffer; -import java.nio.channels.Channel; -import java.nio.channels.SelectionKey; -import java.nio.channels.Selector; -import java.nio.channels.ServerSocketChannel; -import java.nio.channels.SocketChannel; -import java.nio.charset.Charset; - -/** - * Created by Myth on 2017/4/3 0003 - * NIO Server端,节省线程 - * 该程序启动就建立了一个可监听连接请求的ServerSocketChannel,并将该Channel注册到指定的Selector - * 从JDK1.4开始,Java提供了NIO API来提供开发 - * 之前的都是一个线程处理一个客户端,线程资源消耗大,因为之前那些处理方式在程序输入输出时会线程阻塞,NIO就不会 - * NIO 采用多路复用和轮询的机制, 将阻塞都让一个线程来处理,然后提取出准备好的IO来操作, 提高性能, 请求和线程比为 n:1 - * 原先的BIO是 1:1 - */ -public class NioServer { - - static final int PORT = 30000; - //定义实现编码,解码的字符集 - private Charset charset = Charset.forName("UTF-8"); - private volatile boolean stop = false; - - public static void main(String[] s) throws Exception { - new NioServer().init(); -// new NioServer().initByOtherWay(); - } - - - private synchronized void init() throws Exception { - Selector selector = Selector.open(); - //通过OPEN方法来打开一个未绑定的ServerSocketChannel 实例 - ServerSocketChannel server = ServerSocketChannel.open(); - //将该ServerSocketChannel绑定到指定ip - server.bind(new InetSocketAddress(PORT)); - //设置是NIO 非阻塞模式 - server.configureBlocking(false); - - //将sever注册到指定Selector对象上 - server.register(selector, SelectionKey.OP_ACCEPT); - // 只要有管道准备就绪了,就继续轮询 -// while (selector.select() > 0) { - while (!stop) { - // TODO 能写成死循环, 但是下面这个方法一定要调用, 为什么 - // 因为只有在选择了至少一个通道后,才会返回该选择器的唤醒方法,或者当前线程中断,以先到者为准。 这是API的翻译 - int result = selector.select(); - if (result <= 0) { - continue; - } - System.out.println("准备好的数量:" + result); - //依次处理selector上的每个已经准备好的管道 - for (SelectionKey sk : selector.selectedKeys()) { - //从selector上的已选择key集中删除正在处理的连接请求 - selector.selectedKeys().remove(sk); - if (sk.isAcceptable()) { - //调用accept方法,产生服务器端的SocketChannel - SocketChannel sc = server.accept(); - sc.configureBlocking(false);//NIO模式 - sc.register(selector, SelectionKey.OP_READ);//注册到selector上 - sk.interestOps(SelectionKey.OP_ACCEPT);//将sk对应的Channel设置成准备接受其他请求 - } - //如果有数据需要读取 - // TODO 为什么 打开服务端,打开客户端, 然后关闭客户端, 服务端死循环的输出,再次打开客户端后 , - // 服务端就报错 ConcurrentModificationException 退出了, 客户端也随之断开了连接 - // 当给方法加上同步关键字后, 就直接退出了没有报错, 说明, 当客户端重启后, 存在并发修改 - // TODO 那么为什么呢 - // 反之则是客户端死循环输出 - if (sk.isReadable()) { - SocketChannel sc = (SocketChannel) sk.channel(); - ByteBuffer buff = ByteBuffer.allocate(1024); - StringBuilder content = new StringBuilder(); - try { - while (sc.read(buff) > 0) { - buff.flip(); - content.append(charset.decode(buff)); - } - System.out.println("读取的数据 : " + content.toString()); - if ("stop".equalsIgnoreCase(content.toString())) { - stop(); - } - sk.interestOps(SelectionKey.OP_READ);//设置成准备下次读取 - } catch (Exception e) { - //从Selector中删除指定的SelectionKey - sk.cancel(); - if (sk.channel() != null) { - sk.channel().close(); - } - } - //聊天信息不为空 - if (content.length() > 0) { - //遍历selector里注册的所有SelectionKey - for (SelectionKey key : selector.keys()) { - Channel targetChannel = key.channel();//获取Channel - //如果改Channel是SocketChannel是SocketChannel对象 - if (targetChannel instanceof SocketChannel) { - //将读到的内容写入到该Channel中去 - SocketChannel dest = (SocketChannel) targetChannel; - dest.write(charset.encode(content.toString())); - } - } - } - } - } - - } - - } - - private void stop() { - this.stop = true; - } - -// -// public void initByOtherWay() throws IOException { -// Selector selector = Selector.open(); -// //通过OPEN方法来打开一个未绑定的ServerSocketChannel 实例 -// ServerSocketChannel server = ServerSocketChannel.open(); -// //将该ServerSocketChannel绑定到指定ip -// server.bind(new InetSocketAddress(PORT)); -// //设置是NIO 非阻塞模式 -// server.configureBlocking(false); -// -// //将sever注册到指定Selector对象上 -// server.register(selector, SelectionKey.OP_ACCEPT); -// -// while (!stop) { -// selector.select(2000); -// Set selectedKeys = selector.selectedKeys(); -// Iterator it = selectedKeys.iterator(); -// SelectionKey key; -// while (it.hasNext()) { -// key = it.next(); -// it.remove(); -// try { -// handleInput(key); -// } catch (Exception e) { -// if (key != null) { -// key.cancel(); -// if (key.channel() != null) -// key.channel().close(); -// } -// } -// } -// } -// } -// -// private void handleInput(SelectionKey key) throws IOException { -// if(key.isValid()){ -// if(key.isAcceptable()){ -// // Accept the new connection -// ServerSocketChannel ssc = (ServerSocketChannel) key.channel(); -// SocketChannel sc = ssc.accept(); -// sc.configureBlocking(false); -// // Add the new connection to the selector -// sc.register(selector, SelectionKey.OP_READ); -// } -// if(key.isReadable()){ -// SocketChannel sc = (SocketChannel) key.channel(); -// ByteBuffer readBuffer = ByteBuffer.allocate(1024); -// int readBytes = sc.read(readBuffer); -// if (readBytes > 0) { -// // 读取完就要切换 -// readBuffer.flip(); -// byte[] bytes = new byte[readBuffer.remaining()]; -// readBuffer.get(bytes); -// String body = new String(bytes, "UTF-8"); -// System.out.println("The time server receive order : "+ body); -// if("STOP".equalsIgnoreCase(body)){ -// stop(); -// } -// doWrite(sc, "response"); -// } else if (readBytes < 0) { -// // 对端链路关闭 -// key.cancel(); -// sc.close(); -// } -// // 读到0字节,忽略 -// } -// } -// -// } -// - -// -// private void doWrite(SocketChannel channel, String response) -// throws IOException { -// if (response != null && response.trim().length() > 0) { -// byte[] bytes = response.getBytes(); -// ByteBuffer writeBuffer = ByteBuffer.allocate(bytes.length); -// writeBuffer.put(bytes); -// writeBuffer.flip(); -// channel.write(writeBuffer); -// } -// } - - -} +package com.github.kuangcp.nio; + +import java.net.InetSocketAddress; +import java.nio.ByteBuffer; +import java.nio.channels.Channel; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; +import java.nio.charset.Charset; + +/** + * Created by Myth on 2017/4/3 0003 + * NIO Server端,节省线程 + * 该程序启动就建立了一个可监听连接请求的ServerSocketChannel,并将该Channel注册到指定的Selector + * 从JDK1.4开始,Java提供了NIO API来提供开发 + * 之前的都是一个线程处理一个客户端,线程资源消耗大,因为之前那些处理方式在程序输入输出时会线程阻塞,NIO就不会 + * NIO 采用多路复用和轮询的机制, 将阻塞都让一个线程来处理,然后提取出准备好的IO来操作, 提高性能, 请求和线程比为 n:1 + * 原先的BIO是 1:1 + */ +public class NioServer { + + static final int PORT = 30000; + //定义实现编码,解码的字符集 + private Charset charset = Charset.forName("UTF-8"); + private volatile boolean stop = false; + + public static void main(String[] s) throws Exception { + new NioServer().init(); +// new NioServer().initByOtherWay(); + } + + + private synchronized void init() throws Exception { + Selector selector = Selector.open(); + //通过OPEN方法来打开一个未绑定的ServerSocketChannel 实例 + ServerSocketChannel server = ServerSocketChannel.open(); + //将该ServerSocketChannel绑定到指定ip + server.bind(new InetSocketAddress(PORT)); + //设置是NIO 非阻塞模式 + server.configureBlocking(false); + + //将sever注册到指定Selector对象上 + server.register(selector, SelectionKey.OP_ACCEPT); + // 只要有管道准备就绪了,就继续轮询 +// while (selector.select() > 0) { + while (!stop) { + // TODO 能写成死循环, 但是下面这个方法一定要调用, 为什么 + // 因为只有在选择了至少一个通道后,才会返回该选择器的唤醒方法,或者当前线程中断,以先到者为准。 这是API的翻译 + int result = selector.select(); + if (result <= 0) { + continue; + } + System.out.println("准备好的数量:" + result); + //依次处理selector上的每个已经准备好的管道 + for (SelectionKey sk : selector.selectedKeys()) { + //从selector上的已选择key集中删除正在处理的连接请求 + selector.selectedKeys().remove(sk); + if (sk.isAcceptable()) { + //调用accept方法,产生服务器端的SocketChannel + SocketChannel sc = server.accept(); + sc.configureBlocking(false);//NIO模式 + sc.register(selector, SelectionKey.OP_READ);//注册到selector上 + sk.interestOps(SelectionKey.OP_ACCEPT);//将sk对应的Channel设置成准备接受其他请求 + } + //如果有数据需要读取 + // TODO 为什么 打开服务端,打开客户端, 然后关闭客户端, 服务端死循环的输出,再次打开客户端后 , + // 服务端就报错 ConcurrentModificationException 退出了, 客户端也随之断开了连接 + // 当给方法加上同步关键字后, 就直接退出了没有报错, 说明, 当客户端重启后, 存在并发修改 + // TODO 那么为什么呢 + // 反之则是客户端死循环输出 + if (sk.isReadable()) { + SocketChannel sc = (SocketChannel) sk.channel(); + ByteBuffer buff = ByteBuffer.allocate(1024); + StringBuilder content = new StringBuilder(); + try { + while (sc.read(buff) > 0) { + buff.flip(); + content.append(charset.decode(buff)); + } + System.out.println("读取的数据 : " + content.toString()); + if ("stop".equalsIgnoreCase(content.toString())) { + stop(); + } + sk.interestOps(SelectionKey.OP_READ);//设置成准备下次读取 + } catch (Exception e) { + //从Selector中删除指定的SelectionKey + sk.cancel(); + if (sk.channel() != null) { + sk.channel().close(); + } + } + //聊天信息不为空 + if (content.length() > 0) { + //遍历selector里注册的所有SelectionKey + for (SelectionKey key : selector.keys()) { + Channel targetChannel = key.channel();//获取Channel + //如果改Channel是SocketChannel是SocketChannel对象 + if (targetChannel instanceof SocketChannel) { + //将读到的内容写入到该Channel中去 + SocketChannel dest = (SocketChannel) targetChannel; + dest.write(charset.encode(content.toString())); + } + } + } + } + } + + } + + } + + private void stop() { + this.stop = true; + } + +// +// public void initByOtherWay() throws IOException { +// Selector selector = Selector.open(); +// //通过OPEN方法来打开一个未绑定的ServerSocketChannel 实例 +// ServerSocketChannel server = ServerSocketChannel.open(); +// //将该ServerSocketChannel绑定到指定ip +// server.bind(new InetSocketAddress(PORT)); +// //设置是NIO 非阻塞模式 +// server.configureBlocking(false); +// +// //将sever注册到指定Selector对象上 +// server.register(selector, SelectionKey.OP_ACCEPT); +// +// while (!stop) { +// selector.select(2000); +// Set selectedKeys = selector.selectedKeys(); +// Iterator it = selectedKeys.iterator(); +// SelectionKey key; +// while (it.hasNext()) { +// key = it.next(); +// it.remove(); +// try { +// handleInput(key); +// } catch (Exception e) { +// if (key != null) { +// key.cancel(); +// if (key.channel() != null) +// key.channel().close(); +// } +// } +// } +// } +// } +// +// private void handleInput(SelectionKey key) throws IOException { +// if(key.isValid()){ +// if(key.isAcceptable()){ +// // Accept the new connection +// ServerSocketChannel ssc = (ServerSocketChannel) key.channel(); +// SocketChannel sc = ssc.accept(); +// sc.configureBlocking(false); +// // Add the new connection to the selector +// sc.register(selector, SelectionKey.OP_READ); +// } +// if(key.isReadable()){ +// SocketChannel sc = (SocketChannel) key.channel(); +// ByteBuffer readBuffer = ByteBuffer.allocate(1024); +// int readBytes = sc.read(readBuffer); +// if (readBytes > 0) { +// // 读取完就要切换 +// readBuffer.flip(); +// byte[] bytes = new byte[readBuffer.remaining()]; +// readBuffer.get(bytes); +// String body = new String(bytes, "UTF-8"); +// System.out.println("The time server receive order : "+ body); +// if("STOP".equalsIgnoreCase(body)){ +// stop(); +// } +// doWrite(sc, "response"); +// } else if (readBytes < 0) { +// // 对端链路关闭 +// key.cancel(); +// sc.close(); +// } +// // 读到0字节,忽略 +// } +// } +// +// } +// + +// +// private void doWrite(SocketChannel channel, String response) +// throws IOException { +// if (response != null && response.trim().length() > 0) { +// byte[] bytes = response.getBytes(); +// ByteBuffer writeBuffer = ByteBuffer.allocate(bytes.length); +// writeBuffer.put(bytes); +// writeBuffer.flip(); +// channel.write(writeBuffer); +// } +// } + + +} diff --git a/java-network/src/main/java/com/github/kuangcp/port/LogicThread.java b/java-network/src/main/java/com/github/kuangcp/port/LogicThread.java index 5d68c48e..49f14104 100644 --- a/java-network/src/main/java/com/github/kuangcp/port/LogicThread.java +++ b/java-network/src/main/java/com/github/kuangcp/port/LogicThread.java @@ -1,61 +1,61 @@ -package com.github.kuangcp.port; - -import com.github.kuangcp.io.ResourceTool; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.Socket; - -/** - * 服务器端逻辑线程 - */ -public class LogicThread extends Thread { - - private Socket socket; - private InputStream is; - private OutputStream os; - - public LogicThread(Socket socket) { - this.socket = socket; - start(); //启动线程 - } - - public void run() { - byte[] b = new byte[1024]; - try { - //初始化流 - os = socket.getOutputStream(); - is = socket.getInputStream(); - for (int i = 0; i < 3; i++) { - //读取数据 - int n = is.read(b); - //逻辑处理 - byte[] response = logic(b, 0, n); - //反馈数据 - os.write(response); - } - } catch (Exception e) { - e.printStackTrace(); - } finally { - try { - ResourceTool.close(os, is, socket); - } catch (IOException e) { - e.printStackTrace(); - } - } - } - - /** - * 逻辑处理方法,实现echo逻辑 - * - * @param b 客户端发送数据缓冲区 - * @param off 起始下标 - * @param len 有效数据长度 - */ - private byte[] logic(byte[] b, int off, int len) { - byte[] response = new byte[len]; - //将有效数据拷贝到数组response中 - System.arraycopy(b, 0, response, 0, len); - return response; - } +package com.github.kuangcp.port; + +import com.github.kuangcp.io.ResourceTool; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.Socket; + +/** + * 服务器端逻辑线程 + */ +public class LogicThread extends Thread { + + private Socket socket; + private InputStream is; + private OutputStream os; + + public LogicThread(Socket socket) { + this.socket = socket; + start(); //启动线程 + } + + public void run() { + byte[] b = new byte[1024]; + try { + //初始化流 + os = socket.getOutputStream(); + is = socket.getInputStream(); + for (int i = 0; i < 3; i++) { + //读取数据 + int n = is.read(b); + //逻辑处理 + byte[] response = logic(b, 0, n); + //反馈数据 + os.write(response); + } + } catch (Exception e) { + e.printStackTrace(); + } finally { + try { + ResourceTool.close(os, is, socket); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + /** + * 逻辑处理方法,实现echo逻辑 + * + * @param b 客户端发送数据缓冲区 + * @param off 起始下标 + * @param len 有效数据长度 + */ + private byte[] logic(byte[] b, int off, int len) { + byte[] response = new byte[len]; + //将有效数据拷贝到数组response中 + System.arraycopy(b, 0, response, 0, len); + return response; + } } \ No newline at end of file diff --git a/java-network/src/main/java/com/github/kuangcp/port/LogicThreadTest.java b/java-network/src/main/java/com/github/kuangcp/port/LogicThreadTest.java index 956fd107..c20db1d3 100644 --- a/java-network/src/main/java/com/github/kuangcp/port/LogicThreadTest.java +++ b/java-network/src/main/java/com/github/kuangcp/port/LogicThreadTest.java @@ -1,22 +1,22 @@ -package com.github.kuangcp.port; - -import java.io.IOException; -import java.net.ServerSocket; -import java.net.Socket; - -public class LogicThreadTest { - - public static void main(String[] args) { - ServerSocket serverSocket; - Socket socket; - try { - serverSocket = new ServerSocket(10000); - socket = serverSocket.accept(); - LogicThread t = new LogicThread(socket); - t.start(); - } catch (IOException e) { - e.printStackTrace(); - } - } - -} +package com.github.kuangcp.port; + +import java.io.IOException; +import java.net.ServerSocket; +import java.net.Socket; + +public class LogicThreadTest { + + public static void main(String[] args) { + ServerSocket serverSocket; + Socket socket; + try { + serverSocket = new ServerSocket(10000); + socket = serverSocket.accept(); + LogicThread t = new LogicThread(socket); + t.start(); + } catch (IOException e) { + e.printStackTrace(); + } + } + +} diff --git a/java-network/src/main/java/com/github/kuangcp/port/MulSocketServer.java b/java-network/src/main/java/com/github/kuangcp/port/MulSocketServer.java index 9bf39287..007e13a3 100644 --- a/java-network/src/main/java/com/github/kuangcp/port/MulSocketServer.java +++ b/java-network/src/main/java/com/github/kuangcp/port/MulSocketServer.java @@ -1,51 +1,51 @@ -package com.github.kuangcp.port; - -import com.github.kuangcp.io.ResourceTool; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.ServerSocket; -import java.net.Socket; -import lombok.extern.slf4j.Slf4j; - -/** - * 复用连接的echo服务器 功能:将客户端发送的内容反馈给客户端 - */ -@Slf4j -public class MulSocketServer { - - public static void main(String[] args) { - ServerSocket serverSocket = null; - Socket socket = null; - OutputStream os = null; - InputStream is = null; - // 监听端口号 - int port = 10000; - try { - // 建立连接 - serverSocket = new ServerSocket(port); - System.out.println("服务器已启动:"); - // 获得连接 - socket = serverSocket.accept(); - // 初始化流 - is = socket.getInputStream(); - os = socket.getOutputStream(); - byte[] b = new byte[1024]; - for (int i = 0; i < 3; i++) { - int n = is.read(b); - // 输出 - System.out.println("客户端发送内容为:" + new String(b, 0, n)); - // 向客户端发送反馈内容 - os.write(b, 0, n); - } - } catch (Exception e) { - log.error(e.getMessage(), e); - } finally { - try { - ResourceTool.close(os, is, socket, serverSocket); - } catch (Exception e) { - log.error(e.getMessage(), e); - } - } - } - +package com.github.kuangcp.port; + +import com.github.kuangcp.io.ResourceTool; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.ServerSocket; +import java.net.Socket; +import lombok.extern.slf4j.Slf4j; + +/** + * 复用连接的echo服务器 功能:将客户端发送的内容反馈给客户端 + */ +@Slf4j +public class MulSocketServer { + + public static void main(String[] args) { + ServerSocket serverSocket = null; + Socket socket = null; + OutputStream os = null; + InputStream is = null; + // 监听端口号 + int port = 10000; + try { + // 建立连接 + serverSocket = new ServerSocket(port); + System.out.println("服务器已启动:"); + // 获得连接 + socket = serverSocket.accept(); + // 初始化流 + is = socket.getInputStream(); + os = socket.getOutputStream(); + byte[] b = new byte[1024]; + for (int i = 0; i < 3; i++) { + int n = is.read(b); + // 输出 + System.out.println("客户端发送内容为:" + new String(b, 0, n)); + // 向客户端发送反馈内容 + os.write(b, 0, n); + } + } catch (Exception e) { + log.error(e.getMessage(), e); + } finally { + try { + ResourceTool.close(os, is, socket, serverSocket); + } catch (Exception e) { + log.error(e.getMessage(), e); + } + } + } + } \ No newline at end of file diff --git a/java-network/src/main/java/com/github/kuangcp/runable/GreetingClient.java b/java-network/src/main/java/com/github/kuangcp/runable/GreetingClient.java index dc29e48b..3f556776 100644 --- a/java-network/src/main/java/com/github/kuangcp/runable/GreetingClient.java +++ b/java-network/src/main/java/com/github/kuangcp/runable/GreetingClient.java @@ -1,77 +1,77 @@ -package com.github.kuangcp.runable; - -import java.io.BufferedReader; -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.net.Socket; -import java.util.Scanner; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * 客户端 - */ -public class GreetingClient { - - private static Logger logger = LoggerFactory.getLogger(GreetingClient.class); - - public static void main(String[] s) { -// In(); - String[] args = {"localhost", "10000"}; - String serverName = args[0]; - int port = Integer.parseInt(args[1]); - - try { - Socket client = new Socket(serverName, port); - while (true) { - logger.info("### Connecting to " + serverName + " on port " + port); - - logger.info("Just connected to " + client.getRemoteSocketAddress()); - OutputStream outToServer = client.getOutputStream(); - - DataOutputStream out = new DataOutputStream(outToServer); - out.writeUTF("[客户端发送 : Hello from " + client.getLocalSocketAddress() + "]"); - - InputStream inFromServer = client.getInputStream(); - DataInputStream in = new DataInputStream(inFromServer); - logger.info("接收到服务器:" + in.readUTF()); - - while (true) { - InputStream inFromServer2 = client.getInputStream(); - DataInputStream in2 = new DataInputStream(inFromServer2); - logger.info("接收到服务器:" + in2.readUTF()); - - Scanner scanner = new Scanner(System.in); - String temp = scanner.nextLine(); - out = new DataOutputStream(outToServer); - out.writeUTF(temp); - if ("9090".equals(temp)) { - break; - } - } - break; - } - client.close(); - - } catch (IOException e) { - e.printStackTrace(); - } - } - - public static void In() { -// Scanner scanner = new Scanner(System.in); -// logger.info(scanner.nextLine()); -// scanner.nextLine(); - - BufferedReader sin = new BufferedReader(new InputStreamReader(System.in)); - try { - logger.info(sin.readLine()); - } catch (IOException e) { - e.printStackTrace(); - } - } -} +package com.github.kuangcp.runable; + +import java.io.BufferedReader; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.net.Socket; +import java.util.Scanner; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 客户端 + */ +public class GreetingClient { + + private static Logger logger = LoggerFactory.getLogger(GreetingClient.class); + + public static void main(String[] s) { +// In(); + String[] args = {"localhost", "10000"}; + String serverName = args[0]; + int port = Integer.parseInt(args[1]); + + try { + Socket client = new Socket(serverName, port); + while (true) { + logger.info("### Connecting to " + serverName + " on port " + port); + + logger.info("Just connected to " + client.getRemoteSocketAddress()); + OutputStream outToServer = client.getOutputStream(); + + DataOutputStream out = new DataOutputStream(outToServer); + out.writeUTF("[客户端发送 : Hello from " + client.getLocalSocketAddress() + "]"); + + InputStream inFromServer = client.getInputStream(); + DataInputStream in = new DataInputStream(inFromServer); + logger.info("接收到服务器:" + in.readUTF()); + + while (true) { + InputStream inFromServer2 = client.getInputStream(); + DataInputStream in2 = new DataInputStream(inFromServer2); + logger.info("接收到服务器:" + in2.readUTF()); + + Scanner scanner = new Scanner(System.in); + String temp = scanner.nextLine(); + out = new DataOutputStream(outToServer); + out.writeUTF(temp); + if ("9090".equals(temp)) { + break; + } + } + break; + } + client.close(); + + } catch (IOException e) { + e.printStackTrace(); + } + } + + public static void In() { +// Scanner scanner = new Scanner(System.in); +// logger.info(scanner.nextLine()); +// scanner.nextLine(); + + BufferedReader sin = new BufferedReader(new InputStreamReader(System.in)); + try { + logger.info(sin.readLine()); + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/java-network/src/main/java/com/github/kuangcp/runable/GreetingServer.java b/java-network/src/main/java/com/github/kuangcp/runable/GreetingServer.java index 35be0829..c99b191c 100644 --- a/java-network/src/main/java/com/github/kuangcp/runable/GreetingServer.java +++ b/java-network/src/main/java/com/github/kuangcp/runable/GreetingServer.java @@ -1,74 +1,74 @@ -package com.github.kuangcp.runable; - -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.IOException; -import java.net.ServerSocket; -import java.net.Socket; -import java.net.SocketTimeoutException; -import java.util.Scanner; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * 服务器端 - * 输入线程阻塞的原因,不能同时收发,怎么处理?多线程? - * 服务器不应该发出,而是做中转站 - */ -public class GreetingServer extends Thread { - - private ServerSocket serverSocket; - private static Logger logger = LoggerFactory.getLogger(GreetingServer.class); - - public GreetingServer(int port) throws IOException { - serverSocket = new ServerSocket(port); - serverSocket.setSoTimeout(10000); - } - - public void run() { - while (true) { - try { - logger.info("##### Waiting for client on port " + serverSocket.getLocalPort() + "..."); - Socket server = serverSocket.accept(); - Scanner scanner = new Scanner(System.in); - - logger.info("Just connected to " + server.getRemoteSocketAddress()); - DataInputStream in = new DataInputStream(server.getInputStream()); - logger.info("接收到的:" + in.readUTF()); - // 对客户端发出的消息 - DataOutputStream out = new DataOutputStream(server.getOutputStream()); - - while (true) { - String temp = scanner.nextLine(); - logger.info("input:" + temp); - out.writeUTF(temp); - if ("90".equals(temp)) { - break; - } - - } - out.writeUTF( - "Thank you for connecting to " + server.getLocalSocketAddress() + " Goodbye!"); - - server.close(); - } catch (SocketTimeoutException s) { - logger.info("Socket timed out!"); - break; - } catch (IOException e) { - e.printStackTrace(); - break; - } - } - } - - public static void main(String[] args) { -// int port = Integer.parseInt(args[0]); - int port = 10000; - try { - Thread t = new GreetingServer(port); - t.start(); - } catch (IOException e) { - e.printStackTrace(); - } - } -} +package com.github.kuangcp.runable; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.SocketTimeoutException; +import java.util.Scanner; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 服务器端 + * 输入线程阻塞的原因,不能同时收发,怎么处理?多线程? + * 服务器不应该发出,而是做中转站 + */ +public class GreetingServer extends Thread { + + private ServerSocket serverSocket; + private static Logger logger = LoggerFactory.getLogger(GreetingServer.class); + + public GreetingServer(int port) throws IOException { + serverSocket = new ServerSocket(port); + serverSocket.setSoTimeout(10000); + } + + public void run() { + while (true) { + try { + logger.info("##### Waiting for client on port " + serverSocket.getLocalPort() + "..."); + Socket server = serverSocket.accept(); + Scanner scanner = new Scanner(System.in); + + logger.info("Just connected to " + server.getRemoteSocketAddress()); + DataInputStream in = new DataInputStream(server.getInputStream()); + logger.info("接收到的:" + in.readUTF()); + // 对客户端发出的消息 + DataOutputStream out = new DataOutputStream(server.getOutputStream()); + + while (true) { + String temp = scanner.nextLine(); + logger.info("input:" + temp); + out.writeUTF(temp); + if ("90".equals(temp)) { + break; + } + + } + out.writeUTF( + "Thank you for connecting to " + server.getLocalSocketAddress() + " Goodbye!"); + + server.close(); + } catch (SocketTimeoutException s) { + logger.info("Socket timed out!"); + break; + } catch (IOException e) { + e.printStackTrace(); + break; + } + } + } + + public static void main(String[] args) { +// int port = Integer.parseInt(args[0]); + int port = 10000; + try { + Thread t = new GreetingServer(port); + t.start(); + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/java-network/src/main/java/com/github/kuangcp/selfclose/SocketSelfClose.java b/java-network/src/main/java/com/github/kuangcp/selfclose/SocketSelfClose.java index 7eb3f32e..4dcdd0ca 100644 --- a/java-network/src/main/java/com/github/kuangcp/selfclose/SocketSelfClose.java +++ b/java-network/src/main/java/com/github/kuangcp/selfclose/SocketSelfClose.java @@ -1,35 +1,35 @@ -package com.github.kuangcp.selfclose; - -import java.io.PrintStream; -import java.net.ServerSocket; -import java.net.Socket; -import java.util.Scanner; - -/** - * Created by Myth on 2017/4/3 - * Socket 半关闭状态,可以输入输出流单独控制关闭,但是关闭后就不能开启了 - * 只适用于一站式的通信协议,例如HTTP协议-客户端连接到服务器后,开始发送请求数据,发送完成后无需再次发送数据 - * 客户端,使用chatting room下的客户端即可进行测试验证 - */ -public class SocketSelfClose { - - public static void main(String[] strings) throws Exception { - ServerSocket serverSocket = new ServerSocket(30000); - Socket socket = serverSocket.accept(); - PrintStream printStream = new PrintStream(socket.getOutputStream()); - - printStream.println("服务器第一行数据"); - printStream.println("服务器第二行数据"); - - //关闭Socket的输出流, - socket.shutdownOutput(); - System.out.println("socket对象是否关闭" + socket.isClosed()); - Scanner scanner = new Scanner(socket.getInputStream()); - while (scanner.hasNextLine()) { - System.out.println(scanner.nextLine()); - } - scanner.close(); - socket.close(); - serverSocket.close(); - } -} +package com.github.kuangcp.selfclose; + +import java.io.PrintStream; +import java.net.ServerSocket; +import java.net.Socket; +import java.util.Scanner; + +/** + * Created by Myth on 2017/4/3 + * Socket 半关闭状态,可以输入输出流单独控制关闭,但是关闭后就不能开启了 + * 只适用于一站式的通信协议,例如HTTP协议-客户端连接到服务器后,开始发送请求数据,发送完成后无需再次发送数据 + * 客户端,使用chatting room下的客户端即可进行测试验证 + */ +public class SocketSelfClose { + + public static void main(String[] strings) throws Exception { + ServerSocket serverSocket = new ServerSocket(30000); + Socket socket = serverSocket.accept(); + PrintStream printStream = new PrintStream(socket.getOutputStream()); + + printStream.println("服务器第一行数据"); + printStream.println("服务器第二行数据"); + + //关闭Socket的输出流, + socket.shutdownOutput(); + System.out.println("socket对象是否关闭" + socket.isClosed()); + Scanner scanner = new Scanner(socket.getInputStream()); + while (scanner.hasNextLine()) { + System.out.println(scanner.nextLine()); + } + scanner.close(); + socket.close(); + serverSocket.close(); + } +} diff --git a/java-question/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethod/Equality.java b/java-question/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethod/Equality.java index 6d979e1d..78e55318 100644 --- a/java-question/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethod/Equality.java +++ b/java-question/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethod/Equality.java @@ -1,62 +1,62 @@ -package com.github.kuangcp.simpleMethod.SimplexMethod; - -import java.util.ArrayList; -import java.util.List; - -/** - * Created by Myth on 2017/3/20 0020 - * 约束式的对象形式 - */ -public class Equality { - - //式子的代表性参数X的下标 - Integer index; - List params; - Double result; - - public Equality() { - params = new ArrayList(); - } - - public Equality(List params, Double result) { - this.params = params; - this.result = result; - } - - public List getParams() { - return params; - } - - public void setParams(List params) { - this.params = params; - } - - public Double getResult() { - return result; - } - - public void setResult(Double result) { - this.result = result; - } - - public Integer getIndex() { - return index; - } - - public void setIndex(Integer index) { - this.index = index; - } - - @Override - public String toString() { - StringBuilder sub = new StringBuilder("["); - for (Double b : params) { - sub.append(b).append(","); - } - sub.append("]"); - return "Equality{" + - "params=" + sub + - ", result=" + result + - '}'; - } -} +package com.github.kuangcp.simpleMethod.SimplexMethod; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by Myth on 2017/3/20 0020 + * 约束式的对象形式 + */ +public class Equality { + + //式子的代表性参数X的下标 + Integer index; + List params; + Double result; + + public Equality() { + params = new ArrayList(); + } + + public Equality(List params, Double result) { + this.params = params; + this.result = result; + } + + public List getParams() { + return params; + } + + public void setParams(List params) { + this.params = params; + } + + public Double getResult() { + return result; + } + + public void setResult(Double result) { + this.result = result; + } + + public Integer getIndex() { + return index; + } + + public void setIndex(Integer index) { + this.index = index; + } + + @Override + public String toString() { + StringBuilder sub = new StringBuilder("["); + for (Double b : params) { + sub.append(b).append(","); + } + sub.append("]"); + return "Equality{" + + "params=" + sub + + ", result=" + result + + '}'; + } +} diff --git a/java-question/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethod/SimplexMethod.java b/java-question/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethod/SimplexMethod.java index c52d77ff..776deeb6 100644 --- a/java-question/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethod/SimplexMethod.java +++ b/java-question/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethod/SimplexMethod.java @@ -1,295 +1,295 @@ -package com.github.kuangcp.simpleMethod.SimplexMethod; - - -import com.github.kuangcp.simpleMethod.number.ReadProperties; -import java.util.ArrayList; -import java.util.List; - -/** - * Created by Myth on 2017/3/20 - * 使用单纯形法来求解最优解 - * 但是还有一个数据类型的问题是无法避免的,一般是用分数是最好的,但是浮点数方便计算。。。 - */ -public class SimplexMethod { - - //读取配置文件 - private static ReadProperties config; - //最大参数个数 - private static int MAXPARAMS; - //最大行列式数 - private static int EQUALITY; - //约束式子系数集合 - private List Rows = new ArrayList(); - //原始式子系数集合 - private List Max = new ArrayList(); - //单纯形表的总体数据结构 - private List Tables = new ArrayList
(); - //单纯形表中间计算结果右侧 - private List Os = new ArrayList(); - //单纯形表计算结果最下一行 - private List Zs = new ArrayList(); - //出入基锁定的坐标 - private Integer resultCol; - private Integer resultRow; - - static { - config = new ReadProperties("src/main/resources/math/SimplexMethod.properties"); - MAXPARAMS = config.getInt("MaxParams"); - EQUALITY = config.getInt("Equality"); - } - - public static void main(String[] s) { - SimplexMethod sm = new SimplexMethod(); - sm.run(); - } - - /** - * 初始化整体数据 - */ - public void init() { - //添加求解式的数据 - String maxs = config.getString("Max"); - String[] temp = maxs.split(","); - for (String data : temp) { - //System.out.println(data); - Max.add(Double.parseDouble(data)); - } - //添加约束式的数据 - for (int i = 1; i <= EQUALITY; i++) { - String buffer = config.getString("E" + i); - String[] tempList = buffer.split(","); - Equality e = new Equality(); - for (int j = 0; j < tempList.length; j++) { - e.getParams().add(Double.parseDouble(tempList[j])); - } - e.setResult(config.getDouble("B" + i)); - e.setIndex(config.getInt("I" + i)); - Rows.add(e); - } - //填入单纯形表总体数据中去 - for (int i = 0; i < EQUALITY; i++) { - Equality stRow = Rows.get(i); - Integer index = stRow.getIndex(); - //装填第一行的数据 - Table table = new Table(Max.get(index - 1), index, stRow.getResult(), stRow.getParams(), - null); - Tables.add(table); - } - //查看数据是否正确装填 - disp("单纯形表的中心数据 : ", Tables); -// disp("求解的方程式",Max); -// disp("约束条件的方程式",Rows); - //展示方程式 - System.out.println("原题样式 : "); - showRows(); - - } - - //进行表格的运算 - public void run() { - init(); - Boolean flag = true; -//运算出初始的表格 - //计算最底下一行 - CaculateLastRow(); - //计算右边列 - CaculateRightCol(); - //判断是否达到退出条件 - flag = exitTime(Zs); - disp("运行状态", Tables); - -//迭代的计算直到满足条件 - while (!flag) { - try { - Thread.sleep(10); - } catch (Exception e) { - e.printStackTrace(); - } - //算完一轮 - Double fatherNum = Tables.get(resultRow).getRows().get(resultCol); - List
oldTables = Tables; - Tables = new ArrayList
(); - - //转换出基入基的参数 - List rowsTemps = oldTables.get(resultRow).getRows(); - for (int k = 0; k < rowsTemps.size(); k++) { - rowsTemps.set(k, rowsTemps.get(k) / fatherNum); - } - Table temp = new Table(Max.get(resultCol), resultCol + 1, - oldTables.get(resultRow).getB() / fatherNum, rowsTemps, null); - Tables.add(temp); - //转换剩余的 - for (int j = 0; j < EQUALITY; j++) { - if (j != resultRow) { - rowsTemps = oldTables.get(j).getRows(); - Double motherNum = rowsTemps.get(resultCol); - Table fatherRow = Tables.get(0); - for (int k = 0; k < rowsTemps.size(); k++) { - rowsTemps.set(k, rowsTemps.get(k) - (motherNum * fatherRow.getRows().get(k))); - } - Integer tempXb = oldTables.get(j).getXb(); - Table otherTemp = new Table(Max.get(tempXb - 1), tempXb, - oldTables.get(j).getB() - motherNum * fatherRow.getB(), rowsTemps, null); - Tables.add(otherTemp); - } - - } -// System.out.println("Tables"+Tables.size()); - Zs.clear(); - Os.clear(); - //计算最后一行 - CaculateLastRow(); - //判断是否达到退出条件 - flag = exitTime(Zs); - if (!flag) { - CaculateRightCol(); - } - disp("运行状态", Tables, true); - disp("底栏", Zs, false); - - } - //运算完成 - Double result = 0.0; - Double[] results = new Double[MAXPARAMS]; - for (int i = 0; i < EQUALITY; i++) { - Table t = Tables.get(i); - result += t.getCb() * t.getB(); - results[t.getXb() - 1] = t.getB(); - } - System.out.println("最优目标值是 : " + result); - String resultstr = "X=("; - for (Double d : results) { - if (d != null) { - resultstr += d + ","; - } else { - resultstr += "0,"; - } - - } - resultstr = resultstr.substring(0, resultstr.length() - 1); - resultstr += ")"; - System.out.println(resultstr); - - } - - /** - * 计算最后一行和最右边的一列 - */ - public void CaculateLastRow() { - for (int i = 0; i < MAXPARAMS; i++) {//循环变量个数 - Double temp = Max.get(i); - for (int j = 0; j < EQUALITY; j++) {//循环层数 - temp -= Tables.get(j).getRows().get(i) * Tables.get(j).getCb(); - } - Zs.add(temp); - } - disp("最后一行", Zs, false); - resultCol = MaxList(Zs, true); - - } - - public void CaculateRightCol() { - //计算右边栏 - for (int i = 0; i < EQUALITY; i++) { - Double temp = Tables.get(i).getB() / Tables.get(i).getRows().get(resultCol); - Tables.get(i).setO(temp); - Os.add(temp); - } - //disp("右栏",Os,false); - resultRow = MaxList(Os, false); - } - - /** - * 判断最后一行是否到了最后的计算结果 - * - * @return true就是算到了最后一步 - */ - public boolean exitTime(List list) { - boolean flag = true; - for (Double b : list) { - if (b > 0) { - flag = false; - } - } - return flag; - } - - /** - * 求得集合中的极值元素下标 - * - * @param max true就是最大值 - * @return 极值下标 - */ - public Integer MaxList(List list, boolean max) { - Integer index = 0; - Double temp = list.get(index); - for (int i = 1; i < list.size(); i++) { - if (max && temp < list.get(i)) { - index = i; - temp = list.get(i); - } - if (!max && temp > list.get(i)) { - index = i; - temp = list.get(i); - } - } - return index; - } - - /** - * 方便展示原始数据 - */ - public void disp(String title, List list) { - disp(title, list, true); - } - - public void disp(String title, List list, boolean flag) { - System.out.println(title); - for (int i = 0; i < list.size(); i++) { - if (flag) { - System.out.println(list.get(i).toString()); - } else { - System.out.print(list.get(i).toString() + " "); - } - } - if (!flag) { - System.out.println(); - } - } - - /** - * 展示整体方程式,原题的样子 - */ - public void showRows() { - StringBuilder MaxRows = new StringBuilder("Max(z)="); - for (int i = 0; i < MAXPARAMS; i++) { - if (Max.get(i) != 0) { - MaxRows.append(Max.get(i)).append(" X").append(i + 1).append(" + "); - } - } - MaxRows = new StringBuilder(MaxRows.substring(0, MaxRows.length() - 2)); - System.out.println("Aim : " + MaxRows); - for (int i = 0; i < EQUALITY; i++) { - StringBuilder Row = new StringBuilder(); - List temp = Rows.get(i).getParams(); - for (int j = 0; j < temp.size(); j++) { - Double a = temp.get(j); - if (a != 0) { - if (a != 1) { - Row.append(a).append("X").append(j + 1).append(" + "); - } else { - Row.append("X").append(j + 1).append(" + "); - } - } else { - Row.append(" "); - } - } - Row = new StringBuilder(Row.substring(0, Row.length() - 2)); - Row.append(" = ").append(Rows.get(i).getResult()).append(" |X").append(Rows.get(i).getIndex()) - .append("|"); - System.out.println("Equality : " + Row); - } - } - -} - +package com.github.kuangcp.simpleMethod.SimplexMethod; + + +import com.github.kuangcp.simpleMethod.number.ReadProperties; +import java.util.ArrayList; +import java.util.List; + +/** + * Created by Myth on 2017/3/20 + * 使用单纯形法来求解最优解 + * 但是还有一个数据类型的问题是无法避免的,一般是用分数是最好的,但是浮点数方便计算。。。 + */ +public class SimplexMethod { + + //读取配置文件 + private static ReadProperties config; + //最大参数个数 + private static int MAXPARAMS; + //最大行列式数 + private static int EQUALITY; + //约束式子系数集合 + private List Rows = new ArrayList(); + //原始式子系数集合 + private List Max = new ArrayList(); + //单纯形表的总体数据结构 + private List
Tables = new ArrayList
(); + //单纯形表中间计算结果右侧 + private List Os = new ArrayList(); + //单纯形表计算结果最下一行 + private List Zs = new ArrayList(); + //出入基锁定的坐标 + private Integer resultCol; + private Integer resultRow; + + static { + config = new ReadProperties("src/main/resources/math/SimplexMethod.properties"); + MAXPARAMS = config.getInt("MaxParams"); + EQUALITY = config.getInt("Equality"); + } + + public static void main(String[] s) { + SimplexMethod sm = new SimplexMethod(); + sm.run(); + } + + /** + * 初始化整体数据 + */ + public void init() { + //添加求解式的数据 + String maxs = config.getString("Max"); + String[] temp = maxs.split(","); + for (String data : temp) { + //System.out.println(data); + Max.add(Double.parseDouble(data)); + } + //添加约束式的数据 + for (int i = 1; i <= EQUALITY; i++) { + String buffer = config.getString("E" + i); + String[] tempList = buffer.split(","); + Equality e = new Equality(); + for (int j = 0; j < tempList.length; j++) { + e.getParams().add(Double.parseDouble(tempList[j])); + } + e.setResult(config.getDouble("B" + i)); + e.setIndex(config.getInt("I" + i)); + Rows.add(e); + } + //填入单纯形表总体数据中去 + for (int i = 0; i < EQUALITY; i++) { + Equality stRow = Rows.get(i); + Integer index = stRow.getIndex(); + //装填第一行的数据 + Table table = new Table(Max.get(index - 1), index, stRow.getResult(), stRow.getParams(), + null); + Tables.add(table); + } + //查看数据是否正确装填 + disp("单纯形表的中心数据 : ", Tables); +// disp("求解的方程式",Max); +// disp("约束条件的方程式",Rows); + //展示方程式 + System.out.println("原题样式 : "); + showRows(); + + } + + //进行表格的运算 + public void run() { + init(); + Boolean flag = true; +//运算出初始的表格 + //计算最底下一行 + CaculateLastRow(); + //计算右边列 + CaculateRightCol(); + //判断是否达到退出条件 + flag = exitTime(Zs); + disp("运行状态", Tables); + +//迭代的计算直到满足条件 + while (!flag) { + try { + Thread.sleep(10); + } catch (Exception e) { + e.printStackTrace(); + } + //算完一轮 + Double fatherNum = Tables.get(resultRow).getRows().get(resultCol); + List
oldTables = Tables; + Tables = new ArrayList
(); + + //转换出基入基的参数 + List rowsTemps = oldTables.get(resultRow).getRows(); + for (int k = 0; k < rowsTemps.size(); k++) { + rowsTemps.set(k, rowsTemps.get(k) / fatherNum); + } + Table temp = new Table(Max.get(resultCol), resultCol + 1, + oldTables.get(resultRow).getB() / fatherNum, rowsTemps, null); + Tables.add(temp); + //转换剩余的 + for (int j = 0; j < EQUALITY; j++) { + if (j != resultRow) { + rowsTemps = oldTables.get(j).getRows(); + Double motherNum = rowsTemps.get(resultCol); + Table fatherRow = Tables.get(0); + for (int k = 0; k < rowsTemps.size(); k++) { + rowsTemps.set(k, rowsTemps.get(k) - (motherNum * fatherRow.getRows().get(k))); + } + Integer tempXb = oldTables.get(j).getXb(); + Table otherTemp = new Table(Max.get(tempXb - 1), tempXb, + oldTables.get(j).getB() - motherNum * fatherRow.getB(), rowsTemps, null); + Tables.add(otherTemp); + } + + } +// System.out.println("Tables"+Tables.size()); + Zs.clear(); + Os.clear(); + //计算最后一行 + CaculateLastRow(); + //判断是否达到退出条件 + flag = exitTime(Zs); + if (!flag) { + CaculateRightCol(); + } + disp("运行状态", Tables, true); + disp("底栏", Zs, false); + + } + //运算完成 + Double result = 0.0; + Double[] results = new Double[MAXPARAMS]; + for (int i = 0; i < EQUALITY; i++) { + Table t = Tables.get(i); + result += t.getCb() * t.getB(); + results[t.getXb() - 1] = t.getB(); + } + System.out.println("最优目标值是 : " + result); + String resultstr = "X=("; + for (Double d : results) { + if (d != null) { + resultstr += d + ","; + } else { + resultstr += "0,"; + } + + } + resultstr = resultstr.substring(0, resultstr.length() - 1); + resultstr += ")"; + System.out.println(resultstr); + + } + + /** + * 计算最后一行和最右边的一列 + */ + public void CaculateLastRow() { + for (int i = 0; i < MAXPARAMS; i++) {//循环变量个数 + Double temp = Max.get(i); + for (int j = 0; j < EQUALITY; j++) {//循环层数 + temp -= Tables.get(j).getRows().get(i) * Tables.get(j).getCb(); + } + Zs.add(temp); + } + disp("最后一行", Zs, false); + resultCol = MaxList(Zs, true); + + } + + public void CaculateRightCol() { + //计算右边栏 + for (int i = 0; i < EQUALITY; i++) { + Double temp = Tables.get(i).getB() / Tables.get(i).getRows().get(resultCol); + Tables.get(i).setO(temp); + Os.add(temp); + } + //disp("右栏",Os,false); + resultRow = MaxList(Os, false); + } + + /** + * 判断最后一行是否到了最后的计算结果 + * + * @return true就是算到了最后一步 + */ + public boolean exitTime(List list) { + boolean flag = true; + for (Double b : list) { + if (b > 0) { + flag = false; + } + } + return flag; + } + + /** + * 求得集合中的极值元素下标 + * + * @param max true就是最大值 + * @return 极值下标 + */ + public Integer MaxList(List list, boolean max) { + Integer index = 0; + Double temp = list.get(index); + for (int i = 1; i < list.size(); i++) { + if (max && temp < list.get(i)) { + index = i; + temp = list.get(i); + } + if (!max && temp > list.get(i)) { + index = i; + temp = list.get(i); + } + } + return index; + } + + /** + * 方便展示原始数据 + */ + public void disp(String title, List list) { + disp(title, list, true); + } + + public void disp(String title, List list, boolean flag) { + System.out.println(title); + for (int i = 0; i < list.size(); i++) { + if (flag) { + System.out.println(list.get(i).toString()); + } else { + System.out.print(list.get(i).toString() + " "); + } + } + if (!flag) { + System.out.println(); + } + } + + /** + * 展示整体方程式,原题的样子 + */ + public void showRows() { + StringBuilder MaxRows = new StringBuilder("Max(z)="); + for (int i = 0; i < MAXPARAMS; i++) { + if (Max.get(i) != 0) { + MaxRows.append(Max.get(i)).append(" X").append(i + 1).append(" + "); + } + } + MaxRows = new StringBuilder(MaxRows.substring(0, MaxRows.length() - 2)); + System.out.println("Aim : " + MaxRows); + for (int i = 0; i < EQUALITY; i++) { + StringBuilder Row = new StringBuilder(); + List temp = Rows.get(i).getParams(); + for (int j = 0; j < temp.size(); j++) { + Double a = temp.get(j); + if (a != 0) { + if (a != 1) { + Row.append(a).append("X").append(j + 1).append(" + "); + } else { + Row.append("X").append(j + 1).append(" + "); + } + } else { + Row.append(" "); + } + } + Row = new StringBuilder(Row.substring(0, Row.length() - 2)); + Row.append(" = ").append(Rows.get(i).getResult()).append(" |X").append(Rows.get(i).getIndex()) + .append("|"); + System.out.println("Equality : " + Row); + } + } + +} + diff --git a/java-question/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethod/Table.java b/java-question/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethod/Table.java index 162b74ee..d4567748 100644 --- a/java-question/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethod/Table.java +++ b/java-question/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethod/Table.java @@ -1,85 +1,85 @@ -package com.github.kuangcp.simpleMethod.SimplexMethod; - -import java.util.ArrayList; -import java.util.List; - -/** - * Created by Myth on 2017/3/21 0021 - * 单纯形表的总体结构中的行对象 - */ -public class Table { - - private Double Cb; - private Integer Xb; - private Double b; - private List rows; - private Double O; - - public Table() { - } - - public Table(Double cb, Integer xb, Double b, List row, Double o) { - Cb = cb; - Xb = xb; - this.b = b; - O = o; - rows = new ArrayList<>(); - rows.addAll(row); - } - - public Double getCb() { - return Cb; - } - - public void setCb(Double cb) { - Cb = cb; - } - - public Integer getXb() { - return Xb; - } - - public void setXb(Integer xb) { - Xb = xb; - } - - public Double getB() { - return b; - } - - public void setB(Double b) { - this.b = b; - } - - public List getRows() { - return rows; - } - - public void setRows(List rows) { - this.rows = rows; - } - - public Double getO() { - return O; - } - - public void setO(Double o) { - O = o; - } - - @Override - public String toString() { - StringBuilder sub = new StringBuilder("["); - for (Double b : rows) { - sub.append(b).append(","); - } - sub.append("]"); - return "Table{" + - "Cb=" + Cb + - ", Xb=" + Xb + - ", b=" + b + - ", rows=" + sub + - ", O=" + O + - '}'; - } -} +package com.github.kuangcp.simpleMethod.SimplexMethod; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by Myth on 2017/3/21 0021 + * 单纯形表的总体结构中的行对象 + */ +public class Table { + + private Double Cb; + private Integer Xb; + private Double b; + private List rows; + private Double O; + + public Table() { + } + + public Table(Double cb, Integer xb, Double b, List row, Double o) { + Cb = cb; + Xb = xb; + this.b = b; + O = o; + rows = new ArrayList<>(); + rows.addAll(row); + } + + public Double getCb() { + return Cb; + } + + public void setCb(Double cb) { + Cb = cb; + } + + public Integer getXb() { + return Xb; + } + + public void setXb(Integer xb) { + Xb = xb; + } + + public Double getB() { + return b; + } + + public void setB(Double b) { + this.b = b; + } + + public List getRows() { + return rows; + } + + public void setRows(List rows) { + this.rows = rows; + } + + public Double getO() { + return O; + } + + public void setO(Double o) { + O = o; + } + + @Override + public String toString() { + StringBuilder sub = new StringBuilder("["); + for (Double b : rows) { + sub.append(b).append(","); + } + sub.append("]"); + return "Table{" + + "Cb=" + Cb + + ", Xb=" + Xb + + ", b=" + b + + ", rows=" + sub + + ", O=" + O + + '}'; + } +} diff --git a/java-question/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethodQuarter/Equality.java b/java-question/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethodQuarter/Equality.java index c6b7ea06..6a9112fd 100644 --- a/java-question/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethodQuarter/Equality.java +++ b/java-question/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethodQuarter/Equality.java @@ -1,48 +1,48 @@ -package com.github.kuangcp.simpleMethod.SimplexMethodQuarter; - - -import com.github.kuangcp.math.number.Fraction; -import java.util.ArrayList; -import java.util.List; -import lombok.Data; - -/** - * Created by Myth on 2017/3/22 - * 约束式的对象形式 分数数据类型 - */ -@Data -public class Equality { - - Integer index; - List params; - Fraction result; - - public Equality() { - params = new ArrayList<>(); - } - - public Equality(List params, Fraction result) { - this.params = params; - this.result = result; - } - - public Equality(Integer index, List params, Fraction result) { - this.index = index; - this.params = params; - this.result = result; - } - - @Override - public String toString() { - StringBuilder sub = new StringBuilder("["); - for (Fraction b : params) { - sub.append(b.toString()).append(","); - } - sub.append("]"); - return "Equality{" + - "index=" + index + - ", params=" + sub + - ", result=" + result + - '}'; - } -} +package com.github.kuangcp.simpleMethod.SimplexMethodQuarter; + + +import com.github.kuangcp.math.number.Fraction; +import java.util.ArrayList; +import java.util.List; +import lombok.Data; + +/** + * Created by Myth on 2017/3/22 + * 约束式的对象形式 分数数据类型 + */ +@Data +public class Equality { + + Integer index; + List params; + Fraction result; + + public Equality() { + params = new ArrayList<>(); + } + + public Equality(List params, Fraction result) { + this.params = params; + this.result = result; + } + + public Equality(Integer index, List params, Fraction result) { + this.index = index; + this.params = params; + this.result = result; + } + + @Override + public String toString() { + StringBuilder sub = new StringBuilder("["); + for (Fraction b : params) { + sub.append(b.toString()).append(","); + } + sub.append("]"); + return "Equality{" + + "index=" + index + + ", params=" + sub + + ", result=" + result + + '}'; + } +} diff --git a/java-question/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethodQuarter/SimplexMethod.java b/java-question/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethodQuarter/SimplexMethod.java index d18e4235..23c08d19 100644 --- a/java-question/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethodQuarter/SimplexMethod.java +++ b/java-question/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethodQuarter/SimplexMethod.java @@ -1,409 +1,409 @@ -package com.github.kuangcp.simpleMethod.SimplexMethodQuarter; - -import com.github.kuangcp.math.number.Fraction; -import com.github.kuangcp.simpleMethod.number.ReadProperties; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * Created by Myth on 2017/3/22 0022 - */ -public class SimplexMethod { - - private static ReadProperties config;//读取配置文件 - private static int MAX_PARAMS;//最大参数个数 - private static int EQUALITY;//最大行列式数 - private List Rows = new ArrayList<>();//约束式子系数集合 - private List Max = new ArrayList<>();//原始式子系数集合 - private List
Tables = new ArrayList<>();//单纯形表的总体数据结构 - private List Os = new ArrayList<>();//单纯形表中间计算结果右侧 - private List Zs = new ArrayList<>();//单纯形表计算结果最下一行 - //出入基锁定的坐标 - private Integer resultCol; - private Integer resultRow; - private boolean CONTINUE = true;//记录是否要继续计算的标识属性 - // private boolean SUCCESS = true;//记录是否成功计算出结果 - private Map Xbs = new HashMap<>(); - private Integer Round = 1; - - static { - config = new ReadProperties("src/main/resources/math/SimplexMethod.properties"); - MAX_PARAMS = config.getInt("MaxParams"); - EQUALITY = config.getInt("Equality"); - } - - public static void main(String[] s) { - SimplexMethod sm = new SimplexMethod(); - try { - sm.run(); - } catch (Exception e) { - e.printStackTrace(); - } - } - - /** - * 初始化整体数据 - */ - private void init() { - //添加求解式的数据 - String maxs = config.getString("Max"); - String[] temp = maxs.split(","); - for (String data : temp) { -// System.out.println(data); - Max.add(Fraction.valueOf(data)); - } - //添加约束式的数据 - for (int i = 1; i <= EQUALITY; i++) { - String buffer = config.getString("E" + i); - String[] tempList = buffer.split(","); - Equality e = new Equality(); - for (String aTempList : tempList) { - e.getParams().add(Fraction.valueOf(aTempList)); - } - e.setResult(Fraction.valueOf(config.getString("B" + i))); - e.setIndex(config.getInt("I" + i)); - Rows.add(e); - - } - //填入单纯形表总体数据中去 - Equality stRow; - for (int i = 0; i < EQUALITY; i++) { - stRow = Rows.get(i); - Integer index = stRow.getIndex(); - //装填第一行的数据 - Table table = new Table(Max.get(index - 1), index, stRow.getResult(), stRow.getParams(), - null); - Tables.add(table); - } - //查看数据是否正确装填 -// disp("单纯形表的中心数据 : ",Tables); - disp("求解的方程式系数 :", false, Max, false); -// disp("约束条件的方程式",Rows); - //展示方程式 - System.out.println("原题样式 : "); - showRows(); - - } - - /** - * 循环计算直到出现结果 - */ - public void run() throws Exception { - init(); -//运算出初始的表格 - disp("中间计算结果", Tables); - //计算最底下一行 - CaculateLastRow(); - //计算右边列 - CaculateRightCol(); - //判断是否达到退出条件 - CONTINUE = isCONTINUES(Zs); - - //迭代的计算直到满足条件 - while (CONTINUE) { - try { - Thread.sleep(100); - } catch (Exception e) { - e.printStackTrace(); - } - //算完一轮 - Fraction fatherNum = Tables.get(resultRow).getRows().get(resultCol); - List
oldTables = Tables; - Tables = new ArrayList<>(); - - //转换出基入基的参数 - List rowsTemps = oldTables.get(resultRow).getRows(); - for (int k = 0; k < rowsTemps.size(); k++) { - rowsTemps.set(k, rowsTemps.get(k).divide(fatherNum)); - } - Table temp = new Table(Max.get(resultCol), resultCol + 1, - oldTables.get(resultRow).getBl().divide(fatherNum), rowsTemps, null); - Tables.add(temp); - //转换剩余的 - for (int j = 0; j < EQUALITY; j++) { - if (j != resultRow) { - rowsTemps = oldTables.get(j).getRows(); - Fraction motherNum = rowsTemps.get(resultCol); - Table fatherRow = Tables.get(0); - for (int k = 0; k < rowsTemps.size(); k++) { - rowsTemps.set(k, rowsTemps.get(k).subtract(motherNum.multiply(fatherRow.getRows().get(k)))); - } - Integer tempXb = oldTables.get(j).getXb(); - Fraction multiply = motherNum.multiply(fatherRow.getBl()); - Table otherTemp = new Table(Max.get(tempXb - 1), tempXb, - oldTables.get(j).getBl().subtract(multiply), rowsTemps, null); - Tables.add(otherTemp); - } - - } - Zs.clear(); - Os.clear(); -// log("下右两个计算集合的大小"+Zs.size()+":"+Os.size()); - - disp("中间计算结果", true, Tables, true); - //计算最后一行 - CaculateLastRow(); - //判断是否达到退出条件 - CONTINUE = isCONTINUES(Zs); - if (CONTINUE) { - CaculateRightCol(); - } - System.out.println("******************************"); - } - - //运算完成 - //@TODO 要进行判断是否成功计算,然后执行不同的方法 - finallyResult(); - } - - /** - * 处理最后运行结果,进行判断 - * 1. 右列没有一个正数,最后一行也没有正数 - * 2. Xb列死循环,即变量循环的出基入基 - * - * @throws Exception 异常 - */ - private void finallyResult() throws Exception { - Integer RightMin = MaxList(Os, false, true, false); - boolean SUCCESS = true; - if (RightMin == -1) { - System.out.println("右列没有一个正数,最后一行也没有正数,原方程没有最优解"); - SUCCESS = false; - } - //正确的计算出结果 - if (SUCCESS) { - Fraction result = new Fraction(0); - Fraction[] results = new Fraction[MAX_PARAMS]; - for (int i = 0; i < EQUALITY; i++) { - Table t = Tables.get(i); - result = result.add(t.getCb().multiply(t.getBl())); - results[t.getXb() - 1] = t.getBl(); - } - System.out.println("最优目标值是 : " + result); - StringBuilder resultstr = new StringBuilder("X=("); - for (Fraction d : results) { - if (d != null) { - resultstr.append(d).append(","); - } else { - resultstr.append("0,"); - } - } - resultstr = new StringBuilder(resultstr.substring(0, resultstr.length() - 1)); - resultstr.append(")"); - System.out.println(resultstr); - - } - for (String key : Xbs.keySet()) { - log(Xbs.get(key) + "轮" + key); - } - } - - /** - * 计算最后一行和最右边的一列 - */ - private void CaculateLastRow() throws Exception { - for (int i = 0; i < MAX_PARAMS; i++) {//循环变量个数 - Fraction temp = Max.get(i); -// disp("目标方程系数组",Max,false); -// System.out.println("Max中取到的temp"+temp); - for (int j = 0; j < EQUALITY; j++) {//循环层数 -// System.out.println("乘积 "+Tables.get(j).getCb()+"*"+Tables.get(j).getRows().get(i)+" = "+Tables.get(j).getCb().multiply(Tables.get(j).getRows().get(i))); - temp = temp.subtract(Tables.get(j).getCb().multiply(Tables.get(j).getRows().get(i))); -// System.out.println("temp:"+temp+"Cb:"+Tables.get(j).getCb()+"*"+Tables.get(j).getRows().get(i)); - } - Zs.add(temp); -// System.out.println("jieguo "+temp); - } - disp("最后一行", false, Zs, false); - resultCol = MaxList(Zs, true, true, true); - - } - - //计算右边栏 - private void CaculateRightCol() throws Exception { - -// log("计算所得行最大Index"+resultCol); - for (int i = 0; i < EQUALITY; i++) { -// log("计算表达式 "+Tables.get(i).getBl()+" / "+Tables.get(i).getRows().get(resultCol)); - Fraction temp = Tables.get(i).getBl().divide(Tables.get(i).getRows().get(resultCol)); -// log("结果"+temp); - Tables.get(i).setO(temp); - Os.add(temp); - } - disp("右栏", false, Os, false); - resultRow = MaxList(Os, false, true, false); - if (resultRow == -1) { - CONTINUE = false; - } -// log("右边最小index :"+resultRow); - // @ToDo 计算右边当右边有大于0的数 才继续,否则退出计算 要结合退出函数使用 - - } - - /** - * 1.判断最后一行是否到了最后的计算结果 即全小于0 就可以退出计算了 - * 2.又加入一个判断机制,循环出入基的情况 - * - * @param list 分数数组 - * @return false就不再继续 - */ - private boolean isCONTINUES(List list) { - boolean flag = false; - for (Fraction b : list) { - if (b.isPositive()) { - flag = true; - break; - } - } -// disp("检测",Tables); - StringBuilder temp = new StringBuilder(); - for (int i = 0; i < EQUALITY; i++) { - temp.append(Tables.get(i).getXb()); - } - if (!Xbs.containsKey(temp.toString())) { - Xbs.put(temp.toString(), Round++ + ""); - } else { - return false; - } - return flag; - - } - - /** - * 求解含有非数的集合的极值,异常返回-1 - * - * @param list 分数数组 - * @param isMax 是否求最大 - * @param haveInfinity 是否有非数 - * @param permitMinus 是否允许负数进行笔记比较 - * @return 最大 - */ - Integer MaxList(List list, boolean isMax, boolean haveInfinity, boolean permitMinus) { - Integer index = null; - //有非数的集合 - if (haveInfinity) { - Map tempMap = new HashMap<>(); - //去除非数 - for (int i = 0; i < list.size(); i++) { - if (permitMinus && list.get(i).getDenominator() != 0) { - tempMap.put(i, list.get(i)); - } - //不允许负数的情况 - if (!permitMinus) { - if (list.get(i).getDenominator() != 0 && list.get(i).isPositive()) { - tempMap.put(i, list.get(i)); - } - } - } - if (tempMap.size() == 0) { - return -1; - } - for (Integer integer : tempMap.keySet()) { - if (index == null) { - index = integer; - } else if (isMax && !tempMap.get(index).isGreaterThan(tempMap.get(integer))) { - index = integer; - } else if (!isMax && tempMap.get(index).isGreaterThan(tempMap.get(integer))) { - index = integer; - } - } - } - //没有非数的集合 - if (!haveInfinity) { - if (list.size() == 0) { - return -1; - } - Fraction temp = list.get(0); - for (int i = 0; i < list.size(); i++) { - if (list.get(i).isPositive()) { - if (isMax && !temp.isGreaterThan(list.get(i))) { - index = i; - temp = list.get(i); - } - if (!isMax && temp.isGreaterThan(list.get(i))) { - index = i; - temp = list.get(i); - } - } - - } - - } - return index; - } - - /** - * 方便展示原始数据 - * - * @param list 数据数组 - */ - private void disp(String title, List list) { - disp(title, true, list, true); - } - - /** - * 展示数据 - * - * @param title 标题 - * @param titleTurn 标题是否换行 - * @param list 数据 - * @param turn 内容是否换行 - */ - private void disp(String title, boolean titleTurn, List list, boolean turn) { - if (titleTurn) { - System.out.println(title); - } else { - System.out.print(title + " : "); - } - for (Object aList : list) { - if (turn) { - System.out.println(aList.toString()); - } else { - System.out.print(aList.toString() + " "); - } - } - if (!turn) { - System.out.println(); - } - } - - /** - * 展示整体方程式,原题的样子 - */ - private void showRows() { - StringBuilder MaxRows = new StringBuilder("Max(z)="); - for (int i = 0; i < MAX_PARAMS; i++) { - if (!Max.get(i).isZero()) { - MaxRows.append(Max.get(i)).append(" X").append(i + 1).append(" + "); - } - } - MaxRows = new StringBuilder(MaxRows.substring(0, MaxRows.length() - 2)); - System.out.println("Aim : " + MaxRows); - for (int i = 0; i < EQUALITY; i++) { - StringBuilder Row = new StringBuilder(); - List temp = Rows.get(i).getParams(); - for (int j = 0; j < temp.size(); j++) { - Fraction a = temp.get(j); - if (!a.isZero()) { - if (!a.isOne()) { - Row.append(a.toString()).append("X").append(j + 1).append(" + "); - } else { - Row.append("X").append(j + 1).append(" + "); - } - } else { - Row.append(" "); - } - } - Row = new StringBuilder(Row.substring(0, Row.length() - 2)); - Row.append(" = ").append(Rows.get(i).getResult()).append(" |X").append(Rows.get(i).getIndex()) - .append("|"); - System.out.println("Equality : " + Row); - } - } - - private void log(String s) { - System.out.println(s); - } -} +package com.github.kuangcp.simpleMethod.SimplexMethodQuarter; + +import com.github.kuangcp.math.number.Fraction; +import com.github.kuangcp.simpleMethod.number.ReadProperties; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Created by Myth on 2017/3/22 0022 + */ +public class SimplexMethod { + + private static ReadProperties config;//读取配置文件 + private static int MAX_PARAMS;//最大参数个数 + private static int EQUALITY;//最大行列式数 + private List Rows = new ArrayList<>();//约束式子系数集合 + private List Max = new ArrayList<>();//原始式子系数集合 + private List
Tables = new ArrayList<>();//单纯形表的总体数据结构 + private List Os = new ArrayList<>();//单纯形表中间计算结果右侧 + private List Zs = new ArrayList<>();//单纯形表计算结果最下一行 + //出入基锁定的坐标 + private Integer resultCol; + private Integer resultRow; + private boolean CONTINUE = true;//记录是否要继续计算的标识属性 + // private boolean SUCCESS = true;//记录是否成功计算出结果 + private Map Xbs = new HashMap<>(); + private Integer Round = 1; + + static { + config = new ReadProperties("src/main/resources/math/SimplexMethod.properties"); + MAX_PARAMS = config.getInt("MaxParams"); + EQUALITY = config.getInt("Equality"); + } + + public static void main(String[] s) { + SimplexMethod sm = new SimplexMethod(); + try { + sm.run(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * 初始化整体数据 + */ + private void init() { + //添加求解式的数据 + String maxs = config.getString("Max"); + String[] temp = maxs.split(","); + for (String data : temp) { +// System.out.println(data); + Max.add(Fraction.valueOf(data)); + } + //添加约束式的数据 + for (int i = 1; i <= EQUALITY; i++) { + String buffer = config.getString("E" + i); + String[] tempList = buffer.split(","); + Equality e = new Equality(); + for (String aTempList : tempList) { + e.getParams().add(Fraction.valueOf(aTempList)); + } + e.setResult(Fraction.valueOf(config.getString("B" + i))); + e.setIndex(config.getInt("I" + i)); + Rows.add(e); + + } + //填入单纯形表总体数据中去 + Equality stRow; + for (int i = 0; i < EQUALITY; i++) { + stRow = Rows.get(i); + Integer index = stRow.getIndex(); + //装填第一行的数据 + Table table = new Table(Max.get(index - 1), index, stRow.getResult(), stRow.getParams(), + null); + Tables.add(table); + } + //查看数据是否正确装填 +// disp("单纯形表的中心数据 : ",Tables); + disp("求解的方程式系数 :", false, Max, false); +// disp("约束条件的方程式",Rows); + //展示方程式 + System.out.println("原题样式 : "); + showRows(); + + } + + /** + * 循环计算直到出现结果 + */ + public void run() throws Exception { + init(); +//运算出初始的表格 + disp("中间计算结果", Tables); + //计算最底下一行 + CaculateLastRow(); + //计算右边列 + CaculateRightCol(); + //判断是否达到退出条件 + CONTINUE = isCONTINUES(Zs); + + //迭代的计算直到满足条件 + while (CONTINUE) { + try { + Thread.sleep(100); + } catch (Exception e) { + e.printStackTrace(); + } + //算完一轮 + Fraction fatherNum = Tables.get(resultRow).getRows().get(resultCol); + List
oldTables = Tables; + Tables = new ArrayList<>(); + + //转换出基入基的参数 + List rowsTemps = oldTables.get(resultRow).getRows(); + for (int k = 0; k < rowsTemps.size(); k++) { + rowsTemps.set(k, rowsTemps.get(k).divide(fatherNum)); + } + Table temp = new Table(Max.get(resultCol), resultCol + 1, + oldTables.get(resultRow).getBl().divide(fatherNum), rowsTemps, null); + Tables.add(temp); + //转换剩余的 + for (int j = 0; j < EQUALITY; j++) { + if (j != resultRow) { + rowsTemps = oldTables.get(j).getRows(); + Fraction motherNum = rowsTemps.get(resultCol); + Table fatherRow = Tables.get(0); + for (int k = 0; k < rowsTemps.size(); k++) { + rowsTemps.set(k, rowsTemps.get(k).subtract(motherNum.multiply(fatherRow.getRows().get(k)))); + } + Integer tempXb = oldTables.get(j).getXb(); + Fraction multiply = motherNum.multiply(fatherRow.getBl()); + Table otherTemp = new Table(Max.get(tempXb - 1), tempXb, + oldTables.get(j).getBl().subtract(multiply), rowsTemps, null); + Tables.add(otherTemp); + } + + } + Zs.clear(); + Os.clear(); +// log("下右两个计算集合的大小"+Zs.size()+":"+Os.size()); + + disp("中间计算结果", true, Tables, true); + //计算最后一行 + CaculateLastRow(); + //判断是否达到退出条件 + CONTINUE = isCONTINUES(Zs); + if (CONTINUE) { + CaculateRightCol(); + } + System.out.println("******************************"); + } + + //运算完成 + //@TODO 要进行判断是否成功计算,然后执行不同的方法 + finallyResult(); + } + + /** + * 处理最后运行结果,进行判断 + * 1. 右列没有一个正数,最后一行也没有正数 + * 2. Xb列死循环,即变量循环的出基入基 + * + * @throws Exception 异常 + */ + private void finallyResult() throws Exception { + Integer RightMin = MaxList(Os, false, true, false); + boolean SUCCESS = true; + if (RightMin == -1) { + System.out.println("右列没有一个正数,最后一行也没有正数,原方程没有最优解"); + SUCCESS = false; + } + //正确的计算出结果 + if (SUCCESS) { + Fraction result = new Fraction(0); + Fraction[] results = new Fraction[MAX_PARAMS]; + for (int i = 0; i < EQUALITY; i++) { + Table t = Tables.get(i); + result = result.add(t.getCb().multiply(t.getBl())); + results[t.getXb() - 1] = t.getBl(); + } + System.out.println("最优目标值是 : " + result); + StringBuilder resultstr = new StringBuilder("X=("); + for (Fraction d : results) { + if (d != null) { + resultstr.append(d).append(","); + } else { + resultstr.append("0,"); + } + } + resultstr = new StringBuilder(resultstr.substring(0, resultstr.length() - 1)); + resultstr.append(")"); + System.out.println(resultstr); + + } + for (String key : Xbs.keySet()) { + log(Xbs.get(key) + "轮" + key); + } + } + + /** + * 计算最后一行和最右边的一列 + */ + private void CaculateLastRow() throws Exception { + for (int i = 0; i < MAX_PARAMS; i++) {//循环变量个数 + Fraction temp = Max.get(i); +// disp("目标方程系数组",Max,false); +// System.out.println("Max中取到的temp"+temp); + for (int j = 0; j < EQUALITY; j++) {//循环层数 +// System.out.println("乘积 "+Tables.get(j).getCb()+"*"+Tables.get(j).getRows().get(i)+" = "+Tables.get(j).getCb().multiply(Tables.get(j).getRows().get(i))); + temp = temp.subtract(Tables.get(j).getCb().multiply(Tables.get(j).getRows().get(i))); +// System.out.println("temp:"+temp+"Cb:"+Tables.get(j).getCb()+"*"+Tables.get(j).getRows().get(i)); + } + Zs.add(temp); +// System.out.println("jieguo "+temp); + } + disp("最后一行", false, Zs, false); + resultCol = MaxList(Zs, true, true, true); + + } + + //计算右边栏 + private void CaculateRightCol() throws Exception { + +// log("计算所得行最大Index"+resultCol); + for (int i = 0; i < EQUALITY; i++) { +// log("计算表达式 "+Tables.get(i).getBl()+" / "+Tables.get(i).getRows().get(resultCol)); + Fraction temp = Tables.get(i).getBl().divide(Tables.get(i).getRows().get(resultCol)); +// log("结果"+temp); + Tables.get(i).setO(temp); + Os.add(temp); + } + disp("右栏", false, Os, false); + resultRow = MaxList(Os, false, true, false); + if (resultRow == -1) { + CONTINUE = false; + } +// log("右边最小index :"+resultRow); + // @ToDo 计算右边当右边有大于0的数 才继续,否则退出计算 要结合退出函数使用 + + } + + /** + * 1.判断最后一行是否到了最后的计算结果 即全小于0 就可以退出计算了 + * 2.又加入一个判断机制,循环出入基的情况 + * + * @param list 分数数组 + * @return false就不再继续 + */ + private boolean isCONTINUES(List list) { + boolean flag = false; + for (Fraction b : list) { + if (b.isPositive()) { + flag = true; + break; + } + } +// disp("检测",Tables); + StringBuilder temp = new StringBuilder(); + for (int i = 0; i < EQUALITY; i++) { + temp.append(Tables.get(i).getXb()); + } + if (!Xbs.containsKey(temp.toString())) { + Xbs.put(temp.toString(), Round++ + ""); + } else { + return false; + } + return flag; + + } + + /** + * 求解含有非数的集合的极值,异常返回-1 + * + * @param list 分数数组 + * @param isMax 是否求最大 + * @param haveInfinity 是否有非数 + * @param permitMinus 是否允许负数进行笔记比较 + * @return 最大 + */ + Integer MaxList(List list, boolean isMax, boolean haveInfinity, boolean permitMinus) { + Integer index = null; + //有非数的集合 + if (haveInfinity) { + Map tempMap = new HashMap<>(); + //去除非数 + for (int i = 0; i < list.size(); i++) { + if (permitMinus && list.get(i).getDenominator() != 0) { + tempMap.put(i, list.get(i)); + } + //不允许负数的情况 + if (!permitMinus) { + if (list.get(i).getDenominator() != 0 && list.get(i).isPositive()) { + tempMap.put(i, list.get(i)); + } + } + } + if (tempMap.size() == 0) { + return -1; + } + for (Integer integer : tempMap.keySet()) { + if (index == null) { + index = integer; + } else if (isMax && !tempMap.get(index).isGreaterThan(tempMap.get(integer))) { + index = integer; + } else if (!isMax && tempMap.get(index).isGreaterThan(tempMap.get(integer))) { + index = integer; + } + } + } + //没有非数的集合 + if (!haveInfinity) { + if (list.size() == 0) { + return -1; + } + Fraction temp = list.get(0); + for (int i = 0; i < list.size(); i++) { + if (list.get(i).isPositive()) { + if (isMax && !temp.isGreaterThan(list.get(i))) { + index = i; + temp = list.get(i); + } + if (!isMax && temp.isGreaterThan(list.get(i))) { + index = i; + temp = list.get(i); + } + } + + } + + } + return index; + } + + /** + * 方便展示原始数据 + * + * @param list 数据数组 + */ + private void disp(String title, List list) { + disp(title, true, list, true); + } + + /** + * 展示数据 + * + * @param title 标题 + * @param titleTurn 标题是否换行 + * @param list 数据 + * @param turn 内容是否换行 + */ + private void disp(String title, boolean titleTurn, List list, boolean turn) { + if (titleTurn) { + System.out.println(title); + } else { + System.out.print(title + " : "); + } + for (Object aList : list) { + if (turn) { + System.out.println(aList.toString()); + } else { + System.out.print(aList.toString() + " "); + } + } + if (!turn) { + System.out.println(); + } + } + + /** + * 展示整体方程式,原题的样子 + */ + private void showRows() { + StringBuilder MaxRows = new StringBuilder("Max(z)="); + for (int i = 0; i < MAX_PARAMS; i++) { + if (!Max.get(i).isZero()) { + MaxRows.append(Max.get(i)).append(" X").append(i + 1).append(" + "); + } + } + MaxRows = new StringBuilder(MaxRows.substring(0, MaxRows.length() - 2)); + System.out.println("Aim : " + MaxRows); + for (int i = 0; i < EQUALITY; i++) { + StringBuilder Row = new StringBuilder(); + List temp = Rows.get(i).getParams(); + for (int j = 0; j < temp.size(); j++) { + Fraction a = temp.get(j); + if (!a.isZero()) { + if (!a.isOne()) { + Row.append(a.toString()).append("X").append(j + 1).append(" + "); + } else { + Row.append("X").append(j + 1).append(" + "); + } + } else { + Row.append(" "); + } + } + Row = new StringBuilder(Row.substring(0, Row.length() - 2)); + Row.append(" = ").append(Rows.get(i).getResult()).append(" |X").append(Rows.get(i).getIndex()) + .append("|"); + System.out.println("Equality : " + Row); + } + } + + private void log(String s) { + System.out.println(s); + } +} diff --git a/java-question/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethodQuarter/Table.java b/java-question/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethodQuarter/Table.java index be7e2d79..521491ef 100644 --- a/java-question/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethodQuarter/Table.java +++ b/java-question/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethodQuarter/Table.java @@ -1,46 +1,46 @@ -package com.github.kuangcp.simpleMethod.SimplexMethodQuarter; - - -import com.github.kuangcp.math.number.Fraction; -import java.util.ArrayList; -import java.util.List; -import lombok.Data; - -/** - * Created by Myth on 2017/3/22 - * 使用分数作为基本数据类型来计算 - */ -@Data -public class Table { - - private Fraction cb; - private Integer xb; - private Fraction bl; - private List rows; - private Fraction o; - - public Table(Fraction cb, Integer xb, Fraction bl, List rows, Fraction o) { - this.cb = cb; - this.xb = xb; - this.bl = bl; - this.o = o; - this.rows = new ArrayList<>(); - this.rows.addAll(rows); - } - - @Override - public String toString() { - StringBuilder sub = new StringBuilder("["); - for (Fraction b : rows) { - sub.append(b.toString()).append(","); - } - sub.append("]"); - return "Table{" + - "cb=" + cb + - ", xb=" + xb + - ", bl=" + bl + - ", rows=" + sub + - ", o=" + o + - '}'; - } -} +package com.github.kuangcp.simpleMethod.SimplexMethodQuarter; + + +import com.github.kuangcp.math.number.Fraction; +import java.util.ArrayList; +import java.util.List; +import lombok.Data; + +/** + * Created by Myth on 2017/3/22 + * 使用分数作为基本数据类型来计算 + */ +@Data +public class Table { + + private Fraction cb; + private Integer xb; + private Fraction bl; + private List rows; + private Fraction o; + + public Table(Fraction cb, Integer xb, Fraction bl, List rows, Fraction o) { + this.cb = cb; + this.xb = xb; + this.bl = bl; + this.o = o; + this.rows = new ArrayList<>(); + this.rows.addAll(rows); + } + + @Override + public String toString() { + StringBuilder sub = new StringBuilder("["); + for (Fraction b : rows) { + sub.append(b.toString()).append(","); + } + sub.append("]"); + return "Table{" + + "cb=" + cb + + ", xb=" + xb + + ", bl=" + bl + + ", rows=" + sub + + ", o=" + o + + '}'; + } +} diff --git a/java-question/src/main/java/com/github/kuangcp/simpleMethod/number/ReadProperties.java b/java-question/src/main/java/com/github/kuangcp/simpleMethod/number/ReadProperties.java index 1d092f24..10c06916 100644 --- a/java-question/src/main/java/com/github/kuangcp/simpleMethod/number/ReadProperties.java +++ b/java-question/src/main/java/com/github/kuangcp/simpleMethod/number/ReadProperties.java @@ -1,52 +1,52 @@ -package com.github.kuangcp.simpleMethod.number; - -import java.io.File; -import java.io.FileInputStream; -import java.nio.charset.StandardCharsets; -import java.util.Properties; - -/** - * Created by Myth on 2017/1/13 0013 - 20:51 - * FIXME 读取不到文件 - */ -public class ReadProperties { - - private Properties cfg = new Properties(); - - /** - * 输入的是从src开始的路径: src/main/resources/a.properties - */ - public ReadProperties(String file) { - try { - File f = new File(file); - cfg.load(new FileInputStream(f)); - } catch (Exception e) { - e.printStackTrace(); - throw new RuntimeException(e); - } - } - - public String getString(String key) { - return cfg.getProperty(key); - } - - public int getInt(String key) { - return Integer.parseInt(cfg.getProperty(key)); - } - - public double getDouble(String key) { - return Double.parseDouble(getString(key)); - } - - public static void main(String[] a) { - ReadProperties read = new ReadProperties("src/main/resources/math/SimplexMethod.properties"); - String result = read.getString("78"); - try { - //配置文件含中文需要转码 - System.out.println(new String(result.getBytes(StandardCharsets.ISO_8859_1), - StandardCharsets.UTF_8)); - } catch (Exception e) { - e.printStackTrace(); - } - } -} +package com.github.kuangcp.simpleMethod.number; + +import java.io.File; +import java.io.FileInputStream; +import java.nio.charset.StandardCharsets; +import java.util.Properties; + +/** + * Created by Myth on 2017/1/13 0013 - 20:51 + * FIXME 读取不到文件 + */ +public class ReadProperties { + + private Properties cfg = new Properties(); + + /** + * 输入的是从src开始的路径: src/main/resources/a.properties + */ + public ReadProperties(String file) { + try { + File f = new File(file); + cfg.load(new FileInputStream(f)); + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException(e); + } + } + + public String getString(String key) { + return cfg.getProperty(key); + } + + public int getInt(String key) { + return Integer.parseInt(cfg.getProperty(key)); + } + + public double getDouble(String key) { + return Double.parseDouble(getString(key)); + } + + public static void main(String[] a) { + ReadProperties read = new ReadProperties("src/main/resources/math/SimplexMethod.properties"); + String result = read.getString("78"); + try { + //配置文件含中文需要转码 + System.out.println(new String(result.getBytes(StandardCharsets.ISO_8859_1), + StandardCharsets.UTF_8)); + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/java-question/src/main/resources/math/SimplexMethod.properties b/java-question/src/main/resources/math/SimplexMethod.properties index 5b73e13f..75e34e91 100644 --- a/java-question/src/main/resources/math/SimplexMethod.properties +++ b/java-question/src/main/resources/math/SimplexMethod.properties @@ -1,52 +1,52 @@ -#I代表的是一个式子里基向量,记录了初始单纯形表的数据 -#MaxParams=5 -#Equality=3 -#Max=1500,2500,0,0,0 -# -#E1=3,2,1,0,0 -#B1=65 -#I1=3 -# -#E2=2,1,0,1,0 -#B2=40 -#I2=4 -# -#E3=0,3,0,0,1 -#B3=75 -#I3=5 - -################################### - -#MaxParams=6 -#Equality=3 -#Max=0,0,0,0,-1,-1 -# -#E1=1,2,3,0,1,0 -#B1=15 -#I1=5 -# -#E2=2,1,5,0,0,1 -#B2=20 -#I2=6 -# -#E3=1,2,4,1,0,0 -#B3=26 -#I3=4 - -################################# - -MaxParams=8 -Equality=3 -Max=0,0,0,0,0,0,-1,-1 - -E1=-1,5,1,0,0,1,0,0 -B1=4 -I1=6 - -E2=1,1,1,-1,0,0,1,0 -B2=3 -I2=7 - -E3=-1,2,0,0,-1,0,0,1 -B3=2 -I3=8 +#I代表的是一个式子里基向量,记录了初始单纯形表的数据 +#MaxParams=5 +#Equality=3 +#Max=1500,2500,0,0,0 +# +#E1=3,2,1,0,0 +#B1=65 +#I1=3 +# +#E2=2,1,0,1,0 +#B2=40 +#I2=4 +# +#E3=0,3,0,0,1 +#B3=75 +#I3=5 + +################################### + +#MaxParams=6 +#Equality=3 +#Max=0,0,0,0,-1,-1 +# +#E1=1,2,3,0,1,0 +#B1=15 +#I1=5 +# +#E2=2,1,5,0,0,1 +#B2=20 +#I2=6 +# +#E3=1,2,4,1,0,0 +#B3=26 +#I3=4 + +################################# + +MaxParams=8 +Equality=3 +Max=0,0,0,0,0,0,-1,-1 + +E1=-1,5,1,0,0,1,0,0 +B1=4 +I1=6 + +E2=1,1,1,-1,0,0,1,0 +B2=3 +I2=7 + +E3=-1,2,0,0,-1,0,0,1 +B3=2 +I3=8 diff --git a/java-question/src/main/resources/math/log4j.xml b/java-question/src/main/resources/math/log4j.xml index 7c6e4ed3..04c2323d 100644 --- a/java-question/src/main/resources/math/log4j.xml +++ b/java-question/src/main/resources/math/log4j.xml @@ -1,63 +1,63 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/java-question/src/test/java/com/github/kuangcp/simpleMethod/SimplexMethodQuarter/MethodTest.java b/java-question/src/test/java/com/github/kuangcp/simpleMethod/SimplexMethodQuarter/MethodTest.java index 201cee7a..d73d1233 100644 --- a/java-question/src/test/java/com/github/kuangcp/simpleMethod/SimplexMethodQuarter/MethodTest.java +++ b/java-question/src/test/java/com/github/kuangcp/simpleMethod/SimplexMethodQuarter/MethodTest.java @@ -1,47 +1,47 @@ -package com.github.kuangcp.simpleMethod.SimplexMethodQuarter; - -import com.github.kuangcp.math.number.Fraction; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import org.junit.Test; - -/** - * Created by Myth on 2017/3/23 0023 - */ -public class MethodTest { - - @Test - public void testMax() { - List list = new ArrayList<>(); - - list.add(new Fraction(-1, 3)); - list.add(new Fraction(-15, 1)); - list.add(new Fraction(5, 2)); - SimplexMethod sm = new SimplexMethod(); - Integer index = sm.MaxList(list, false, true, false); - if (index != -1) { - System.out.println(index + " => " + list.get(index)); - } else { - System.out.println("没有合适的数据"); - } - for (Fraction t : list) { - System.out.println(t.toString()); - } - } - - @Test - public void testMap() { - Map temp = new HashMap(); - temp.put("2", "3"); - if (!temp.containsKey("2")) { - temp.put("2", "5"); - } else { - System.out.println("All"); - } - for (String key : temp.keySet()) { - System.out.println(temp.get(key)); - } - } -} +package com.github.kuangcp.simpleMethod.SimplexMethodQuarter; + +import com.github.kuangcp.math.number.Fraction; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.junit.Test; + +/** + * Created by Myth on 2017/3/23 0023 + */ +public class MethodTest { + + @Test + public void testMax() { + List list = new ArrayList<>(); + + list.add(new Fraction(-1, 3)); + list.add(new Fraction(-15, 1)); + list.add(new Fraction(5, 2)); + SimplexMethod sm = new SimplexMethod(); + Integer index = sm.MaxList(list, false, true, false); + if (index != -1) { + System.out.println(index + " => " + list.get(index)); + } else { + System.out.println("没有合适的数据"); + } + for (Fraction t : list) { + System.out.println(t.toString()); + } + } + + @Test + public void testMap() { + Map temp = new HashMap(); + temp.put("2", "3"); + if (!temp.containsKey("2")) { + temp.put("2", "5"); + } else { + System.out.println("All"); + } + for (String key : temp.keySet()) { + System.out.println(temp.get(key)); + } + } +} diff --git a/settings.gradle b/settings.gradle index 7c158347..b78497b2 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,19 +1,19 @@ -rootProject.name = 'JavaBase' -include( - 'java-io' - , 'java-generic' - , 'java-collection' - , 'java-class' - , 'java-gui' - , 'java-network' - , 'java-thread' - , 'java-algorithms' - , 'java-question' - , 'java-pattern' - , 'java-8' - , 'java-concurrency' - , 'java-test' - , 'java-guava' - , 'java-netty' - , 'java-spring' -) +rootProject.name = 'JavaBase' +include( + 'java-io' + , 'java-generic' + , 'java-collection' + , 'java-class' + , 'java-gui' + , 'java-network' + , 'java-thread' + , 'java-algorithms' + , 'java-question' + , 'java-pattern' + , 'java-8' + , 'java-concurrency' + , 'java-test' + , 'java-guava' + , 'java-netty' + , 'java-spring' +) From 6e601f0bdc74b8454284a778bf756fa64cfe9737 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Mon, 15 Apr 2019 00:53:08 +0800 Subject: [PATCH 004/476] +) init class --- .../kuangcp/loader/ClassInitialTest.java | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 java-class/src/test/java/com/github/kuangcp/loader/ClassInitialTest.java diff --git a/java-class/src/test/java/com/github/kuangcp/loader/ClassInitialTest.java b/java-class/src/test/java/com/github/kuangcp/loader/ClassInitialTest.java new file mode 100644 index 00000000..8f8ed5e5 --- /dev/null +++ b/java-class/src/test/java/com/github/kuangcp/loader/ClassInitialTest.java @@ -0,0 +1,29 @@ +package com.github.kuangcp.loader; + +import lombok.extern.slf4j.Slf4j; + +/** + * @author kuangcp on 2019-04-15 12:08 AM + */ +@Slf4j +public class ClassInitialTest { + + public static void main(String[] args) throws ClassNotFoundException { + String path = "com.github.kuangcp.loader.InitialTest"; + Class clazz = InitialTest.class; + log.debug("InitialTest.class"); + + Thread.currentThread().getContextClassLoader().loadClass(path); + log.debug("classLoader.loadClass"); + + Class.forName(path); + log.debug("Class.forName"); + } +} +@Slf4j +class InitialTest { + + static { + log.info("init class"); + } +} From 6cc69a5f766c9631baabc7b0114e58a463de48ec Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Mon, 15 Apr 2019 13:31:51 +0800 Subject: [PATCH 005/476] *) make thread sorted --- .../kuangcp/loader/ClassInitialTest.java | 14 ++-- .../java/com/github/kuangcp/order/Holder.java | 10 --- .../java/com/github/kuangcp/order/README.md | 4 +- .../java/com/github/kuangcp/order/Show.java | 36 ----------- .../kuangcp/order/ShowWithVolatile.java | 45 ------------- .../java/com/github/kuangcp/order/Task.java | 46 +++++++++++++ .../kuangcp/order/TaskWithVolatile.java | 49 ++++++++++++++ .../kuangcp/order/OrderedThreadTest.java | 62 ++++++++++++++++++ .../com/github/kuangcp/order/ShowTest.java | 64 ------------------- 9 files changed, 167 insertions(+), 163 deletions(-) delete mode 100644 java-thread/src/main/java/com/github/kuangcp/order/Holder.java delete mode 100644 java-thread/src/main/java/com/github/kuangcp/order/Show.java delete mode 100644 java-thread/src/main/java/com/github/kuangcp/order/ShowWithVolatile.java create mode 100644 java-thread/src/main/java/com/github/kuangcp/order/Task.java create mode 100644 java-thread/src/main/java/com/github/kuangcp/order/TaskWithVolatile.java create mode 100644 java-thread/src/test/java/com/github/kuangcp/order/OrderedThreadTest.java delete mode 100644 java-thread/src/test/java/com/github/kuangcp/order/ShowTest.java diff --git a/java-class/src/test/java/com/github/kuangcp/loader/ClassInitialTest.java b/java-class/src/test/java/com/github/kuangcp/loader/ClassInitialTest.java index 8f8ed5e5..a9bfec58 100644 --- a/java-class/src/test/java/com/github/kuangcp/loader/ClassInitialTest.java +++ b/java-class/src/test/java/com/github/kuangcp/loader/ClassInitialTest.java @@ -10,16 +10,18 @@ public class ClassInitialTest { public static void main(String[] args) throws ClassNotFoundException { String path = "com.github.kuangcp.loader.InitialTest"; - Class clazz = InitialTest.class; - log.debug("InitialTest.class"); - Thread.currentThread().getContextClassLoader().loadClass(path); - log.debug("classLoader.loadClass"); + Class clazz = InitialTest.class; + log.debug("InitialTest.class {}", clazz.getSimpleName()); - Class.forName(path); - log.debug("Class.forName"); + clazz = Thread.currentThread().getContextClassLoader().loadClass(path); + log.debug("classLoader.loadClass {}", clazz.getSimpleName()); + + clazz = Class.forName(path); + log.debug("Class.forName {}", clazz.getSimpleName()); } } + @Slf4j class InitialTest { diff --git a/java-thread/src/main/java/com/github/kuangcp/order/Holder.java b/java-thread/src/main/java/com/github/kuangcp/order/Holder.java deleted file mode 100644 index f9054b3e..00000000 --- a/java-thread/src/main/java/com/github/kuangcp/order/Holder.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.github.kuangcp.order; - -/** - * Created by https://github.com/kuangcp on 18-1-18 下午5:04 - * - * @author kuangcp - */ -public class Holder { - volatile int count = 0; -} diff --git a/java-thread/src/main/java/com/github/kuangcp/order/README.md b/java-thread/src/main/java/com/github/kuangcp/order/README.md index e9e31c8a..f3dc9432 100644 --- a/java-thread/src/main/java/com/github/kuangcp/order/README.md +++ b/java-thread/src/main/java/com/github/kuangcp/order/README.md @@ -1,7 +1,7 @@ # 开启多个线程,并有序执行这些线程 ## 第一种 -利用原子递增控制线程准入顺序。 +利用原子递增控制线程准入顺序。但是还是不能完全控制顺序 ## 第二种 -使用volatile关键字 \ No newline at end of file +使用volatile关键字, 达到需求 diff --git a/java-thread/src/main/java/com/github/kuangcp/order/Show.java b/java-thread/src/main/java/com/github/kuangcp/order/Show.java deleted file mode 100644 index ec05499e..00000000 --- a/java-thread/src/main/java/com/github/kuangcp/order/Show.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.github.kuangcp.order; - -import java.util.concurrent.atomic.AtomicInteger; - -/** - * Created by https://github.com/kuangcp on 18-1-18 下午1:53 - * 利用原子递增控制线程准入顺序。 - * @author kuangcp - */ -public class Show implements Runnable{ - private String target; - private int order; - private AtomicInteger count; //利用原子递增控制线程准入顺序。 - - public Show(String target, int order, AtomicInteger count) { - this.target = target; - this.order = order; - this.count = count; - } - - @Override - public void run() { - while (true) { - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - if(count.get() % 5 == order) { - System.out.print(target); - count.incrementAndGet(); - } - - } - } -} diff --git a/java-thread/src/main/java/com/github/kuangcp/order/ShowWithVolatile.java b/java-thread/src/main/java/com/github/kuangcp/order/ShowWithVolatile.java deleted file mode 100644 index f0d97046..00000000 --- a/java-thread/src/main/java/com/github/kuangcp/order/ShowWithVolatile.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.github.kuangcp.order; - -/** - * Created by https://github.com/kuangcp on 18-1-18 下午1:57 - * 使用了volatile关键字。让每个线程都能拿到最新的count的值,当其中一个线程执行++操作后, - * 其他两个线程就会拿到最新的值,并检查是否符合准入条件。 - * - * volatile不是线程安全的。而且两者没有任何关系。volatile变量不在用户线程保存副本,因此对所有线程都能提供最新的值。 - * 但试想,如果多个线程同时并发更新这个变量,其结果也是显而易见的,最后一次的更新会覆盖前面所有更新,从而导致线程不安全。 - * 该示例一次只有一个线程满足准入条件,因此不存在对变量的并发更新。 - * - * volatile(易失性)的值是最新的与线程安全完全是不相干的,所以不要误用volatile实现并发控制。 - * 相关博客 http://blog.csdn.net/yuechang5/article/details/79081697 - * @author kuangcp - */ -public class ShowWithVolatile implements Runnable{ - String target; - Holder holder; - int order; - - public ShowWithVolatile(String target, int order, Holder holder) { - this.holder = holder; - this.order = order; - this.target = target; - } - - @Override - public void run() { - while (true) { - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - if (holder.count % 5 == order) { - System.out.println(Thread.currentThread().getName() + " ===== "+ order); - holder.count ++; - } - } -// int i = 0; -// while(i ++ < 10000){ -// holder.count ++; -// } - } -} diff --git a/java-thread/src/main/java/com/github/kuangcp/order/Task.java b/java-thread/src/main/java/com/github/kuangcp/order/Task.java new file mode 100644 index 00000000..bed5ec45 --- /dev/null +++ b/java-thread/src/main/java/com/github/kuangcp/order/Task.java @@ -0,0 +1,46 @@ +package com.github.kuangcp.order; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import lombok.extern.slf4j.Slf4j; + +/** + * Created by https://github.com/kuangcp on 18-1-18 下午1:53 + * 利用原子递增控制线程准入顺序。 + * + * @author kuangcp + */ +@Slf4j +public class Task implements Runnable { + + private String target; + private int order; + private AtomicInteger count; //利用原子递增控制线程准入顺序。 + + public Task(String target, int order, AtomicInteger count) { + this.target = target; + this.order = order; + this.count = count; + } + + @Override + public void run() { + while (true) { + int count = this.count.get(); + if (count % 5 != order) { + continue; + } + + // 使用 log 会乱, 使用下面的逻辑也不能完全保障顺序 + this.count.incrementAndGet(); +// log.info("target={} order={} count={}", target, order, count); + System.out.println(target + " " + order + " " + count); + + try { + TimeUnit.SECONDS.sleep(1); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } +} diff --git a/java-thread/src/main/java/com/github/kuangcp/order/TaskWithVolatile.java b/java-thread/src/main/java/com/github/kuangcp/order/TaskWithVolatile.java new file mode 100644 index 00000000..44b9e62f --- /dev/null +++ b/java-thread/src/main/java/com/github/kuangcp/order/TaskWithVolatile.java @@ -0,0 +1,49 @@ +package com.github.kuangcp.order; + +import lombok.extern.slf4j.Slf4j; + +/** + * Created by https://github.com/kuangcp on 18-1-18 下午1:57 + * 使用了volatile关键字。让每个线程都能拿到最新的count的值,当其中一个线程执行++操作后, + * 其他两个线程就会拿到最新的值,并检查是否符合准入条件。 + * + * volatile不是线程安全的。而且两者没有任何关系。volatile变量不在用户线程保存副本,因此对所有线程都能提供最新的值。 + * 但试想,如果多个线程同时并发更新这个变量,其结果也是显而易见的,最后一次的更新会覆盖前面所有更新,从而导致线程不安全。 + * 该示例一次只有一个线程满足准入条件,因此不存在对变量的并发更新。 + * + * volatile(易失性)的值是最新的与线程安全完全是不相干的,所以不要误用volatile实现并发控制。 + * 相关博客 http://blog.csdn.net/yuechang5/article/details/79081697 + * + * @author kuangcp + */ +@Slf4j +public class TaskWithVolatile implements Runnable { + + private String target; + private static volatile int count = 0; + private int order; + + public TaskWithVolatile(String target, int order) { + this.order = order; + this.target = target; + } + + @Override + public void run() { + while (true) { + if (count % 5 != order) { + continue; + } + + log.info("target={} order={} count={}", target, order, count); +// System.out.println(target + " " + order + " " + count); + count += 1; + + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } +} diff --git a/java-thread/src/test/java/com/github/kuangcp/order/OrderedThreadTest.java b/java-thread/src/test/java/com/github/kuangcp/order/OrderedThreadTest.java new file mode 100644 index 00000000..d3903f2b --- /dev/null +++ b/java-thread/src/test/java/com/github/kuangcp/order/OrderedThreadTest.java @@ -0,0 +1,62 @@ +package com.github.kuangcp.order; + +import java.util.concurrent.atomic.AtomicInteger; +import org.junit.Test; + +/** + * Created by https://github.com/kuangcp on 18-1-18 下午1:54 + * 多线程有序的输出结果 参考博客 http://www.jb51.net/article/108762.htm + * 在Junit中, test方法来测试多线程, 因为一个Test注解的方法看做main方法的时候, 没有阻塞的,所以直接退出了 + * 里面写的开启的线程都是隶属于他的子线程, 所以也一起跟着关闭了. + * 思路: + * + * @author kuangcp + */ +public class OrderedThreadTest { + + private static AtomicInteger count = new AtomicInteger(0); + + @Test + public void testOrder() throws InterruptedException { + Thread thread1 = new Thread(new Task("A", 0, count)); + Thread thread2 = new Thread(new Task("B", 1, count)); + Thread thread3 = new Thread(new Task("C", 2, count)); + Thread thread4 = new Thread(new Task("D", 3, count)); + Thread thread5 = new Thread(new Task("E", 4, count)); + + thread1.start(); + thread2.start(); + thread3.start(); + thread4.start(); + thread5.start(); + + // 避免Junit主线程直接退出 + thread1.join(); + thread2.join(); + thread3.join(); + thread4.join(); + thread5.join(); + } + + @Test + public void testOrderWithVolatile() throws InterruptedException { + Thread thread1 = new Thread(new TaskWithVolatile("A", 0)); + Thread thread2 = new Thread(new TaskWithVolatile("B", 1)); + Thread thread3 = new Thread(new TaskWithVolatile("C", 2)); + Thread thread4 = new Thread(new TaskWithVolatile("D", 3)); + Thread thread5 = new Thread(new TaskWithVolatile("E", 4)); + + thread1.start(); + thread2.start(); + thread3.start(); + thread4.start(); + thread5.start(); + + // 避免Junit主线程直接退出 + thread1.join(); + thread2.join(); + thread3.join(); + thread4.join(); + thread5.join(); + } +} \ No newline at end of file diff --git a/java-thread/src/test/java/com/github/kuangcp/order/ShowTest.java b/java-thread/src/test/java/com/github/kuangcp/order/ShowTest.java deleted file mode 100644 index 6caeb6a4..00000000 --- a/java-thread/src/test/java/com/github/kuangcp/order/ShowTest.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.github.kuangcp.order; - -import org.junit.Test; - -import java.util.concurrent.atomic.AtomicInteger; - -/** - * Created by https://github.com/kuangcp on 18-1-18 下午1:54 - * 多线程有序的输出结果 参考博客 http://www.jb51.net/article/108762.htm - * 在Junit中, test方法来测试多线程, 因为一个Test注解的方法看做main方法的时候, 没有阻塞的,所以直接退出了 - * 里面写的开启的线程都是隶属于他的子线程, 所以也一起跟着关闭了. - * 思路: - * - * @author kuangcp - */ -public class ShowTest { - - static AtomicInteger count = new AtomicInteger(0); - - public static void main(String[]s) { - ShowTest test = new ShowTest(); - -// test.testOrder(); - test.testOrder2(); - } - @Test - public void testOrder() { - Thread thread1 = new Thread(new Show("A", 0, count)); - thread1.start(); -// thread1.join(); - Thread thread2 = new Thread(new Show("B", 1, count)); - thread2.start(); - Thread thread3 = new Thread(new Show("C", 2, count)); - thread3.start(); - Thread thread4 = new Thread(new Show("D", 3, count)); - thread4.start(); - Thread thread5 = new Thread(new Show("E", 4, count)); - thread5.start(); -// Scanner sc = new Scanner(System.in); -// sc.nextLine(); -// try { -// Thread.sleep(6000); -// } catch (InterruptedException e) { -// e.printStackTrace(); -// } - } - - static Holder holder = new Holder(); - @Test - public void testOrder2(){ - Thread thread1 = new Thread(new ShowWithVolatile("A", 0, holder)); - thread1.start(); - Thread thread2 = new Thread(new ShowWithVolatile("B", 1, holder)); - thread2.start(); - Thread thread3 = new Thread(new ShowWithVolatile("C", 2, holder)); - thread3.start(); - Thread thread4 = new Thread(new ShowWithVolatile("D", 3, holder)); - thread4.start(); - Thread thread5 = new Thread(new ShowWithVolatile("E", 4, holder)); - thread5.start(); - - - } -} \ No newline at end of file From a0d1c5a2e72026ce2c828d1d1375a58a0e969ee4 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Mon, 15 Apr 2019 21:01:08 +0800 Subject: [PATCH 006/476] -) decrease thread count --- .../src/main/java/com/github/kuangcp/order/Task.java | 2 +- .../com/github/kuangcp/order/TaskWithVolatile.java | 2 +- .../com/github/kuangcp/order/OrderedThreadTest.java | 12 ------------ 3 files changed, 2 insertions(+), 14 deletions(-) diff --git a/java-thread/src/main/java/com/github/kuangcp/order/Task.java b/java-thread/src/main/java/com/github/kuangcp/order/Task.java index bed5ec45..04acb306 100644 --- a/java-thread/src/main/java/com/github/kuangcp/order/Task.java +++ b/java-thread/src/main/java/com/github/kuangcp/order/Task.java @@ -27,7 +27,7 @@ public Task(String target, int order, AtomicInteger count) { public void run() { while (true) { int count = this.count.get(); - if (count % 5 != order) { + if (count % 3 != order) { continue; } diff --git a/java-thread/src/main/java/com/github/kuangcp/order/TaskWithVolatile.java b/java-thread/src/main/java/com/github/kuangcp/order/TaskWithVolatile.java index 44b9e62f..f8dcf0d6 100644 --- a/java-thread/src/main/java/com/github/kuangcp/order/TaskWithVolatile.java +++ b/java-thread/src/main/java/com/github/kuangcp/order/TaskWithVolatile.java @@ -31,7 +31,7 @@ public TaskWithVolatile(String target, int order) { @Override public void run() { while (true) { - if (count % 5 != order) { + if (count % 3 != order) { continue; } diff --git a/java-thread/src/test/java/com/github/kuangcp/order/OrderedThreadTest.java b/java-thread/src/test/java/com/github/kuangcp/order/OrderedThreadTest.java index d3903f2b..d9d90d32 100644 --- a/java-thread/src/test/java/com/github/kuangcp/order/OrderedThreadTest.java +++ b/java-thread/src/test/java/com/github/kuangcp/order/OrderedThreadTest.java @@ -21,21 +21,15 @@ public void testOrder() throws InterruptedException { Thread thread1 = new Thread(new Task("A", 0, count)); Thread thread2 = new Thread(new Task("B", 1, count)); Thread thread3 = new Thread(new Task("C", 2, count)); - Thread thread4 = new Thread(new Task("D", 3, count)); - Thread thread5 = new Thread(new Task("E", 4, count)); thread1.start(); thread2.start(); thread3.start(); - thread4.start(); - thread5.start(); // 避免Junit主线程直接退出 thread1.join(); thread2.join(); thread3.join(); - thread4.join(); - thread5.join(); } @Test @@ -43,20 +37,14 @@ public void testOrderWithVolatile() throws InterruptedException { Thread thread1 = new Thread(new TaskWithVolatile("A", 0)); Thread thread2 = new Thread(new TaskWithVolatile("B", 1)); Thread thread3 = new Thread(new TaskWithVolatile("C", 2)); - Thread thread4 = new Thread(new TaskWithVolatile("D", 3)); - Thread thread5 = new Thread(new TaskWithVolatile("E", 4)); thread1.start(); thread2.start(); thread3.start(); - thread4.start(); - thread5.start(); // 避免Junit主线程直接退出 thread1.join(); thread2.join(); thread3.join(); - thread4.join(); - thread5.join(); } } \ No newline at end of file From f76fd170a7975dca1a2848721950056853772b0f Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Thu, 18 Apr 2019 08:37:03 +0800 Subject: [PATCH 007/476] *) executor frame --- ....java => InstantiationAndConstructor.java} | 6 +-- .../github/kuangcp/instantiation/Readme.md | 3 +- ...a => InstantiationAndConstructorTest.java} | 20 +++++---- .../kuangcp/loader/ClassInitialTest.java | 4 +- .../kuangcp/loader/InstantiationSortTest.java | 43 +++++++++++++++++++ .../java/syntax/bit/BitOperatorsTest.java | 16 ++++--- .../test/java/syntax/string/StringTest.java | 3 ++ .../com/github/kuangcp/map/HashMapTest.java | 43 +++++++++++++++++++ .../com/github/kuangcp/UseThreadPool.java | 20 ++++++--- 9 files changed, 133 insertions(+), 25 deletions(-) rename java-class/src/main/java/com/github/kuangcp/instantiation/{TargetObject.java => InstantiationAndConstructor.java} (76%) rename java-class/src/test/java/com/github/kuangcp/instantiation/{TargetObjectTest.java => InstantiationAndConstructorTest.java} (69%) create mode 100644 java-class/src/test/java/com/github/kuangcp/loader/InstantiationSortTest.java create mode 100644 java-collection/src/test/java/com/github/kuangcp/map/HashMapTest.java diff --git a/java-class/src/main/java/com/github/kuangcp/instantiation/TargetObject.java b/java-class/src/main/java/com/github/kuangcp/instantiation/InstantiationAndConstructor.java similarity index 76% rename from java-class/src/main/java/com/github/kuangcp/instantiation/TargetObject.java rename to java-class/src/main/java/com/github/kuangcp/instantiation/InstantiationAndConstructor.java index f46ea409..06ebade4 100644 --- a/java-class/src/main/java/com/github/kuangcp/instantiation/TargetObject.java +++ b/java-class/src/main/java/com/github/kuangcp/instantiation/InstantiationAndConstructor.java @@ -9,15 +9,15 @@ */ @Data @Slf4j -public class TargetObject implements Serializable, Cloneable { +public class InstantiationAndConstructor implements Serializable, Cloneable { private String name; - public TargetObject() { + public InstantiationAndConstructor() { log.info("invoke empty constructor"); } - public TargetObject(String name) { + public InstantiationAndConstructor(String name) { this.name = name; log.info("invoke constructor: name={}", name); } diff --git a/java-class/src/main/java/com/github/kuangcp/instantiation/Readme.md b/java-class/src/main/java/com/github/kuangcp/instantiation/Readme.md index 9f4c3098..c66de09a 100644 --- a/java-class/src/main/java/com/github/kuangcp/instantiation/Readme.md +++ b/java-class/src/main/java/com/github/kuangcp/instantiation/Readme.md @@ -1,5 +1,4 @@ # instantiation -对象实例化的几种方式 [深入理解Java对象的创建过程:类的初始化与实例化](https://blog.csdn.net/justloveyou_/article/details/72466416) - +对象实例化的几种方式 且和构造器的关系 [深入理解Java对象的创建过程:类的初始化与实例化](https://blog.csdn.net/justloveyou_/article/details/72466416) diff --git a/java-class/src/test/java/com/github/kuangcp/instantiation/TargetObjectTest.java b/java-class/src/test/java/com/github/kuangcp/instantiation/InstantiationAndConstructorTest.java similarity index 69% rename from java-class/src/test/java/com/github/kuangcp/instantiation/TargetObjectTest.java rename to java-class/src/test/java/com/github/kuangcp/instantiation/InstantiationAndConstructorTest.java index b5547cea..21d043c3 100644 --- a/java-class/src/test/java/com/github/kuangcp/instantiation/TargetObjectTest.java +++ b/java-class/src/test/java/com/github/kuangcp/instantiation/InstantiationAndConstructorTest.java @@ -14,35 +14,37 @@ /** * @author kuangcp on 3/9/19-5:48 PM + * 实例化和构造器的关系 + * * clone 以及 serialize 创建对象时 不会调用构造器 */ @Slf4j -public class TargetObjectTest { +public class InstantiationAndConstructorTest { @Test public void testInitByNew() { - new TargetObject(); - new TargetObject("name"); + new InstantiationAndConstructor(); + new InstantiationAndConstructor("name"); } @Test public void testInitByNewInstance() throws IllegalAccessException, InstantiationException { - TargetObject.class.newInstance(); + InstantiationAndConstructor.class.newInstance(); } @Test public void testInitByReflect() throws ReflectiveOperationException { - Constructor constructor = TargetObject.class.getConstructor(String.class); + Constructor constructor = InstantiationAndConstructor.class.getConstructor(String.class); String name = "use reflect"; - TargetObject domain = constructor.newInstance(name); + InstantiationAndConstructor domain = constructor.newInstance(name); assertThat(domain.getName(), equalTo(name)); } @Test public void testInitByClone() throws CloneNotSupportedException { - TargetObject target = new TargetObject(); + InstantiationAndConstructor target = new InstantiationAndConstructor(); Object clone = target.clone(); assertThat(target, equalTo(clone)); @@ -52,7 +54,7 @@ public void testInitByClone() throws CloneNotSupportedException { @Test public void testInitByDeserialize() throws IOException, ClassNotFoundException { - TargetObject targetObject = new TargetObject("name"); + InstantiationAndConstructor targetObject = new InstantiationAndConstructor("name"); ByteArrayOutputStream byteOutput = new ByteArrayOutputStream(); ObjectOutputStream output = new ObjectOutputStream(byteOutput); @@ -61,7 +63,7 @@ public void testInitByDeserialize() throws IOException, ClassNotFoundException { ByteArrayInputStream byteInput = new ByteArrayInputStream(byteOutput.toByteArray()); ObjectInputStream input = new ObjectInputStream(byteInput); - TargetObject result = (TargetObject) input.readObject(); + InstantiationAndConstructor result = (InstantiationAndConstructor) input.readObject(); assertThat(result.getName(), equalTo("name")); } } diff --git a/java-class/src/test/java/com/github/kuangcp/loader/ClassInitialTest.java b/java-class/src/test/java/com/github/kuangcp/loader/ClassInitialTest.java index a9bfec58..d9848644 100644 --- a/java-class/src/test/java/com/github/kuangcp/loader/ClassInitialTest.java +++ b/java-class/src/test/java/com/github/kuangcp/loader/ClassInitialTest.java @@ -1,6 +1,7 @@ package com.github.kuangcp.loader; import lombok.extern.slf4j.Slf4j; +import org.junit.Test; /** * @author kuangcp on 2019-04-15 12:08 AM @@ -8,7 +9,8 @@ @Slf4j public class ClassInitialTest { - public static void main(String[] args) throws ClassNotFoundException { + @Test + public void testLoad1() throws ClassNotFoundException { String path = "com.github.kuangcp.loader.InitialTest"; Class clazz = InitialTest.class; diff --git a/java-class/src/test/java/com/github/kuangcp/loader/InstantiationSortTest.java b/java-class/src/test/java/com/github/kuangcp/loader/InstantiationSortTest.java new file mode 100644 index 00000000..59fdf550 --- /dev/null +++ b/java-class/src/test/java/com/github/kuangcp/loader/InstantiationSortTest.java @@ -0,0 +1,43 @@ +package com.github.kuangcp.loader; + +import lombok.extern.slf4j.Slf4j; + +/** + * @author kuangcp on 2019-04-17 5:16 PM + * 类属性和对象属性实例化的顺序 + * + * 17:21:08.112 INFO [main] c.g.k.i.InstantiationSortTest:21 object init block + * 17:21:08.117 INFO [main] c.g.k.i.InstantiationSortTest:25 constructor + * 17:21:08.118 INFO [main] c.g.k.i.InstantiationSortTest:26 a=1 b=2 + * 17:21:08.120 INFO [main] c.g.k.i.InstantiationSortTest:17 static init block + * 17:21:08.120 INFO [main] c.g.k.i.InstantiationSortTest:30 static method + */ +@Slf4j +public class InstantiationSortTest { + + static int b = 2; + int a = 1; + + static InstantiationSortTest test = new InstantiationSortTest(); + + static { + log.info("static init block"); + } + + { + log.info("object init block"); + } + + InstantiationSortTest() { + log.info("constructor"); + log.info("a={} b={}", a, b); + } + + static void staticMethod() { + log.info("static method"); + } + + public static void main(String[] args) { + staticMethod(); + } +} diff --git a/java-class/src/test/java/syntax/bit/BitOperatorsTest.java b/java-class/src/test/java/syntax/bit/BitOperatorsTest.java index 54b3890f..b7de3017 100644 --- a/java-class/src/test/java/syntax/bit/BitOperatorsTest.java +++ b/java-class/src/test/java/syntax/bit/BitOperatorsTest.java @@ -20,14 +20,20 @@ public class BitOperatorsTest { @Test - public void testSimple() { + public void testSimpleBitOperator() { // 与 : 1 1 -> 1 否则 0 + // 1010 + // 1001 + // 1000 assertThat(0b1010 & 0b1001, equalTo(0b1000)); // 或 : 0 0 -> 0 否则 1 assertThat(0b101 | 0b001, equalTo(0b101)); // 异或: 不同 -> 1 否则 0 + // 101 + // 001 + // 100 assertThat(0b101 ^ 0b001, equalTo(0b100)); // 非 @@ -84,16 +90,16 @@ public void testRight() { public void testLeft() { assertThat(0b11_0110 << 2, equalTo(0b1101_1000)); - assertThat(0b1<<31, equalTo(0b1000_0000_0000_0000_0000_0000_0000_0000)); + assertThat(0b1 << 31, equalTo(0b1000_0000_0000_0000_0000_0000_0000_0000)); show(0b1000_0000_0000_0000_0000_0000_0000_0000); // 溢出了32位... - assertThat(0b1<<35, equalTo(0b1000)); - show(0b1<<35); + assertThat(0b1 << 35, equalTo(0b1000)); + show(0b1 << 35); } private static void show(int result) { - log.info("{} {}", Integer.toBinaryString(result), result); + log.info("{} {}", String.format("%32s", Integer.toBinaryString(result)), result); } @Test diff --git a/java-class/src/test/java/syntax/string/StringTest.java b/java-class/src/test/java/syntax/string/StringTest.java index 2c51ae24..f91c722e 100644 --- a/java-class/src/test/java/syntax/string/StringTest.java +++ b/java-class/src/test/java/syntax/string/StringTest.java @@ -51,7 +51,9 @@ public void testUUID() { @Test public void testConstantPool() { assert new String("1") != new String("1"); + assert "1" != new String("1"); + assert "11" != new String("1") + new String("1"); // 常量池 @@ -59,6 +61,7 @@ public void testConstantPool() { // 左边在常量池 右边在堆, 即使 HashCode 一致 assert "1" + "1" != new String("1") + new String("1"); + assert ("1" + "1").hashCode() == (new String("1") + new String("1")).hashCode(); // 以下三种 右边表达式的结果都是等价的, 都在堆创建了一个新的对象 diff --git a/java-collection/src/test/java/com/github/kuangcp/map/HashMapTest.java b/java-collection/src/test/java/com/github/kuangcp/map/HashMapTest.java new file mode 100644 index 00000000..98aaed59 --- /dev/null +++ b/java-collection/src/test/java/com/github/kuangcp/map/HashMapTest.java @@ -0,0 +1,43 @@ +package com.github.kuangcp.map; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.IsEqual.equalTo; + +import java.util.HashMap; +import java.util.UUID; +import org.junit.Test; + +/** + * @author kuangcp on 2019-04-16 10:22 AM + */ +public class HashMapTest { + + @Test + public void testPut() { + HashMap map = new HashMap<>(3); // 初始大小4 扩容阈值为 3 大于才扩容 + for (int i = 0; i < 100; i++) { + String key = i + " " + UUID.randomUUID().toString(); + map.put(key, " "); + int i1 = key.hashCode(); + map.size(); + } + } + + static int hash(Object key) { + int h; + return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); + } + + @Test + public void testR() { + int value = 0b101010; + // 101010 + // 000101 + // 101111 + assertThat(value ^ (value >>> 3), equalTo(0b101111)); + + assertThat( 0b10111010111101110101001100000011 + ^ 0b00000000000000001011101011110111, + equalTo(0b10111010111101111110100111110100)); + } +} diff --git a/java-thread/src/main/java/com/github/kuangcp/UseThreadPool.java b/java-thread/src/main/java/com/github/kuangcp/UseThreadPool.java index 5ead7918..9c7097e9 100644 --- a/java-thread/src/main/java/com/github/kuangcp/UseThreadPool.java +++ b/java-thread/src/main/java/com/github/kuangcp/UseThreadPool.java @@ -13,12 +13,22 @@ public class UseThreadPool { private void baseType() { - ExecutorService a = Executors.newCachedThreadPool(); // 创建有缓存功能的线程池 - ExecutorService b = Executors.newFixedThreadPool(1); // 创建具有固定大小的线程池 - ExecutorService c = Executors.newSingleThreadExecutor();// 创建单线程的线程池 + // 创建有缓存功能的线程池 + ExecutorService a = Executors.newCachedThreadPool(); - ScheduledExecutorService d = Executors.newScheduledThreadPool(1); // 创建固定大小的线程池,指定延迟 - ScheduledExecutorService e = Executors.newSingleThreadScheduledExecutor();// 创建单线程的线程池,指定延迟 + // 创建具有固定大小的线程池 + ExecutorService b = Executors.newFixedThreadPool(1); + + // 创建单线程的线程池 + ExecutorService c = Executors.newSingleThreadExecutor(); + + // 创建具有定时功能的线程池 指定基本线程池数量, 该线程池的队列是无限队列 + ScheduledExecutorService d = Executors.newScheduledThreadPool(1); + + /////////////// + + // 创建单线程的线程池,指定延迟 + ScheduledExecutorService e = Executors.newSingleThreadScheduledExecutor(); // a.submit(); 提交任务 // a.execute(); 执行任务 From 93978fb8c471413982bfdee747389327e371b794 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Fri, 19 Apr 2019 00:50:48 +0800 Subject: [PATCH 008/476] *) change groovy to java --- README.md | 2 + .../src/test/java/syntax/longs/LongTest.java | 30 +++++ java-concurrency/Readme.md | 2 + java-concurrency/build.gradle | 1 - .../kuangcp/countlatch/CountLatchDemo.java | 1 + .../kuangcp/queue/ShowBlockingQueue.groovy | 109 ------------------ .../queue/use/blocking/Appointment.java | 20 ++++ .../kuangcp/queue/use/blocking/Cat.java | 22 ++++ .../kuangcp/queue/use/blocking/Dog.java | 19 +++ .../kuangcp/queue/use/blocking/Pet.java | 19 +++ .../queue/use/blocking/Veterinarian.java | 52 +++++++++ .../kuangcp/schedule/AddSchedule.groovy | 7 -- .../kuangcp/schedule/CreateModel.groovy | 48 ++++---- .../github/kuangcp/schedule/ShowSTPE.groovy | 85 +++++++------- .../queue/use/blocking/VeterinarianTest.java | 41 +++++++ .../src/test/resources/logback.xml | 83 +++++++++++++ 16 files changed, 357 insertions(+), 184 deletions(-) create mode 100644 java-class/src/test/java/syntax/longs/LongTest.java create mode 100644 java-concurrency/Readme.md delete mode 100644 java-concurrency/src/main/java/com/github/kuangcp/queue/ShowBlockingQueue.groovy create mode 100644 java-concurrency/src/main/java/com/github/kuangcp/queue/use/blocking/Appointment.java create mode 100644 java-concurrency/src/main/java/com/github/kuangcp/queue/use/blocking/Cat.java create mode 100644 java-concurrency/src/main/java/com/github/kuangcp/queue/use/blocking/Dog.java create mode 100644 java-concurrency/src/main/java/com/github/kuangcp/queue/use/blocking/Pet.java create mode 100644 java-concurrency/src/main/java/com/github/kuangcp/queue/use/blocking/Veterinarian.java delete mode 100644 java-concurrency/src/main/java/com/github/kuangcp/schedule/AddSchedule.groovy create mode 100644 java-concurrency/src/test/java/com/github/kuangcp/queue/use/blocking/VeterinarianTest.java create mode 100644 java-concurrency/src/test/resources/logback.xml diff --git a/README.md b/README.md index 9a58ee20..00a92dd7 100644 --- a/README.md +++ b/README.md @@ -25,3 +25,5 @@ # 同类仓库 > [tutorials](https://github.com/eugenp/tutorials) +> [Java 学习笔记](https://github.com/brianway/java-learning) +> [demo](https://gitee.com/code4everything/demo) diff --git a/java-class/src/test/java/syntax/longs/LongTest.java b/java-class/src/test/java/syntax/longs/LongTest.java new file mode 100644 index 00000000..96ffbe9d --- /dev/null +++ b/java-class/src/test/java/syntax/longs/LongTest.java @@ -0,0 +1,30 @@ +package syntax.longs; + +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; + +/** + * @author kuangcp on 2019-04-18 11:13 PM + */ +@Slf4j +public class LongTest { + + // 原因很简单, 前三个编译后实际上是一样的代码, 而这个转换都是走了Long的缓存 LongCache + // Long a = 3L; + // Long b = 3L; + // Long c = 3L; + @Test + public void testCache() { + Long a = (long) 3; + Long b = 3L; + Long c = Long.valueOf(3); + Long d = new Long(3); + + assert a == b; + assert a == c; + assert a != d; + assert b == c; + assert b != d; + assert c != d; + } +} diff --git a/java-concurrency/Readme.md b/java-concurrency/Readme.md new file mode 100644 index 00000000..daadb764 --- /dev/null +++ b/java-concurrency/Readme.md @@ -0,0 +1,2 @@ +# Java中的并发 +> [笔记](https://github.com/Kuangcp/Memo/blob/master/Java/AdvancedLearning/Concurrency.md) diff --git a/java-concurrency/build.gradle b/java-concurrency/build.gradle index 8399d660..5f4176ae 100644 --- a/java-concurrency/build.gradle +++ b/java-concurrency/build.gradle @@ -1,7 +1,6 @@ plugins { id 'groovy' id 'maven' - id 'java' } dependencies { diff --git a/java-concurrency/src/main/java/com/github/kuangcp/countlatch/CountLatchDemo.java b/java-concurrency/src/main/java/com/github/kuangcp/countlatch/CountLatchDemo.java index 65bc5a46..8fedeba8 100644 --- a/java-concurrency/src/main/java/com/github/kuangcp/countlatch/CountLatchDemo.java +++ b/java-concurrency/src/main/java/com/github/kuangcp/countlatch/CountLatchDemo.java @@ -26,6 +26,7 @@ public static void main(String[] s) { ProcessingThread local = new ProcessingThread("localhost" + (9000 + i), cdl); nodes.add(local); local.start(); + log.info("a: i={}", i); } cdl.await(); //达到quorum 开始发送更新 } catch (InterruptedException e) { diff --git a/java-concurrency/src/main/java/com/github/kuangcp/queue/ShowBlockingQueue.groovy b/java-concurrency/src/main/java/com/github/kuangcp/queue/ShowBlockingQueue.groovy deleted file mode 100644 index 5cb5c470..00000000 --- a/java-concurrency/src/main/java/com/github/kuangcp/queue/ShowBlockingQueue.groovy +++ /dev/null @@ -1,109 +0,0 @@ -package com.github.kuangcp.queue - -import java.util.concurrent.BlockingQueue -import java.util.concurrent.LinkedBlockingQueue - -/** - * Created by https://github.com/kuangcp on 17-8-18 上午9:28 - * 展示BlockingQueue的特性 多个兽医治疗宠物的队列 - */ -// 因为父类没有无参构造器,所以子类也没有,子类也要显式声明 -// 宠物类 -abstract class Pet{ - protected String name; - public Pet(String name){ - this.name = name; - } - public abstract void examine(); -} - -class Cat extends Pet{ - public Cat(String name){ - super(name); - } - - @Override - void examine() { - println("cat :"+name) //+" addr:"+this - } -} -class Dog extends Pet{ - - Dog(String name) { - super(name) - } - - @Override - void examine() { - println("dog : "+name) - } -} -// 接待类,所有的宠物实例先达到这里再给兽医 -class Appointmnet{ - private final T toBeSeen; - public T getPatient(){ -// println("接待: "+toBeSeen.toString()) - return toBeSeen; - } - public Appointmnet(T incoming){ - toBeSeen = incoming; - } -} - -class Veterinarian extends Thread{ - protected final BlockingQueue> appts; - protected String text = ""; - protected final int restTime; // 预约之间的休息时间 - private boolean shutdown = false; - public Veterinarian(BlockingQueue> lists, int pause){ - appts = lists; - restTime = pause; - } - public synchronized void shutdown(){ - shutdown = true; - } - - @Override - void run() { - while (!shutdown){ - seePatient(); - try{ - sleep(restTime); - }catch (InterruptedException ignored){ - shutdown = true; - } - } - } - /** - * 线程从队列中取出预约, 进行操作,如果当前队列没有预约, 就会阻塞 - */ - public void seePatient(){ - try{ - Appointmnet ap = appts.take(); // 阻塞take - Pet patient = ap.getPatient(); - println(text+" take "+patient.name) - patient.examine(); - }catch(InterruptedException ignored){ - shutdown = true; - } - } -} -BlockingQueue> lists = new LinkedBlockingQueue>() -app = new Appointmnet(new Cat("name")) -lists.add(app) -//app.patient.name = "update" -//lists.add(app) -lists.add(new Appointmnet(new Cat("name2"))) - - -veter = new Veterinarian(lists, 2000); -veter.text = "1" -veter.start() -// 链表这种对象,实参形参内存共享,所以可以启动后添加 -lists.add(new Appointmnet(new Dog("add"))) -lists.add(new Appointmnet(new Dog("dog2"))) -// 队列为空就阻塞了 - -veter2 = new Veterinarian(lists, 1000); -veter2.text = "2" -veter2.start() diff --git a/java-concurrency/src/main/java/com/github/kuangcp/queue/use/blocking/Appointment.java b/java-concurrency/src/main/java/com/github/kuangcp/queue/use/blocking/Appointment.java new file mode 100644 index 00000000..7440b494 --- /dev/null +++ b/java-concurrency/src/main/java/com/github/kuangcp/queue/use/blocking/Appointment.java @@ -0,0 +1,20 @@ +package com.github.kuangcp.queue.use.blocking; + +import lombok.Data; +import lombok.extern.slf4j.Slf4j; + +@Data +@Slf4j +class Appointment { + + private final T toBeSeen; + + T getPatient() { + log.info("handle " + toBeSeen.toString()); + return toBeSeen; + } + + Appointment(T incoming) { + toBeSeen = incoming; + } +} \ No newline at end of file diff --git a/java-concurrency/src/main/java/com/github/kuangcp/queue/use/blocking/Cat.java b/java-concurrency/src/main/java/com/github/kuangcp/queue/use/blocking/Cat.java new file mode 100644 index 00000000..2ec99640 --- /dev/null +++ b/java-concurrency/src/main/java/com/github/kuangcp/queue/use/blocking/Cat.java @@ -0,0 +1,22 @@ +package com.github.kuangcp.queue.use.blocking; + +import lombok.ToString; +import lombok.extern.slf4j.Slf4j; + + +@Slf4j +@ToString +class Cat extends Pet { + + + Cat(String name) { + super(name); + } + + @Override + void examine() { + log.info("cat: " + name); + } + + +} \ No newline at end of file diff --git a/java-concurrency/src/main/java/com/github/kuangcp/queue/use/blocking/Dog.java b/java-concurrency/src/main/java/com/github/kuangcp/queue/use/blocking/Dog.java new file mode 100644 index 00000000..e386dee7 --- /dev/null +++ b/java-concurrency/src/main/java/com/github/kuangcp/queue/use/blocking/Dog.java @@ -0,0 +1,19 @@ +package com.github.kuangcp.queue.use.blocking; + +import lombok.ToString; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@ToString +class Dog extends Pet { + + + Dog(String name) { + super(name); + } + + @Override + void examine() { + log.info("dog : " + name); + } +} \ No newline at end of file diff --git a/java-concurrency/src/main/java/com/github/kuangcp/queue/use/blocking/Pet.java b/java-concurrency/src/main/java/com/github/kuangcp/queue/use/blocking/Pet.java new file mode 100644 index 00000000..17c31b9c --- /dev/null +++ b/java-concurrency/src/main/java/com/github/kuangcp/queue/use/blocking/Pet.java @@ -0,0 +1,19 @@ +package com.github.kuangcp.queue.use.blocking; + +import lombok.Data; + +/** + * Created by https://github.com/kuangcp on 17-8-18 上午9:28 + * 展示BlockingQueue的特性 多个兽医治疗宠物的队列 + */ +@Data +abstract class Pet { + + protected String name; + + Pet(String name) { + this.name = name; + } + + abstract void examine(); +} \ No newline at end of file diff --git a/java-concurrency/src/main/java/com/github/kuangcp/queue/use/blocking/Veterinarian.java b/java-concurrency/src/main/java/com/github/kuangcp/queue/use/blocking/Veterinarian.java new file mode 100644 index 00000000..1fc029c7 --- /dev/null +++ b/java-concurrency/src/main/java/com/github/kuangcp/queue/use/blocking/Veterinarian.java @@ -0,0 +1,52 @@ +package com.github.kuangcp.queue.use.blocking; + +import java.util.concurrent.BlockingQueue; +import lombok.extern.slf4j.Slf4j; + +// 因为父类没有无参构造器,所以子类也没有,子类也要显式声明 宠物类 +// 接待类,所有的宠物实例先达到这里再给兽医 +@Slf4j +public class Veterinarian extends Thread { + + protected final BlockingQueue> pets; + protected String text = ""; + protected final int restTime; // 预约之间的休息时间 + private boolean shutdown = false; + + Veterinarian(BlockingQueue> pets, int pause) { + this.pets = pets; + restTime = pause; + } + + synchronized void shutdown() { + shutdown = true; + } + + @Override + public void run() { + while (!shutdown) { + seePatient(); + try { + sleep(restTime); + } catch (InterruptedException ignored) { + shutdown = true; + } + } + } + + /** + * 线程从队列中取出预约, 进行操作,如果当前队列没有预约, 就会阻塞 + */ + void seePatient() { + try { + Appointment ap = pets.take(); // 阻塞take + Pet patient = ap.getPatient(); + log.info(text + " take " + patient.name); + patient.examine(); + } catch (InterruptedException ignored) { + shutdown = true; + } + } + +} + diff --git a/java-concurrency/src/main/java/com/github/kuangcp/schedule/AddSchedule.groovy b/java-concurrency/src/main/java/com/github/kuangcp/schedule/AddSchedule.groovy deleted file mode 100644 index 60716e83..00000000 --- a/java-concurrency/src/main/java/com/github/kuangcp/schedule/AddSchedule.groovy +++ /dev/null @@ -1,7 +0,0 @@ -package com.github.kuangcp.schedule - -/** - * Created by https://github.com/kuangcp on 17-8-18 下午9:10 - * - */ - diff --git a/java-concurrency/src/main/java/com/github/kuangcp/schedule/CreateModel.groovy b/java-concurrency/src/main/java/com/github/kuangcp/schedule/CreateModel.groovy index cc55b6b0..0eeb3678 100644 --- a/java-concurrency/src/main/java/com/github/kuangcp/schedule/CreateModel.groovy +++ b/java-concurrency/src/main/java/com/github/kuangcp/schedule/CreateModel.groovy @@ -1,44 +1,44 @@ package com.github.kuangcp.schedule -import java.util.concurrent.Callable -import java.util.concurrent.Future -import java.util.concurrent.TimeUnit -import java.util.concurrent.TimeoutException +import java.util.concurrent.* /** * Created by https://github.com/kuangcp on 17-8-18 下午3:59 * 任务建模的三种方法 Callable Future FutureTask - * Callable 是SAM类型(单一抽象方法) 的示例, 这是Java把函数作为一等类型的可行方法 + * Callable 是SAM类型(Single Abstract Method) 的示例, 这是Java把函数作为一等类型的可行方法 * Future接口 用来表示异步任务, * - get() 用来获取结果,如果结果还没准备好就会阻塞直到它能去到结果,有一个可以设置超时的版本,这个版本永远不会阻塞 * - cancel() 运算结束前取消 * - isDone() 调用者用它来判断运算是否结束 */ -////////////////// + list = new ArrayList<>() list.add(12) list.add(134) -Callable cb = new Callable() { - @Override - String call() throws Exception { - return list.toString() - } +Callable callable = new Callable() { + + @Override + String call() throws Exception { + return list.toString() + } } -println(cb.call()) +println("result = " + callable.call()) -////////////////////////////// -// 这里需要封装一个方法得到该对象,这个方法是处于多线程的环境下 -Future fut = null +def executor +try { + executor = Executors.newSingleThreadExecutor() + Future future = executor.submit(callable) + + def result = future.get(60, TimeUnit.MILLISECONDS) + println("future: " + result) +} catch (TimeoutException e) { + println("timeout" + e) +} finally { + if (executor != null) { + executor.shutdown() + } +} -Long result = null -while (result != null){ - try{ - result = fut.get(60, TimeUnit.MILLISECONDS) - }catch(TimeoutException e){ - } - println("Still not found the billionth prime!") -} -println("Found it : "+result.longValue()) diff --git a/java-concurrency/src/main/java/com/github/kuangcp/schedule/ShowSTPE.groovy b/java-concurrency/src/main/java/com/github/kuangcp/schedule/ShowSTPE.groovy index f7e38e55..57ec0298 100644 --- a/java-concurrency/src/main/java/com/github/kuangcp/schedule/ShowSTPE.groovy +++ b/java-concurrency/src/main/java/com/github/kuangcp/schedule/ShowSTPE.groovy @@ -1,11 +1,6 @@ package com.github.kuangcp.schedule -import java.util.concurrent.BlockingQueue -import java.util.concurrent.Executors -import java.util.concurrent.LinkedBlockingQueue -import java.util.concurrent.ScheduledExecutorService -import java.util.concurrent.ScheduledFuture -import java.util.concurrent.TimeUnit +import java.util.concurrent.* /** * Created by https://github.com/kuangcp on 17-8-18 下午5:09 @@ -14,49 +9,53 @@ import java.util.concurrent.TimeUnit * 若执行cancel方法就会进入阻塞状态不会运行工作单元,直到又进行调用执行方法 * 若得到工作单元,则线程就会执行工作单元 */ -class ReadService{ - // 消息队列 - BlockingQueue lbp = new LinkedBlockingQueue<>() - // 一个延迟的、结果可接受的操作,可将其取消。通常已安排的 future 是用 ScheduledExecutorService 安排任务的结果。 - ScheduledFuture hndl - ScheduledExecutorService stpe = Executors.newScheduledThreadPool(2) +class ReadService { + // 消息队列 + BlockingQueue lbp = new LinkedBlockingQueue<>() + // 一个延迟的、结果可接受的操作,可将其取消。通常已安排的 future 是用 ScheduledExecutorService 安排任务的结果。 + ScheduledFuture hndl + ScheduledExecutorService stpe = Executors.newScheduledThreadPool(2) - // 初始化一些信息,同样的调用了这个 线程池,执行了添加信息的工作单元 - void init(){ - lbp.add("初始化") - lbp.add("1") - lbp.add("2") - lbp.add("3") - final Runnable Reader = new Runnable() { - @Override - void run() { - lbp.add(System.currentTimeMillis()+"") - } - } - hndl = stpe.scheduleAtFixedRate(Reader, 10, 1000, TimeUnit.MILLISECONDS) + // 初始化一些信息,同样的调用了这个 线程池,执行了添加信息的工作单元 + void init() { + lbp.add("初始化") + lbp.add("1") + lbp.add("2") + lbp.add("3") + final Runnable Reader = new Runnable() { + + @Override + void run() { + lbp.add(System.currentTimeMillis() + "") + } } - void run(){ - final Runnable Reader = new Runnable() { - @Override - void run() { - String msg = lbp.poll().toString() - if (msg != null){ - println("Receive msg:"+msg) - } + hndl = stpe.scheduleAtFixedRate(Reader, 10, 1000, TimeUnit.MILLISECONDS) + } + + void run() { + final Runnable Reader = new Runnable() { - } + @Override + void run() { + String msg = lbp.poll().toString() + if (msg != null) { + println("Receive msg:" + msg) } - hndl = stpe.scheduleAtFixedRate(Reader, 10, 500, TimeUnit.MILLISECONDS) - } - void cancel(){ - final ScheduledFuture handle = hndl - stpe.schedule(new Runnable(){ - public void run(){ - handle.cancel(true) - } - }, 10, TimeUnit.MILLISECONDS) + } } + hndl = stpe.scheduleAtFixedRate(Reader, 10, 500, TimeUnit.MILLISECONDS) + } + + void cancel() { + final ScheduledFuture handle = hndl + stpe.schedule(new Runnable() { + + public void run() { + handle.cancel(true) + } + }, 10, TimeUnit.MILLISECONDS) + } } // run 和 cancel 是有顺序的,因为ScheduledFuture 对象共享覆盖的原因 diff --git a/java-concurrency/src/test/java/com/github/kuangcp/queue/use/blocking/VeterinarianTest.java b/java-concurrency/src/test/java/com/github/kuangcp/queue/use/blocking/VeterinarianTest.java new file mode 100644 index 00000000..db9d3557 --- /dev/null +++ b/java-concurrency/src/test/java/com/github/kuangcp/queue/use/blocking/VeterinarianTest.java @@ -0,0 +1,41 @@ +package com.github.kuangcp.queue.use.blocking; + +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; + +/** + * @author kuangcp on 2019-04-19 12:41 AM + */ +@Slf4j +public class VeterinarianTest { + + @Test + public void testRun() throws Exception { + BlockingQueue> lists = new LinkedBlockingQueue<>(); + Appointment app = new Appointment<>(new Cat("name")); + lists.add(app); + app.getPatient().setName("who"); + + lists.add(app); + lists.add(new Appointment(new Cat("name2"))); + + Veterinarian veterinarian = new Veterinarian(lists, 2000); + veterinarian.text = "1"; + veterinarian.start(); +// 链表这种对象,实参形参内存共享,所以可以启动后添加 + lists.add(new Appointment(new Dog("add"))); + lists.add(new Appointment(new Dog("dog2"))); +// 队列为空就阻塞了 + log.info("{}", veterinarian); + + veterinarian = new Veterinarian(lists, 1000); + veterinarian.text = "2"; + veterinarian.start(); + + log.info("{}", veterinarian); + } + + +} \ No newline at end of file diff --git a/java-concurrency/src/test/resources/logback.xml b/java-concurrency/src/test/resources/logback.xml new file mode 100644 index 00000000..c32ae9fe --- /dev/null +++ b/java-concurrency/src/test/resources/logback.xml @@ -0,0 +1,83 @@ + + + + + + + + %d{HH:mm:ss.SSS} %highlight(%-5level) [%thread] %logger{36}:%yellow(%-3L) %msg%n + + + + DEBUG + + + + + + ${log.base}debug.log + true + + ${log.base}debug.%d{yyyy-MM-dd}.log + + + %d{MM-dd HH:mm:ss.SSS} |-%-5level |%thread| %logger{36}:%L - %msg%n + + + DEBUG + + + + + + ${log.base}warn.log + true + + ${log.base}warn.%d{yyyy-MM-dd}.log + + + %d{MM-dd HH:mm:ss.SSS} |-%-5level %logger{36}:%L - %msg%n + + + WARN + + + + + + ${log.base}info.log + true + + ${log.base}info.%d{yyyy-MM-dd}.log + + + %d{MM-dd HH:mm:ss.SSS} |-%-5level %logger{36}:%L - %msg%n + + + INFO + + + + + + ${log.base}error.log + true + + ${log.base}error.%d{yyyy-MM-dd}.log + + + %d{MM-dd HH:mm:ss.SSS} | %thread |-%-5level %logger{36}:%L - %msg%n + + + ERROR + + + + + + + + + + + \ No newline at end of file From b0fef46606c6887aaee30a33ab6983468e08d9ce Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Fri, 19 Apr 2019 23:05:19 +0800 Subject: [PATCH 009/476] *) learn nio --- .../queue/use/blocking/Appointment.java | 2 +- .../kuangcp/queue/use/blocking/Cat.java | 17 ++-- .../kuangcp/queue/use/blocking/Dog.java | 13 +-- .../kuangcp/queue/use/blocking/Pet.java | 14 +--- .../queue/use/blocking/Veterinarian.java | 29 ++++--- .../queue/use/blocking/VeterinarianTest.java | 28 +++---- .../kuangcp/nio/{ => selector}/NioClient.java | 2 +- .../kuangcp/nio/{ => selector}/NioServer.java | 10 ++- .../com/github/kuangcp/HowToCreateThread.java | 16 +++- .../ShowCreateThreadForSimpleMain.java | 29 +++++++ .../com/github/kuangcp/ShowInterrupted.java | 55 +++++++++++++ java-thread/src/main/resources/logback.xml | 82 +++++++++++++++++++ 12 files changed, 236 insertions(+), 61 deletions(-) rename java-network/src/main/java/com/github/kuangcp/nio/{ => selector}/NioClient.java (98%) rename java-network/src/main/java/com/github/kuangcp/nio/{ => selector}/NioServer.java (98%) create mode 100644 java-thread/src/main/java/com/github/kuangcp/ShowCreateThreadForSimpleMain.java create mode 100644 java-thread/src/main/java/com/github/kuangcp/ShowInterrupted.java create mode 100644 java-thread/src/main/resources/logback.xml diff --git a/java-concurrency/src/main/java/com/github/kuangcp/queue/use/blocking/Appointment.java b/java-concurrency/src/main/java/com/github/kuangcp/queue/use/blocking/Appointment.java index 7440b494..9bbc749e 100644 --- a/java-concurrency/src/main/java/com/github/kuangcp/queue/use/blocking/Appointment.java +++ b/java-concurrency/src/main/java/com/github/kuangcp/queue/use/blocking/Appointment.java @@ -5,7 +5,7 @@ @Data @Slf4j -class Appointment { +class Appointment { private final T toBeSeen; diff --git a/java-concurrency/src/main/java/com/github/kuangcp/queue/use/blocking/Cat.java b/java-concurrency/src/main/java/com/github/kuangcp/queue/use/blocking/Cat.java index 2ec99640..ec9fdbf1 100644 --- a/java-concurrency/src/main/java/com/github/kuangcp/queue/use/blocking/Cat.java +++ b/java-concurrency/src/main/java/com/github/kuangcp/queue/use/blocking/Cat.java @@ -1,22 +1,23 @@ package com.github.kuangcp.queue.use.blocking; -import lombok.ToString; +import lombok.Data; +import lombok.EqualsAndHashCode; import lombok.extern.slf4j.Slf4j; +@Data @Slf4j -@ToString -class Cat extends Pet { +@EqualsAndHashCode(callSuper = false) +class Cat implements Pet { + private String name; Cat(String name) { - super(name); + this.name = name; } @Override - void examine() { - log.info("cat: " + name); + public void examine() { + log.info("examine cat: " + name); } - - } \ No newline at end of file diff --git a/java-concurrency/src/main/java/com/github/kuangcp/queue/use/blocking/Dog.java b/java-concurrency/src/main/java/com/github/kuangcp/queue/use/blocking/Dog.java index e386dee7..0b48064f 100644 --- a/java-concurrency/src/main/java/com/github/kuangcp/queue/use/blocking/Dog.java +++ b/java-concurrency/src/main/java/com/github/kuangcp/queue/use/blocking/Dog.java @@ -1,19 +1,20 @@ package com.github.kuangcp.queue.use.blocking; -import lombok.ToString; +import lombok.Data; import lombok.extern.slf4j.Slf4j; +@Data @Slf4j -@ToString -class Dog extends Pet { +class Dog implements Pet { + private String name; Dog(String name) { - super(name); + this.name = name; } @Override - void examine() { - log.info("dog : " + name); + public void examine() { + log.info("examine dog : " + name); } } \ No newline at end of file diff --git a/java-concurrency/src/main/java/com/github/kuangcp/queue/use/blocking/Pet.java b/java-concurrency/src/main/java/com/github/kuangcp/queue/use/blocking/Pet.java index 17c31b9c..a384b5d5 100644 --- a/java-concurrency/src/main/java/com/github/kuangcp/queue/use/blocking/Pet.java +++ b/java-concurrency/src/main/java/com/github/kuangcp/queue/use/blocking/Pet.java @@ -1,19 +1,13 @@ package com.github.kuangcp.queue.use.blocking; -import lombok.Data; - /** * Created by https://github.com/kuangcp on 17-8-18 上午9:28 - * 展示BlockingQueue的特性 多个兽医治疗宠物的队列 */ -@Data -abstract class Pet { +interface Pet { - protected String name; + String getName(); - Pet(String name) { - this.name = name; - } + void setName(String name); - abstract void examine(); + void examine(); } \ No newline at end of file diff --git a/java-concurrency/src/main/java/com/github/kuangcp/queue/use/blocking/Veterinarian.java b/java-concurrency/src/main/java/com/github/kuangcp/queue/use/blocking/Veterinarian.java index 1fc029c7..5cb5a08e 100644 --- a/java-concurrency/src/main/java/com/github/kuangcp/queue/use/blocking/Veterinarian.java +++ b/java-concurrency/src/main/java/com/github/kuangcp/queue/use/blocking/Veterinarian.java @@ -1,6 +1,8 @@ package com.github.kuangcp.queue.use.blocking; +import java.util.Objects; import java.util.concurrent.BlockingQueue; +import java.util.concurrent.TimeUnit; import lombok.extern.slf4j.Slf4j; // 因为父类没有无参构造器,所以子类也没有,子类也要显式声明 宠物类 @@ -8,9 +10,9 @@ @Slf4j public class Veterinarian extends Thread { - protected final BlockingQueue> pets; - protected String text = ""; - protected final int restTime; // 预约之间的休息时间 + private final BlockingQueue> pets; + String text; + private final int restTime; // 预约之间的休息时间 private boolean shutdown = false; Veterinarian(BlockingQueue> pets, int pause) { @@ -18,7 +20,7 @@ public class Veterinarian extends Thread { restTime = pause; } - synchronized void shutdown() { + private synchronized void shutdown() { shutdown = true; } @@ -28,8 +30,9 @@ public void run() { seePatient(); try { sleep(restTime); - } catch (InterruptedException ignored) { - shutdown = true; + } catch (InterruptedException e) { + shutdown(); + log.error(e.getMessage(), e); } } } @@ -37,16 +40,20 @@ public void run() { /** * 线程从队列中取出预约, 进行操作,如果当前队列没有预约, 就会阻塞 */ - void seePatient() { + private void seePatient() { try { - Appointment ap = pets.take(); // 阻塞take + Appointment ap = pets.poll(3, TimeUnit.SECONDS); // poll 发生阻塞 + if (Objects.isNull(ap)) { + log.error("queue is empty: text={}", text); + shutdown(); + return; + } Pet patient = ap.getPatient(); - log.info(text + " take " + patient.name); + log.info(text + " take " + patient.getName()); patient.examine(); } catch (InterruptedException ignored) { - shutdown = true; + shutdown(); } } - } diff --git a/java-concurrency/src/test/java/com/github/kuangcp/queue/use/blocking/VeterinarianTest.java b/java-concurrency/src/test/java/com/github/kuangcp/queue/use/blocking/VeterinarianTest.java index db9d3557..109e3d56 100644 --- a/java-concurrency/src/test/java/com/github/kuangcp/queue/use/blocking/VeterinarianTest.java +++ b/java-concurrency/src/test/java/com/github/kuangcp/queue/use/blocking/VeterinarianTest.java @@ -14,28 +14,20 @@ public class VeterinarianTest { @Test public void testRun() throws Exception { BlockingQueue> lists = new LinkedBlockingQueue<>(); - Appointment app = new Appointment<>(new Cat("name")); - lists.add(app); - app.getPatient().setName("who"); - - lists.add(app); - lists.add(new Appointment(new Cat("name2"))); + lists.add(new Appointment<>(new Cat("1"))); + lists.add(new Appointment<>(new Cat("2"))); + lists.add(new Appointment<>(new Dog("1"))); + lists.add(new Appointment<>(new Dog("2"))); Veterinarian veterinarian = new Veterinarian(lists, 2000); - veterinarian.text = "1"; - veterinarian.start(); -// 链表这种对象,实参形参内存共享,所以可以启动后添加 - lists.add(new Appointment(new Dog("add"))); - lists.add(new Appointment(new Dog("dog2"))); -// 队列为空就阻塞了 - log.info("{}", veterinarian); + veterinarian.text = "Veterinarian 1"; + Veterinarian veterinarian2 = new Veterinarian(lists, 1000); + veterinarian2.text = "Veterinarian 2"; - veterinarian = new Veterinarian(lists, 1000); - veterinarian.text = "2"; veterinarian.start(); + veterinarian2.start(); - log.info("{}", veterinarian); + veterinarian.join(); + veterinarian2.join(); } - - } \ No newline at end of file diff --git a/java-network/src/main/java/com/github/kuangcp/nio/NioClient.java b/java-network/src/main/java/com/github/kuangcp/nio/selector/NioClient.java similarity index 98% rename from java-network/src/main/java/com/github/kuangcp/nio/NioClient.java rename to java-network/src/main/java/com/github/kuangcp/nio/selector/NioClient.java index 34c6b386..c4b41965 100644 --- a/java-network/src/main/java/com/github/kuangcp/nio/NioClient.java +++ b/java-network/src/main/java/com/github/kuangcp/nio/selector/NioClient.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.nio; +package com.github.kuangcp.nio.selector; import java.net.InetSocketAddress; import java.nio.ByteBuffer; diff --git a/java-network/src/main/java/com/github/kuangcp/nio/NioServer.java b/java-network/src/main/java/com/github/kuangcp/nio/selector/NioServer.java similarity index 98% rename from java-network/src/main/java/com/github/kuangcp/nio/NioServer.java rename to java-network/src/main/java/com/github/kuangcp/nio/selector/NioServer.java index f619ca0d..76c02422 100644 --- a/java-network/src/main/java/com/github/kuangcp/nio/NioServer.java +++ b/java-network/src/main/java/com/github/kuangcp/nio/selector/NioServer.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.nio; +package com.github.kuangcp.nio.selector; import java.net.InetSocketAddress; import java.nio.ByteBuffer; @@ -13,6 +13,7 @@ * Created by Myth on 2017/4/3 0003 * NIO Server端,节省线程 * 该程序启动就建立了一个可监听连接请求的ServerSocketChannel,并将该Channel注册到指定的Selector + * * 从JDK1.4开始,Java提供了NIO API来提供开发 * 之前的都是一个线程处理一个客户端,线程资源消耗大,因为之前那些处理方式在程序输入输出时会线程阻塞,NIO就不会 * NIO 采用多路复用和轮询的机制, 将阻塞都让一个线程来处理,然后提取出准备好的IO来操作, 提高性能, 请求和线程比为 n:1 @@ -31,12 +32,16 @@ public static void main(String[] s) throws Exception { } + // selector 模型 轮询 private synchronized void init() throws Exception { Selector selector = Selector.open(); + //通过OPEN方法来打开一个未绑定的ServerSocketChannel 实例 ServerSocketChannel server = ServerSocketChannel.open(); - //将该ServerSocketChannel绑定到指定ip + + //将该ServerSocketChannel绑定到指定 ip 端口 server.bind(new InetSocketAddress(PORT)); + //设置是NIO 非阻塞模式 server.configureBlocking(false); @@ -72,6 +77,7 @@ private synchronized void init() throws Exception { if (sk.isReadable()) { SocketChannel sc = (SocketChannel) sk.channel(); ByteBuffer buff = ByteBuffer.allocate(1024); + StringBuilder content = new StringBuilder(); try { while (sc.read(buff) > 0) { diff --git a/java-thread/src/main/java/com/github/kuangcp/HowToCreateThread.java b/java-thread/src/main/java/com/github/kuangcp/HowToCreateThread.java index f759045a..a968cab5 100644 --- a/java-thread/src/main/java/com/github/kuangcp/HowToCreateThread.java +++ b/java-thread/src/main/java/com/github/kuangcp/HowToCreateThread.java @@ -10,7 +10,7 @@ public class HowToCreateThread { * 继承方式来实现线程 * 继承Thread 重写run方法 */ - class ExampleOne extends Thread { + static class ExampleOne extends Thread { boolean runFlag = true; @@ -20,13 +20,17 @@ public void run() { System.out.println("继承方式来实现线程 "); } } + + public void shutdown() { + runFlag = false; + } } /** * 实现接口方式来实现线程 * Runnable不是线程,是线程要运行的代码的宿主。 */ - class ExampleTwo implements Runnable { + static class ExampleTwo implements Runnable { boolean runFlag = true; @@ -36,15 +40,19 @@ public void run() { System.out.println("实现接口方式来实现线程"); } } + + public void shutdown() { + runFlag = false; + } } /** * 直接实例化一个匿名内部方法在方法体里 * 匿名内部类 */ - class ExampleThree { + static class ExampleThree { - void target() { + void test() { // lambda方式 new Thread(() -> { System.out.println("直接实例化一个匿名内部方法在方法体里"); diff --git a/java-thread/src/main/java/com/github/kuangcp/ShowCreateThreadForSimpleMain.java b/java-thread/src/main/java/com/github/kuangcp/ShowCreateThreadForSimpleMain.java new file mode 100644 index 00000000..8d8b8bbc --- /dev/null +++ b/java-thread/src/main/java/com/github/kuangcp/ShowCreateThreadForSimpleMain.java @@ -0,0 +1,29 @@ +package com.github.kuangcp; + +import java.lang.management.ManagementFactory; +import java.lang.management.ThreadInfo; +import java.lang.management.ThreadMXBean; +import lombok.extern.slf4j.Slf4j; + +/** + * @author kuangcp on 2019-04-19 10:08 AM + */ +@Slf4j +public class ShowCreateThreadForSimpleMain { + + public static void main(String[] args) { + ThreadMXBean bean = ManagementFactory.getThreadMXBean(); + ThreadInfo[] infos = bean.dumpAllThreads(false, false); + for (ThreadInfo info : infos) { + log.info("id={} name={}", info.getThreadId(), info.getThreadName()); + } + } + // 预期输出 + //name=Monitor Ctrl-Break + //name=Signal Dispatcher 分发处理发送给JVM进程信号 + //name=Finalizer 调用对象 finalize() + //name=Reference Handler 清除 Reference + //name=main + + +} diff --git a/java-thread/src/main/java/com/github/kuangcp/ShowInterrupted.java b/java-thread/src/main/java/com/github/kuangcp/ShowInterrupted.java new file mode 100644 index 00000000..bac2d69f --- /dev/null +++ b/java-thread/src/main/java/com/github/kuangcp/ShowInterrupted.java @@ -0,0 +1,55 @@ +package com.github.kuangcp; + +import java.util.concurrent.TimeUnit; +import lombok.extern.slf4j.Slf4j; + +/** + * @author kuangcp on 2019-04-19 9:07 PM + */ +@Slf4j +public class ShowInterrupted { + + public static void main(String[] args) throws InterruptedException { + + Thread sleep = new Thread(new SleepRunner(), "Sleep"); + sleep.setDaemon(true); + + Thread busy = new Thread(new BusyRunner(), "Busy"); + busy.setDaemon(true); + sleep.start(); + busy.start(); + + TimeUnit.SECONDS.sleep(5); + sleep.interrupt(); + busy.interrupt(); + log.info("sleep={}", sleep.isInterrupted()); + log.info("busy={}", busy.isInterrupted()); + + sleep.join(); + busy.join(); + } + + static class SleepRunner implements Runnable { + + @Override + public void run() { + while (true) { + try { + TimeUnit.SECONDS.sleep(10); + } catch (InterruptedException e) { + log.error(e.getMessage(), e); + } + } + } + } + + static class BusyRunner implements Runnable { + + @Override + public void run() { + while (true) { + + } + } + } +} diff --git a/java-thread/src/main/resources/logback.xml b/java-thread/src/main/resources/logback.xml new file mode 100644 index 00000000..5e375431 --- /dev/null +++ b/java-thread/src/main/resources/logback.xml @@ -0,0 +1,82 @@ + + + + + + + + %d{HH:mm:ss.SSS} %highlight(%-5level) [%thread] %logger{30}:%yellow(%-3L) %msg%n + + + DEBUG + + + + + + ${log.base}debug.log + true + + ${log.base}debug.%d{yyyy-MM-dd}.log + + + %d{MM-dd HH:mm:ss.SSS} |-%-5level |%thread| %logger{36}:%L - %msg%n + + + DEBUG + + + + + + ${log.base}warn.log + true + + ${log.base}warn.%d{yyyy-MM-dd}.log + + + %d{MM-dd HH:mm:ss.SSS} |-%-5level %logger{36}:%L - %msg%n + + + WARN + + + + + + ${log.base}info.log + true + + ${log.base}info.%d{yyyy-MM-dd}.log + + + %d{MM-dd HH:mm:ss.SSS} |-%-5level %logger{36}:%L - %msg%n + + + INFO + + + + + + ${log.base}error.log + true + + ${log.base}error.%d{yyyy-MM-dd}.log + + + %d{MM-dd HH:mm:ss.SSS} | %thread |-%-5level %logger{36}:%L - %msg%n + + + ERROR + + + + + + + + + + + \ No newline at end of file From 1fb288631bd2d341d4a7df5dab33d95fd79551ee Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Sat, 20 Apr 2019 00:37:09 +0800 Subject: [PATCH 010/476] *) adapted for note change --- java-8/Readme.md | 2 +- java-class/src/main/java/com/github/kuangcp/README.md | 2 +- java-collection/src/test/java/README.md | 2 +- java-concurrency/Readme.md | 2 +- java-concurrency/src/main/java/com/github/kuangcp/README.md | 2 +- java-generic/src/main/java/com/github/kuangcp/README.md | 2 +- java-io/README.md | 2 +- java-network/README.md | 4 ++-- java-thread/src/main/java/com/github/kuangcp/README.md | 2 +- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/java-8/Readme.md b/java-8/Readme.md index 0bba2cfb..3f2a3975 100644 --- a/java-8/Readme.md +++ b/java-8/Readme.md @@ -1,5 +1,5 @@ # Java8 -> [Java8 笔记](https://github.com/Kuangcp/Memo/blob/master/Java/AdvancedLearning/Java8.md) +> [Java8 笔记](https://github.com/Kuangcp/Note/blob/master/Java/AdvancedLearning/Java8.md) - [Java 8 in action](https://github.com/java8/Java8InAction) - [Java 8 ](https://github.com/brianway/java-learning/tree/master/java8) diff --git a/java-class/src/main/java/com/github/kuangcp/README.md b/java-class/src/main/java/com/github/kuangcp/README.md index a131e612..aae8cbb0 100644 --- a/java-class/src/main/java/com/github/kuangcp/README.md +++ b/java-class/src/main/java/com/github/kuangcp/README.md @@ -1,2 +1,2 @@ ## 【类和字节码】 -> [笔记详情](https://github.com/Kuangcp/Memo/blob/master/Java/AdvancedLearning/ClassFile.md) \ No newline at end of file +> [笔记详情](https://github.com/Kuangcp/Note/blob/master/Java/AdvancedLearning/ClassFile.md) \ No newline at end of file diff --git a/java-collection/src/test/java/README.md b/java-collection/src/test/java/README.md index fdb11ece..4e7d1799 100644 --- a/java-collection/src/test/java/README.md +++ b/java-collection/src/test/java/README.md @@ -1,2 +1,2 @@ # 集合 -> [笔记详情](https://github.com/Kuangcp/Memo/blob/master/Java/AdvancedLearning/Collection.md) \ No newline at end of file +> [笔记详情](https://github.com/Kuangcp/Note/blob/master/Java/AdvancedLearning/Collection.md) \ No newline at end of file diff --git a/java-concurrency/Readme.md b/java-concurrency/Readme.md index daadb764..c14a4c91 100644 --- a/java-concurrency/Readme.md +++ b/java-concurrency/Readme.md @@ -1,2 +1,2 @@ # Java中的并发 -> [笔记](https://github.com/Kuangcp/Memo/blob/master/Java/AdvancedLearning/Concurrency.md) +> [笔记](https://github.com/Kuangcp/Note/blob/master/Java/AdvancedLearning/Concurrency.md) diff --git a/java-concurrency/src/main/java/com/github/kuangcp/README.md b/java-concurrency/src/main/java/com/github/kuangcp/README.md index ef0f6b30..a4e22841 100644 --- a/java-concurrency/src/main/java/com/github/kuangcp/README.md +++ b/java-concurrency/src/main/java/com/github/kuangcp/README.md @@ -2,6 +2,6 @@ > 主要参考: Java程序员修炼之道 >> 注: IDEA中Groovy的SDK配置其实选择Gradle的目录就行了 -> [笔记详情](https://github.com/Kuangcp/Memo/blob/master/Java/AdvancedLearning/Concurrents.md) +> [笔记详情](https://github.com/Kuangcp/Note/blob/master/Java/AdvancedLearning/Concurrents.md) TODO 重新整理学习代码 \ No newline at end of file diff --git a/java-generic/src/main/java/com/github/kuangcp/README.md b/java-generic/src/main/java/com/github/kuangcp/README.md index ff354862..c2b02ed6 100644 --- a/java-generic/src/main/java/com/github/kuangcp/README.md +++ b/java-generic/src/main/java/com/github/kuangcp/README.md @@ -1,3 +1,3 @@ # Java泛型 -> [总结](https://github.com/Kuangcp/Memo/blob/master/Java/AdvancedLearning/Generics.md) +> [总结](https://github.com/Kuangcp/Note/blob/master/Java/AdvancedLearning/Generics.md) diff --git a/java-io/README.md b/java-io/README.md index 932e3e14..395ce1f5 100644 --- a/java-io/README.md +++ b/java-io/README.md @@ -1,2 +1,2 @@ # IO的学习 -> [笔记详情](https://github.com/Kuangcp/Memo/blob/master/Java/AdvancedLearning/IO.md) +> [笔记详情](https://github.com/Kuangcp/Note/blob/master/Java/AdvancedLearning/IO.md) diff --git a/java-network/README.md b/java-network/README.md index 9ba93a93..2d5a31e0 100644 --- a/java-network/README.md +++ b/java-network/README.md @@ -1,4 +1,4 @@ # Socket -> 套接字编程 [详细笔记](https://github.com/Kuangcp/Memo/blob/master/Java/AdvancedLearning/Socket.md) | -[IO基础笔记](https://github.com/Kuangcp/Memo/blob/master/Java/AdvancedLearning/IO.md) +> 套接字编程 [详细笔记](https://github.com/Kuangcp/Note/blob/master/Java/AdvancedLearning/Socket.md) | +[IO基础笔记](https://github.com/Kuangcp/Note/blob/master/Java/AdvancedLearning/IO.md) diff --git a/java-thread/src/main/java/com/github/kuangcp/README.md b/java-thread/src/main/java/com/github/kuangcp/README.md index af6e8bad..3d0a4295 100644 --- a/java-thread/src/main/java/com/github/kuangcp/README.md +++ b/java-thread/src/main/java/com/github/kuangcp/README.md @@ -1,2 +1,2 @@ # 线程的基础学习 -> [笔记详情](https://github.com/Kuangcp/Memo/blob/master/Java/AdvancedLearning/Thread.md) \ No newline at end of file +> [笔记详情](https://github.com/Kuangcp/Note/blob/master/Java/AdvancedLearning/Thread.md) \ No newline at end of file From bd911d545c5adadbae09d65d8f52801f1d2e80fa Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Sat, 20 Apr 2019 14:18:05 +0800 Subject: [PATCH 011/476] +) learn NIO --- .../java/netty/timeServer/TimeClient.java | 4 + .../java/netty/timeServer/TimeServer.java | 6 +- .../java/netty/timeServer/TimeClientTest.java | 2 +- .../java/netty/timeServer/TimeServerTest.java | 2 +- .../bio/chattingroom/SocketClient.java | 5 +- .../kuangcp/nio/selector/EchoNIOServer.java | 107 ++++++++ .../kuangcp/nio/selector/NioClient.java | 76 +++--- .../kuangcp/nio/selector/NioServer.java | 241 +++++++----------- .../com/github/kuangcp/ShowInterrupted.java | 4 - 9 files changed, 256 insertions(+), 191 deletions(-) create mode 100644 java-network/src/main/java/com/github/kuangcp/nio/selector/EchoNIOServer.java diff --git a/java-netty/src/main/java/netty/timeServer/TimeClient.java b/java-netty/src/main/java/netty/timeServer/TimeClient.java index 26f8261b..4746baf0 100644 --- a/java-netty/src/main/java/netty/timeServer/TimeClient.java +++ b/java-netty/src/main/java/netty/timeServer/TimeClient.java @@ -13,6 +13,10 @@ @Slf4j public class TimeClient { + void connectLocal(int port) throws Exception { + connect(port, "127.0.0.1"); + } + void connect(int port, String host) throws Exception { // 配置客户端NIO线程组 EventLoopGroup group = new NioEventLoopGroup(); diff --git a/java-netty/src/main/java/netty/timeServer/TimeServer.java b/java-netty/src/main/java/netty/timeServer/TimeServer.java index d876dae2..2ac43844 100644 --- a/java-netty/src/main/java/netty/timeServer/TimeServer.java +++ b/java-netty/src/main/java/netty/timeServer/TimeServer.java @@ -11,10 +11,14 @@ public class TimeServer { - public void bind(int port) throws Exception { + static final int port = 8080; + + public void start() throws Exception { + // NIO 结合两个线程池 // 配置服务端的NIO线程组 , 一个用于接受客户端的连接, 一个是处理SocketChannel的网络读写 EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); + try { ServerBootstrap serverBootstrap = new ServerBootstrap(); serverBootstrap.group(bossGroup, workerGroup) diff --git a/java-netty/src/test/java/netty/timeServer/TimeClientTest.java b/java-netty/src/test/java/netty/timeServer/TimeClientTest.java index a130a372..aa5a5abb 100644 --- a/java-netty/src/test/java/netty/timeServer/TimeClientTest.java +++ b/java-netty/src/test/java/netty/timeServer/TimeClientTest.java @@ -15,6 +15,6 @@ public class TimeClientTest { @Test(threadPoolSize = 20, invocationCount = 50) // @Test public void testClient() throws Exception { - timeClient.connect(8080, "127.0.0.1"); + timeClient.connectLocal(TimeServer.port); } } \ No newline at end of file diff --git a/java-netty/src/test/java/netty/timeServer/TimeServerTest.java b/java-netty/src/test/java/netty/timeServer/TimeServerTest.java index 1e3d5f5f..fa6f2fff 100644 --- a/java-netty/src/test/java/netty/timeServer/TimeServerTest.java +++ b/java-netty/src/test/java/netty/timeServer/TimeServerTest.java @@ -13,6 +13,6 @@ public class TimeServerTest { @Test public void testServer() throws Exception { - timeServer.bind(8080); + timeServer.start(); } } \ No newline at end of file diff --git a/java-network/src/main/java/com/github/kuangcp/bio/chattingroom/SocketClient.java b/java-network/src/main/java/com/github/kuangcp/bio/chattingroom/SocketClient.java index f1642bc5..ca2cfd93 100644 --- a/java-network/src/main/java/com/github/kuangcp/bio/chattingroom/SocketClient.java +++ b/java-network/src/main/java/com/github/kuangcp/bio/chattingroom/SocketClient.java @@ -15,10 +15,9 @@ public class SocketClient { private String name; - public static void main(String[] args) throws Exception { SocketClient client = new SocketClient(); - client.setName(); + client.inputName(); client.start(); } @@ -37,7 +36,7 @@ private void start() throws Exception { } } - private void setName() throws Exception { + private void inputName() throws Exception { log.info("input name"); BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); name = br.readLine(); diff --git a/java-network/src/main/java/com/github/kuangcp/nio/selector/EchoNIOServer.java b/java-network/src/main/java/com/github/kuangcp/nio/selector/EchoNIOServer.java new file mode 100644 index 00000000..5e66d8ca --- /dev/null +++ b/java-network/src/main/java/com/github/kuangcp/nio/selector/EchoNIOServer.java @@ -0,0 +1,107 @@ +package com.github.kuangcp.nio.selector; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.nio.ByteBuffer; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; +import java.nio.charset.StandardCharsets; +import java.util.Iterator; +import java.util.Set; + +/** + * @author kuangcp on 2019-04-20 10:18 AM + */ +public class EchoNIOServer { + + private volatile boolean stop = false; + + public static void main(String[] args) throws IOException { + new EchoNIOServer().start(); + } + public void start() throws IOException { + Selector selector = Selector.open(); + //通过OPEN方法来打开一个未绑定的ServerSocketChannel 实例 + ServerSocketChannel server = ServerSocketChannel.open(); + //将该ServerSocketChannel绑定到指定ip + server.bind(new InetSocketAddress(NioServer.PORT)); + //设置是NIO 非阻塞模式 + server.configureBlocking(false); + + //将sever注册到指定Selector对象上 + server.register(selector, SelectionKey.OP_ACCEPT); + + while (!stop) { + selector.select(2000); + Set selectedKeys = selector.selectedKeys(); + Iterator it = selectedKeys.iterator(); + SelectionKey key; + while (it.hasNext()) { + key = it.next(); + it.remove(); + try { + handleInput(selector, key); + } catch (Exception e) { + if (key != null) { + key.cancel(); + if (key.channel() != null) { + key.channel().close(); + } + } + } + } + } + } + + private void handleInput(Selector selector, SelectionKey key) throws IOException { + if (key.isValid()) { + if (key.isAcceptable()) { + // Accept the new connection + ServerSocketChannel ssc = (ServerSocketChannel) key.channel(); + SocketChannel sc = ssc.accept(); + sc.configureBlocking(false); + // Add the new connection to the selector + sc.register(selector, SelectionKey.OP_READ); + } + + if (key.isReadable()) { + SocketChannel sc = (SocketChannel) key.channel(); + ByteBuffer readBuffer = ByteBuffer.allocate(1024); + int readBytes = sc.read(readBuffer); + if (readBytes > 0) { + // 读取完就要切换 + readBuffer.flip(); + byte[] bytes = new byte[readBuffer.remaining()]; + readBuffer.get(bytes); + String body = new String(bytes, StandardCharsets.UTF_8); + System.out.println("The time server receive order : " + body); + if ("STOP".equalsIgnoreCase(body)) { + stop(); + } + doWrite(sc); + } else if (readBytes < 0) { + // 对端链路关闭 + key.cancel(); + sc.close(); + } + // 读到0字节,忽略 + } + } + + } + + + private void doWrite(SocketChannel channel) throws IOException { + byte[] bytes = "response".getBytes(); + ByteBuffer writeBuffer = ByteBuffer.allocate(bytes.length); + writeBuffer.put(bytes); + writeBuffer.flip(); + channel.write(writeBuffer); + } + + private void stop() { + this.stop = true; + } +} diff --git a/java-network/src/main/java/com/github/kuangcp/nio/selector/NioClient.java b/java-network/src/main/java/com/github/kuangcp/nio/selector/NioClient.java index c4b41965..c8775fe6 100644 --- a/java-network/src/main/java/com/github/kuangcp/nio/selector/NioClient.java +++ b/java-network/src/main/java/com/github/kuangcp/nio/selector/NioClient.java @@ -1,22 +1,21 @@ package com.github.kuangcp.nio.selector; +import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.SocketChannel; -import java.nio.charset.Charset; import java.util.Scanner; +import lombok.extern.slf4j.Slf4j; /** * Created by Myth on 2017/4/5 0005 - * NIO实现的客户端,相对于普通的CS结构节省资源 */ +@Slf4j public class NioClient { - //定义检测SocketChannel的Selector对象 private Selector selector = null; - private Charset charset = Charset.forName("UTF-8"); private boolean stop = false; @@ -24,22 +23,29 @@ public static void main(String[] s) throws Exception { new NioClient().init(); } - private void init() throws Exception { + private void init() throws IOException { selector = Selector.open(); - InetSocketAddress isa = new InetSocketAddress(NioServer.PORT); - SocketChannel sc = SocketChannel.open(isa); - sc.configureBlocking(false); - sc.register(selector, SelectionKey.OP_READ); + InetSocketAddress address = new InetSocketAddress(NioServer.PORT); + SocketChannel channel = SocketChannel.open(address); + channel.configureBlocking(false); + channel.register(selector, SelectionKey.OP_READ); + new ClientThread().start(); + Scanner scan = new Scanner(System.in); -// while (scan.hasNextLine()) { - while (!stop) { - String line = scan.nextLine(); - if ("exit".equalsIgnoreCase(line)) { - // 只是停掉了通信线程, 但是该方法的输入线程还是被阻塞了 - stop(); + try { + while (scan.hasNextLine()) { + String line = scan.nextLine(); + channel.write(NioServer.charset.encode(line)); + + if ("exit".equalsIgnoreCase(line)) { + stop(); + return; + } } - sc.write(charset.encode(line)); + } catch (IOException e) { + log.error(e.getMessage(), e); + stop(); } } @@ -47,32 +53,19 @@ private void stop() { this.stop = true; } - //定义读取服务器数据的线程 - private class ClientThread extends Thread { + //读取服务器数据的线程 + class ClientThread extends Thread { public void run() { try { while (!stop) { - int result = selector.select(); - if (result <= 0) { - continue; - } - System.out.println(stop + "准备好的数量:" + result); + selector.select(); + for (SelectionKey sk : selector.selectedKeys()) { selector.selectedKeys().remove(sk); if (sk.isReadable()) { - SocketChannel sc = (SocketChannel) sk.channel(); - ByteBuffer buff = ByteBuffer.allocate(1024); - StringBuilder content = new StringBuilder(); - while (sc.read(buff) > 0) { - sc.read(buff); - buff.flip(); - content.append(charset.decode(buff)); - } - System.out.println("聊天信息" + content.toString()); - sk.interestOps(SelectionKey.OP_READ); + readContent(sk); } - } } } catch (Exception e) { @@ -81,4 +74,19 @@ public void run() { } } + private void readContent(SelectionKey sk) throws IOException { + SocketChannel sc = (SocketChannel) sk.channel(); + ByteBuffer buff = ByteBuffer.allocate(1024); + StringBuilder content = new StringBuilder(); + while (sc.read(buff) > 0) { + sc.read(buff); + buff.flip(); + content.append(NioServer.charset.decode(buff)); + } + if (content.length() == 0) { + return; + } + log.info("msg={}", content.toString()); + sk.interestOps(SelectionKey.OP_READ); + } } diff --git a/java-network/src/main/java/com/github/kuangcp/nio/selector/NioServer.java b/java-network/src/main/java/com/github/kuangcp/nio/selector/NioServer.java index 76c02422..64fd8a2c 100644 --- a/java-network/src/main/java/com/github/kuangcp/nio/selector/NioServer.java +++ b/java-network/src/main/java/com/github/kuangcp/nio/selector/NioServer.java @@ -1,5 +1,7 @@ package com.github.kuangcp.nio.selector; +import com.github.kuangcp.io.ResourceTool; +import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.Channel; @@ -8,6 +10,9 @@ import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.Set; +import lombok.extern.slf4j.Slf4j; /** * Created by Myth on 2017/4/3 0003 @@ -19,21 +24,22 @@ * NIO 采用多路复用和轮询的机制, 将阻塞都让一个线程来处理,然后提取出准备好的IO来操作, 提高性能, 请求和线程比为 n:1 * 原先的BIO是 1:1 */ +@Slf4j public class NioServer { static final int PORT = 30000; + //定义实现编码,解码的字符集 - private Charset charset = Charset.forName("UTF-8"); + static Charset charset = StandardCharsets.UTF_8; + private volatile boolean stop = false; public static void main(String[] s) throws Exception { - new NioServer().init(); -// new NioServer().initByOtherWay(); + new NioServer().start(); } - // selector 模型 轮询 - private synchronized void init() throws Exception { + private void start() throws Exception { Selector selector = Selector.open(); //通过OPEN方法来打开一个未绑定的ServerSocketChannel 实例 @@ -47,161 +53,102 @@ private synchronized void init() throws Exception { //将sever注册到指定Selector对象上 server.register(selector, SelectionKey.OP_ACCEPT); - // 只要有管道准备就绪了,就继续轮询 -// while (selector.select() > 0) { + while (!stop) { - // TODO 能写成死循环, 但是下面这个方法一定要调用, 为什么 - // 因为只有在选择了至少一个通道后,才会返回该选择器的唤醒方法,或者当前线程中断,以先到者为准。 这是API的翻译 - int result = selector.select(); - if (result <= 0) { - continue; - } - System.out.println("准备好的数量:" + result); + // 因为只有在选择了至少一个通道后,才会返回该选择器的唤醒方法,或者当前线程中断,以先到者为准。否则一直阻塞 + selector.select(); + //依次处理selector上的每个已经准备好的管道 - for (SelectionKey sk : selector.selectedKeys()) { - //从selector上的已选择key集中删除正在处理的连接请求 + Set selectedKeys = selector.selectedKeys(); + for (SelectionKey sk : selectedKeys) { + //从selector上的已选择key集 中删除正在处理的连接请求 selector.selectedKeys().remove(sk); + + // 连接 accept if (sk.isAcceptable()) { - //调用accept方法,产生服务器端的SocketChannel - SocketChannel sc = server.accept(); - sc.configureBlocking(false);//NIO模式 - sc.register(selector, SelectionKey.OP_READ);//注册到selector上 - sk.interestOps(SelectionKey.OP_ACCEPT);//将sk对应的Channel设置成准备接受其他请求 + register(selector, server, sk); + } + // 连接建立 + else if (sk.isConnectable()) { + System.out.println("close"); } - //如果有数据需要读取 - // TODO 为什么 打开服务端,打开客户端, 然后关闭客户端, 服务端死循环的输出,再次打开客户端后 , - // 服务端就报错 ConcurrentModificationException 退出了, 客户端也随之断开了连接 - // 当给方法加上同步关键字后, 就直接退出了没有报错, 说明, 当客户端重启后, 存在并发修改 - // TODO 那么为什么呢 - // 反之则是客户端死循环输出 - if (sk.isReadable()) { - SocketChannel sc = (SocketChannel) sk.channel(); - ByteBuffer buff = ByteBuffer.allocate(1024); - - StringBuilder content = new StringBuilder(); - try { - while (sc.read(buff) > 0) { - buff.flip(); - content.append(charset.decode(buff)); - } - System.out.println("读取的数据 : " + content.toString()); - if ("stop".equalsIgnoreCase(content.toString())) { - stop(); - } - sk.interestOps(SelectionKey.OP_READ);//设置成准备下次读取 - } catch (Exception e) { - //从Selector中删除指定的SelectionKey - sk.cancel(); - if (sk.channel() != null) { - sk.channel().close(); - } - } - //聊天信息不为空 - if (content.length() > 0) { - //遍历selector里注册的所有SelectionKey - for (SelectionKey key : selector.keys()) { - Channel targetChannel = key.channel();//获取Channel - //如果改Channel是SocketChannel是SocketChannel对象 - if (targetChannel instanceof SocketChannel) { - //将读到的内容写入到该Channel中去 - SocketChannel dest = (SocketChannel) targetChannel; - dest.write(charset.encode(content.toString())); - } - } - } + // 读就绪 + else if (sk.isReadable()) { + readContent(selector, sk); + } + // 写就绪 + else if (sk.isWritable()) { + } } + } + } + private void register(Selector selector, ServerSocketChannel server, SelectionKey sk) + throws IOException { + //调用accept方法,产生服务器端的SocketChannel + SocketChannel sc = server.accept(); + sc.configureBlocking(false);//NIO模式 + sc.register(selector, SelectionKey.OP_READ);//注册到selector上 + sk.interestOps(SelectionKey.OP_ACCEPT);//将sk对应的Channel设置成准备接受其他请求 + } + + // 解析数据 + private void readContent(Selector selector, SelectionKey sk) throws IOException { + SocketChannel sc = (SocketChannel) sk.channel(); + ByteBuffer buff = ByteBuffer.allocate(1024); + + StringBuilder content = new StringBuilder(); + try { + int s; + while ((s = sc.read(buff)) > 0) { + buff.flip(); + content.append(charset.decode(buff)); + } + buff.clear(); + if (s == -1) { + System.out.println("close"); + sc.close(); + } + + if (content.length() == 0) { + return; + } + + log.info("receive msg: {}", content.toString()); +// TimeUnit.MILLISECONDS.sleep(700); + + if ("stop".equalsIgnoreCase(content.toString())) { + stop(); + log.info("stop the sever"); + return; + } + + sk.interestOps(SelectionKey.OP_READ);//设置成准备下次读取 + } catch (Exception e) { + //从Selector中删除指定的SelectionKey + sk.cancel(); + ResourceTool.close(sk.channel()); } + if (content.length() <= 0) { + return; + } + + // 遍历selector里注册的所有SelectionKey + // 也就是广播消息出去 + for (SelectionKey key : selector.keys()) { + Channel targetChannel = key.channel();//获取Channel + //如果改Channel是SocketChannel是SocketChannel对象 + if (targetChannel instanceof SocketChannel) { + //将读到的内容写入到该Channel中去 + SocketChannel dest = (SocketChannel) targetChannel; + dest.write(charset.encode(content.toString())); + } + } } private void stop() { this.stop = true; } - -// -// public void initByOtherWay() throws IOException { -// Selector selector = Selector.open(); -// //通过OPEN方法来打开一个未绑定的ServerSocketChannel 实例 -// ServerSocketChannel server = ServerSocketChannel.open(); -// //将该ServerSocketChannel绑定到指定ip -// server.bind(new InetSocketAddress(PORT)); -// //设置是NIO 非阻塞模式 -// server.configureBlocking(false); -// -// //将sever注册到指定Selector对象上 -// server.register(selector, SelectionKey.OP_ACCEPT); -// -// while (!stop) { -// selector.select(2000); -// Set selectedKeys = selector.selectedKeys(); -// Iterator it = selectedKeys.iterator(); -// SelectionKey key; -// while (it.hasNext()) { -// key = it.next(); -// it.remove(); -// try { -// handleInput(key); -// } catch (Exception e) { -// if (key != null) { -// key.cancel(); -// if (key.channel() != null) -// key.channel().close(); -// } -// } -// } -// } -// } -// -// private void handleInput(SelectionKey key) throws IOException { -// if(key.isValid()){ -// if(key.isAcceptable()){ -// // Accept the new connection -// ServerSocketChannel ssc = (ServerSocketChannel) key.channel(); -// SocketChannel sc = ssc.accept(); -// sc.configureBlocking(false); -// // Add the new connection to the selector -// sc.register(selector, SelectionKey.OP_READ); -// } -// if(key.isReadable()){ -// SocketChannel sc = (SocketChannel) key.channel(); -// ByteBuffer readBuffer = ByteBuffer.allocate(1024); -// int readBytes = sc.read(readBuffer); -// if (readBytes > 0) { -// // 读取完就要切换 -// readBuffer.flip(); -// byte[] bytes = new byte[readBuffer.remaining()]; -// readBuffer.get(bytes); -// String body = new String(bytes, "UTF-8"); -// System.out.println("The time server receive order : "+ body); -// if("STOP".equalsIgnoreCase(body)){ -// stop(); -// } -// doWrite(sc, "response"); -// } else if (readBytes < 0) { -// // 对端链路关闭 -// key.cancel(); -// sc.close(); -// } -// // 读到0字节,忽略 -// } -// } -// -// } -// - -// -// private void doWrite(SocketChannel channel, String response) -// throws IOException { -// if (response != null && response.trim().length() > 0) { -// byte[] bytes = response.getBytes(); -// ByteBuffer writeBuffer = ByteBuffer.allocate(bytes.length); -// writeBuffer.put(bytes); -// writeBuffer.flip(); -// channel.write(writeBuffer); -// } -// } - - } diff --git a/java-thread/src/main/java/com/github/kuangcp/ShowInterrupted.java b/java-thread/src/main/java/com/github/kuangcp/ShowInterrupted.java index bac2d69f..70ac10de 100644 --- a/java-thread/src/main/java/com/github/kuangcp/ShowInterrupted.java +++ b/java-thread/src/main/java/com/github/kuangcp/ShowInterrupted.java @@ -10,7 +10,6 @@ public class ShowInterrupted { public static void main(String[] args) throws InterruptedException { - Thread sleep = new Thread(new SleepRunner(), "Sleep"); sleep.setDaemon(true); @@ -24,9 +23,6 @@ public static void main(String[] args) throws InterruptedException { busy.interrupt(); log.info("sleep={}", sleep.isInterrupted()); log.info("busy={}", busy.isInterrupted()); - - sleep.join(); - busy.join(); } static class SleepRunner implements Runnable { From ae4bc7318401d8bda40e15ea5acd65117e45980f Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Mon, 22 Apr 2019 00:04:35 +0800 Subject: [PATCH 012/476] +) jmh test --- dependency.gradle | 79 ++++++++-------- java-class/README.md | 11 ++- java-class/build.gradle | 19 ++-- .../customserialize/MythSerializeTest.java | 34 ------- .../{ => serialize}/customserialize/Myth.java | 2 +- .../customserialize/MythSerialize.java | 32 +++---- .../json/JsonSmileMigrationService.java | 52 +++++++++++ .../serialize/json/speed/FastJsonTool.java | 6 +- .../serialize/json/speed/GsonTool.java | 9 +- .../serialize/json/speed/JacksonTool.java | 16 ++-- .../serialize/json/speed/JsonTool.java | 19 ++++ .../kuangcp/serialize/json/speed/README.md | 5 + .../main/java/jmh/StringBuilderBenchmark.java | 44 +++++++++ .../src/main/java/jvm/oom/MethodAreaOOM.java | 3 +- .../kuangcp/serialize/SerializeTest.java | 12 +-- .../customserialize/MythSerializeTest.java | 33 +++++++ .../kuangcp/serialize/json/GsonTest.java | 2 +- .../kuangcp/serialize/json/SpeedTest.java | 45 +++++++++ .../serialize/json/speed/JsonTool.java | 17 ---- .../kuangcp/serialize/json/speed/README.md | 93 ------------------- .../serialize/json/speed/SpeedTest.java | 39 -------- .../java/jmh/StringBuilderBenchmarkTest.java | 21 +++++ .../github/kuangcp/lock/pvp/PlayerTest.java | 41 ++++++++ 23 files changed, 361 insertions(+), 273 deletions(-) delete mode 100644 java-class/src/main/java/com/github/kuangcp/customserialize/MythSerializeTest.java rename java-class/src/main/java/com/github/kuangcp/{ => serialize}/customserialize/Myth.java (93%) rename java-class/src/main/java/com/github/kuangcp/{ => serialize}/customserialize/MythSerialize.java (78%) create mode 100644 java-class/src/main/java/com/github/kuangcp/serialize/json/JsonSmileMigrationService.java rename java-class/src/{test => main}/java/com/github/kuangcp/serialize/json/speed/FastJsonTool.java (74%) rename java-class/src/{test => main}/java/com/github/kuangcp/serialize/json/speed/GsonTool.java (71%) rename java-class/src/{test => main}/java/com/github/kuangcp/serialize/json/speed/JacksonTool.java (59%) create mode 100644 java-class/src/main/java/com/github/kuangcp/serialize/json/speed/JsonTool.java create mode 100644 java-class/src/main/java/com/github/kuangcp/serialize/json/speed/README.md create mode 100644 java-class/src/main/java/jmh/StringBuilderBenchmark.java create mode 100644 java-class/src/test/java/com/github/kuangcp/serialize/customserialize/MythSerializeTest.java create mode 100644 java-class/src/test/java/com/github/kuangcp/serialize/json/SpeedTest.java delete mode 100644 java-class/src/test/java/com/github/kuangcp/serialize/json/speed/JsonTool.java delete mode 100644 java-class/src/test/java/com/github/kuangcp/serialize/json/speed/README.md delete mode 100644 java-class/src/test/java/com/github/kuangcp/serialize/json/speed/SpeedTest.java create mode 100644 java-class/src/test/java/jmh/StringBuilderBenchmarkTest.java create mode 100644 java-concurrency/src/test/java/com/github/kuangcp/lock/pvp/PlayerTest.java diff --git a/dependency.gradle b/dependency.gradle index 424bd538..6afe69e5 100644 --- a/dependency.gradle +++ b/dependency.gradle @@ -1,59 +1,62 @@ // when add a new dependency, gradle not download that package // just when module build.gradle use this dependency, then download and build project -rootProject.ext { +ext { ver = [ - logback : '1.2.3', - jedis : '2.9.0', - junit : '4.12', - groovy : '2.5.6', - mail : '1.4.7', - jackson : '2.9.5', - hamcrest: '1.3', - kcp_tool: '1.0.3', - netty : '5.0.0.Alpha1', - guava : '27.1-jre' + logback : '1.2.3' + , jedis : '2.9.0' + , junit : '4.12' + , groovy : '2.5.6' + , mail : '1.4.7' + , jackson : '2.9.5' + , hamcrest: '1.3' + , kcp_tool: '1.0.3' + , netty : '5.0.0.Alpha1' + , guava : '27.1-jre' + , jmh : '1.2.1' ] libs = [ - // tool - "lombok" : "org.projectlombok:lombok:1.18.2", - "common-lang" : "org.apache.commons:commons-lang3:3.7", - "groovy" : "org.codehaus.groovy:groovy-all:$rootProject.ver.groovy", - "cglib" : "cglib:cglib:3.2.9", - "common-math" : "org.apache.commons:commons-math3:3.6.1", - "guava" : "com.google.guava:guava:$rootProject.ver.guava", - // mine tool - "kcp-tool" : "com.github.kuangcp:kcp-tool:$rootProject.ver.kcp_tool", + "kcp-tool" : "com.github.kuangcp:kcp-tool:$ver.kcp_tool" + + // tool + , "lombok" : "org.projectlombok:lombok:1.18.2" + , "common-lang" : "org.apache.commons:commons-lang3:3.7" + , "groovy" : "org.codehaus.groovy:groovy-all:$ver.groovy" + , "cglib" : "cglib:cglib:3.2.9" + , "common-math" : "org.apache.commons:commons-math3:3.6.1" + , "guava" : "com.google.guava:guava:$ver.guava" // test, junit has removed hamcrest since 4.11 - "hamcrest-core" : "org.hamcrest:hamcrest-core:$rootProject.ver.hamcrest", - "hamcrest-lib" : "org.hamcrest:hamcrest-library:$rootProject.ver.hamcrest", + , "hamcrest-core" : "org.hamcrest:hamcrest-core:$ver.hamcrest" + , "hamcrest-lib" : "org.hamcrest:hamcrest-library:$ver.hamcrest" + , "jmh" : "org.openjdk.jmh:jmh-core:1.21" + , "jmh-generator-annprocess": "org.openjdk.jmh:jmh-generator-annprocess:1.21" - "junit" : "junit:junit:$rootProject.ver.junit", - "mockito-core" : "org.mockito:mockito-core:2.21.0", - "testng" : "org.testng:testng:6.14.3", + , "junit" : "junit:junit:$ver.junit" + , "mockito-core" : "org.mockito:mockito-core:2.21.0" + , "testng" : "org.testng:testng:6.14.3" // log - "logback-classic" : "ch.qos.logback:logback-classic:$rootProject.ver.logback", + , "logback-classic" : "ch.qos.logback:logback-classic:$ver.logback" - "netty" : "io.netty:netty-all:$rootProject.ver.netty", + // network + , "ok-http" : "com.squareup.okhttp3:okhttp:3.13.1" + , "netty" : "io.netty:netty-all:$ver.netty" // mail - "mail" : "javax.mail:mail:$rootProject.ver.mail", - "activation" : "javax.activation:activation:1.1.1", - - // http - "ok-http" : "com.squareup.okhttp3:okhttp:3.13.1", + , "mail" : "javax.mail:mail:$ver.mail" + , "activation" : "javax.activation:activation:1.1.1" // db - "jedis" : "redis.clients:jedis:3.0.1", + , "jedis" : "redis.clients:jedis:3.0.1" // JSON dependency - "gson" : "com.google.code.gson:gson:2.8.5", - "fastjson" : "com.alibaba:fastjson:1.2.47", - "jackson_core" : "com.fasterxml.jackson.core:jackson-core:$rootProject.ver.jackson", - "jackson_databind" : "com.fasterxml.jackson.core:jackson-databind:$rootProject.ver.jackson", - "jackson_annotations": "com.fasterxml.jackson.core:jackson-annotations:$rootProject.ver.jackson", + , "gson" : "com.google.code.gson:gson:2.8.5" + , "fastjson" : "com.alibaba:fastjson:1.2.47" + , "jackson_core" : "com.fasterxml.jackson.core:jackson-core:$ver.jackson" + , "jackson_databind" : "com.fasterxml.jackson.core:jackson-databind:$ver.jackson" + , "jackson_annotations" : "com.fasterxml.jackson.core:jackson-annotations:$ver.jackson" + , "jackson_smile" : "com.fasterxml.jackson.dataformat:jackson-dataformat-smile:$ver.jackson" ] } diff --git a/java-class/README.md b/java-class/README.md index 0c9cd6f4..baa4f5f7 100644 --- a/java-class/README.md +++ b/java-class/README.md @@ -1,4 +1,9 @@ -# 字节码相关 -> 包括基础数据类型的学习 -> 以及基础语法的学习 +# 基础语法和字节码 + +1. 常用基本数据类型学习 +1. 基础语法的学习 +1. 反射学习 +1. 序列化 +1. 字节码操作学习 +1. JVM 学习 diff --git a/java-class/build.gradle b/java-class/build.gradle index b51f194a..b6cfd307 100644 --- a/java-class/build.gradle +++ b/java-class/build.gradle @@ -1,10 +1,13 @@ dependencies { - implementation rootProject.libs['fastjson'] - implementation rootProject.libs['gson'] - implementation rootProject.libs['jackson_core'] - implementation rootProject.libs['jackson_databind'] - implementation rootProject.libs['jackson_annotations'] - - implementation rootProject.libs['common-lang'] - implementation rootProject.libs['cglib'] + implementation libs['fastjson'] + implementation libs['gson'] + implementation libs['jackson_core'] + implementation libs['jackson_databind'] + implementation libs['jackson_annotations'] + implementation libs['jackson_smile'] + + implementation libs['jmh'] + implementation libs['jmh-generator-annprocess'] + implementation libs['common-lang'] + implementation libs['cglib'] } diff --git a/java-class/src/main/java/com/github/kuangcp/customserialize/MythSerializeTest.java b/java-class/src/main/java/com/github/kuangcp/customserialize/MythSerializeTest.java deleted file mode 100644 index 19fe270e..00000000 --- a/java-class/src/main/java/com/github/kuangcp/customserialize/MythSerializeTest.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.github.kuangcp.customserialize; - - -/** - * Created by https://github.com/kuangcp on 17-10-24 下午2:47 - * 测试自定义序列化 - * - * @author kuangcp - */ -public class MythSerializeTest { - - public static void main(String[] s) { - testOut(); - testIn(); - - } - - public static void testOut() { - MythSerialize mythSerialize = new MythSerialize<>(); - Myth domain = new Myth(); - domain.setName("myth"); - domain.setPhone("121212121"); -// domain.setTest(90909090L); - mythSerialize.out(domain, "/home/kcp/test/person.txt"); - } - - public static void testIn() { - MythSerialize mythSerialize = new MythSerialize<>(); - Myth domain = (Myth) mythSerialize.in(Myth.class, "/home/kcp/test/person.txt"); - System.out.println(domain.toString()); - - - } -} diff --git a/java-class/src/main/java/com/github/kuangcp/customserialize/Myth.java b/java-class/src/main/java/com/github/kuangcp/serialize/customserialize/Myth.java similarity index 93% rename from java-class/src/main/java/com/github/kuangcp/customserialize/Myth.java rename to java-class/src/main/java/com/github/kuangcp/serialize/customserialize/Myth.java index 2ffd4c47..1a99d839 100644 --- a/java-class/src/main/java/com/github/kuangcp/customserialize/Myth.java +++ b/java-class/src/main/java/com/github/kuangcp/serialize/customserialize/Myth.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.customserialize; +package com.github.kuangcp.serialize.customserialize; /** * Created by https://github.com/kuangcp on 17-10-24 下午3:35 diff --git a/java-class/src/main/java/com/github/kuangcp/customserialize/MythSerialize.java b/java-class/src/main/java/com/github/kuangcp/serialize/customserialize/MythSerialize.java similarity index 78% rename from java-class/src/main/java/com/github/kuangcp/customserialize/MythSerialize.java rename to java-class/src/main/java/com/github/kuangcp/serialize/customserialize/MythSerialize.java index f0b8cc65..56df63eb 100644 --- a/java-class/src/main/java/com/github/kuangcp/customserialize/MythSerialize.java +++ b/java-class/src/main/java/com/github/kuangcp/serialize/customserialize/MythSerialize.java @@ -1,8 +1,9 @@ -package com.github.kuangcp.customserialize; +package com.github.kuangcp.serialize.customserialize; import java.io.*; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import lombok.extern.slf4j.Slf4j; /** * Created by https://github.com/kuangcp on 17-10-24 下午2:25 @@ -10,13 +11,13 @@ * * @author kuangcp */ +@Slf4j public class MythSerialize { - public Object in(Class target, String path) { - Object object = null; + public T in(Class target, InputStream inputStream) { + T object = null; try { - FileInputStream fileInputStream = new FileInputStream(path); - Reader reader = new InputStreamReader(fileInputStream); + Reader reader = new InputStreamReader(inputStream); BufferedReader bufferedReader = new BufferedReader(reader); String line = bufferedReader.readLine(); line = line.substring(1, line.length() - 1); @@ -52,9 +53,8 @@ public Object in(Class target, String path) { return object; } - public void out(T object, String path) { + public ByteArrayOutputStream out(T object) { try { - FileOutputStream fileOutputStream = new FileOutputStream(path); Class domain = object.getClass(); Method[] methods = domain.getDeclaredMethods(); StringBuilder builder = new StringBuilder(); @@ -64,21 +64,21 @@ public void out(T object, String path) { if (method.getName().startsWith("get")) { Class returnType = method.getReturnType(); builder.append(returnType.getName()) - .append(":") - .append(method.getName().substring(3)) - .append(":") - .append(method.invoke(object)) + .append(":").append(method.getName().substring(3)) + .append(":").append(method.invoke(object)) .append(","); } } String result = builder.toString().substring(0, builder.length() - 1) + "]"; - System.out.println(result); - // 使用ObjectOutStream类输出会有奇怪的字符 - fileOutputStream.write(result.getBytes()); - fileOutputStream.close(); - System.out.println("序列化完成"); + + log.info("content={}", result); + + ByteArrayOutputStream byteOutput = new ByteArrayOutputStream(); + byteOutput.write(result.getBytes()); + return byteOutput; } catch (IOException | IllegalAccessException | InvocationTargetException e) { e.printStackTrace(); } + return null; } } diff --git a/java-class/src/main/java/com/github/kuangcp/serialize/json/JsonSmileMigrationService.java b/java-class/src/main/java/com/github/kuangcp/serialize/json/JsonSmileMigrationService.java new file mode 100644 index 00000000..8f6bbaa6 --- /dev/null +++ b/java-class/src/main/java/com/github/kuangcp/serialize/json/JsonSmileMigrationService.java @@ -0,0 +1,52 @@ +package com.github.kuangcp.serialize.json; + +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.dataformat.smile.SmileFactory; +import java.io.ByteArrayOutputStream; +import lombok.extern.slf4j.Slf4j; + +/** + * @author kuangcp on 2019-04-21 10:53 PM + */ +@Slf4j +public class JsonSmileMigrationService { + + public static byte[] convertToSmile(byte[] json, JsonFactory jsonFactory, + SmileFactory smileFactory) { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + + try // try-with-resources + ( + JsonGenerator jg = smileFactory.createGenerator(bos); + JsonParser jp = jsonFactory.createParser(json) + ) { + while (jp.nextToken() != null) { + jg.copyCurrentEvent(jp); + } + } catch (Exception e) { + log.error("Error while converting json to smile", e); + } + + return bos.toByteArray(); + } + + public static byte[] convertToJson(byte[] smile, JsonFactory jsonFactory, + SmileFactory smileFactory) { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + + try ( + JsonParser sp = smileFactory.createParser(smile); + JsonGenerator jg = jsonFactory.createGenerator(bos) + ) { + while (sp.nextToken() != null) { + jg.copyCurrentEvent(sp); + } + } catch (Exception e) { + log.error("Error while converting smile to json", e); + } + + return bos.toByteArray(); + } +} \ No newline at end of file diff --git a/java-class/src/test/java/com/github/kuangcp/serialize/json/speed/FastJsonTool.java b/java-class/src/main/java/com/github/kuangcp/serialize/json/speed/FastJsonTool.java similarity index 74% rename from java-class/src/test/java/com/github/kuangcp/serialize/json/speed/FastJsonTool.java rename to java-class/src/main/java/com/github/kuangcp/serialize/json/speed/FastJsonTool.java index 27cfdcff..b0ff7f32 100644 --- a/java-class/src/test/java/com/github/kuangcp/serialize/json/speed/FastJsonTool.java +++ b/java-class/src/main/java/com/github/kuangcp/serialize/json/speed/FastJsonTool.java @@ -12,12 +12,12 @@ public class FastJsonTool implements JsonTool { @Override - public void read() { - + public void fromJSON(String json, Class target) { + JSON.parseObject(json, target); } @Override - public void write(List dataList) { + public void toJSON(List dataList) { JSON.toJSON(dataList); } diff --git a/java-class/src/test/java/com/github/kuangcp/serialize/json/speed/GsonTool.java b/java-class/src/main/java/com/github/kuangcp/serialize/json/speed/GsonTool.java similarity index 71% rename from java-class/src/test/java/com/github/kuangcp/serialize/json/speed/GsonTool.java rename to java-class/src/main/java/com/github/kuangcp/serialize/json/speed/GsonTool.java index a068deb6..f26c92e5 100644 --- a/java-class/src/test/java/com/github/kuangcp/serialize/json/speed/GsonTool.java +++ b/java-class/src/main/java/com/github/kuangcp/serialize/json/speed/GsonTool.java @@ -13,14 +13,15 @@ @Slf4j public class GsonTool implements JsonTool { - @Override - public void read() { + private Gson gson = new Gson(); + @Override + public void fromJSON(String json, Class target) { + gson.fromJson(json, target); } @Override - public void write(List dataList) { - Gson gson = new Gson(); + public void toJSON(List dataList) { gson.toJson(dataList); } diff --git a/java-class/src/test/java/com/github/kuangcp/serialize/json/speed/JacksonTool.java b/java-class/src/main/java/com/github/kuangcp/serialize/json/speed/JacksonTool.java similarity index 59% rename from java-class/src/test/java/com/github/kuangcp/serialize/json/speed/JacksonTool.java rename to java-class/src/main/java/com/github/kuangcp/serialize/json/speed/JacksonTool.java index c0f49053..53a380a0 100644 --- a/java-class/src/test/java/com/github/kuangcp/serialize/json/speed/JacksonTool.java +++ b/java-class/src/main/java/com/github/kuangcp/serialize/json/speed/JacksonTool.java @@ -3,6 +3,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.github.kuangcp.serialize.Person; +import java.io.IOException; import java.util.List; /** @@ -12,19 +13,16 @@ */ public class JacksonTool implements JsonTool { - @Override - public void read() { + private ObjectMapper mapper = new ObjectMapper(); + @Override + public void fromJSON(String json, Class target) throws IOException { + mapper.readValue(json, target); } @Override - public void write(List dataList) { - ObjectMapper mapper = new ObjectMapper(); - try { - mapper.writeValueAsString(dataList); - } catch (JsonProcessingException e) { - e.printStackTrace(); - } + public void toJSON(List dataList) throws JsonProcessingException { + mapper.writeValueAsString(dataList); } @Override diff --git a/java-class/src/main/java/com/github/kuangcp/serialize/json/speed/JsonTool.java b/java-class/src/main/java/com/github/kuangcp/serialize/json/speed/JsonTool.java new file mode 100644 index 00000000..c4bcf4f0 --- /dev/null +++ b/java-class/src/main/java/com/github/kuangcp/serialize/json/speed/JsonTool.java @@ -0,0 +1,19 @@ +package com.github.kuangcp.serialize.json.speed; + +import com.fasterxml.jackson.core.JsonProcessingException; +import java.io.IOException; +import java.util.List; + +/** + * Created by https://github.com/kuangcp + * + * @author kuangcp + */ +public interface JsonTool { + + void fromJSON(String json, Class target) throws IOException; + + void toJSON(List dataList) throws JsonProcessingException; + + String getName(); +} diff --git a/java-class/src/main/java/com/github/kuangcp/serialize/json/speed/README.md b/java-class/src/main/java/com/github/kuangcp/serialize/json/speed/README.md new file mode 100644 index 00000000..7423e714 --- /dev/null +++ b/java-class/src/main/java/com/github/kuangcp/serialize/json/speed/README.md @@ -0,0 +1,5 @@ +- [ ] JMH + +# 测试生成JSON + +# 测试读取JSON diff --git a/java-class/src/main/java/jmh/StringBuilderBenchmark.java b/java-class/src/main/java/jmh/StringBuilderBenchmark.java new file mode 100644 index 00000000..f7823a63 --- /dev/null +++ b/java-class/src/main/java/jmh/StringBuilderBenchmark.java @@ -0,0 +1,44 @@ +package jmh; + +import java.util.concurrent.TimeUnit; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Threads; +import org.openjdk.jmh.annotations.Warmup; + +/** + * @author kuangcp on 2019-04-21 11:38 PM + */ +@BenchmarkMode(Mode.Throughput) +@Warmup(iterations = 3) +@Measurement(iterations = 10, time = 5) +@Threads(8) +@Fork(2) +@OutputTimeUnit(TimeUnit.MILLISECONDS) +public class StringBuilderBenchmark { + + @Benchmark + public void testStringAdd() { + String a = ""; + for (int i = 0; i < 10; i++) { + a += i; + } + print(a); + } + + @Benchmark + public void testStringBuilderAdd() { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < 10; i++) { + sb.append(i); + } + print(sb.toString()); + } + + private void print(String a) { + } +} \ No newline at end of file diff --git a/java-class/src/main/java/jvm/oom/MethodAreaOOM.java b/java-class/src/main/java/jvm/oom/MethodAreaOOM.java index e946e06d..52b543b8 100644 --- a/java-class/src/main/java/jvm/oom/MethodAreaOOM.java +++ b/java-class/src/main/java/jvm/oom/MethodAreaOOM.java @@ -8,7 +8,8 @@ * -XX:MaxMetaSpaceSize=20M * * MetaSpace 一直缓慢的上涨, 一直在载入新的类, - * 在Jvisualvm中看到的元空间最大是好几个g, 但是metaspace上涨到设置的限制时会抛出OOM + * 在Jvisualvm中看到的元空间最大是好几个g, 但是metaspace上涨到设置的最大限制时会抛出OOM + * * Caused by: java.lang.OutOfMemoryError: Metaspace * * @author kuangcp on 4/5/19-12:56 AM diff --git a/java-class/src/test/java/com/github/kuangcp/serialize/SerializeTest.java b/java-class/src/test/java/com/github/kuangcp/serialize/SerializeTest.java index 888864a2..70c34397 100644 --- a/java-class/src/test/java/com/github/kuangcp/serialize/SerializeTest.java +++ b/java-class/src/test/java/com/github/kuangcp/serialize/SerializeTest.java @@ -23,7 +23,7 @@ @Slf4j public class SerializeTest { - + // 字节数组流 @Test public void testSerializeWithByte() throws IOException, ClassNotFoundException { Person person = new Person("name"); @@ -44,15 +44,15 @@ public void testSerializeWithByte() throws IOException, ClassNotFoundException { @Test public void testSerializeWithFile() { try { - out(); - in(); + writeFile(); + readFile(); } catch (IOException | ClassNotFoundException e) { log.error(e.getMessage(), e); Assert.fail(); } } - private void out() throws IOException { + private void writeFile() throws IOException { Person person = new Person("myth"); FileOutputStream fileOutputStream = new FileOutputStream("/home/kcp/test/person.md"); @@ -63,8 +63,8 @@ private void out() throws IOException { log.debug("序列化完成, person={}", person); } - private void in() throws IOException, ClassNotFoundException { - FileInputStream fileInputStream = new FileInputStream("/home/kcp/test/object.md"); + private void readFile() throws IOException, ClassNotFoundException { + FileInputStream fileInputStream = new FileInputStream("/home/kcp/test/person.md"); ObjectInputStream in = new ObjectInputStream(fileInputStream); Person object = (Person) in.readObject(); in.close(); diff --git a/java-class/src/test/java/com/github/kuangcp/serialize/customserialize/MythSerializeTest.java b/java-class/src/test/java/com/github/kuangcp/serialize/customserialize/MythSerializeTest.java new file mode 100644 index 00000000..df7cde3a --- /dev/null +++ b/java-class/src/test/java/com/github/kuangcp/serialize/customserialize/MythSerializeTest.java @@ -0,0 +1,33 @@ +package com.github.kuangcp.serialize.customserialize; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.IsEqual.equalTo; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.util.Objects; +import org.junit.Test; + +/** + * @author kuangcp on 2019-04-21 11:31 AM + */ +public class MythSerializeTest { + + + @Test + public void test() { + MythSerialize mythSerialize = new MythSerialize<>(); + Myth domain = new Myth(); + domain.setName("myth"); + domain.setPhone("121212121"); + domain.setTest(90909090L); + ByteArrayOutputStream out = mythSerialize.out(domain); + + assert Objects.nonNull(out); + + Myth result = mythSerialize.in(Myth.class, new ByteArrayInputStream(out.toByteArray())); + System.out.println(result.toString()); + + assertThat(result.getName(), equalTo(domain.getName())); + } +} diff --git a/java-class/src/test/java/com/github/kuangcp/serialize/json/GsonTest.java b/java-class/src/test/java/com/github/kuangcp/serialize/json/GsonTest.java index 103304cd..55007a14 100644 --- a/java-class/src/test/java/com/github/kuangcp/serialize/json/GsonTest.java +++ b/java-class/src/test/java/com/github/kuangcp/serialize/json/GsonTest.java @@ -25,7 +25,7 @@ public void testRead() { /** - * read a standard json string + * fromJSON a standard json string */ @Test public void testRead2() { diff --git a/java-class/src/test/java/com/github/kuangcp/serialize/json/SpeedTest.java b/java-class/src/test/java/com/github/kuangcp/serialize/json/SpeedTest.java new file mode 100644 index 00000000..60cf248d --- /dev/null +++ b/java-class/src/test/java/com/github/kuangcp/serialize/json/SpeedTest.java @@ -0,0 +1,45 @@ +package com.github.kuangcp.serialize.json; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.github.kuangcp.serialize.Person; +import com.github.kuangcp.serialize.json.speed.FastJsonTool; +import com.github.kuangcp.serialize.json.speed.GsonTool; +import com.github.kuangcp.serialize.json.speed.JacksonTool; +import com.github.kuangcp.serialize.json.speed.JsonTool; +import com.github.kuangcp.time.GetRunTime; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import org.junit.Test; + +/** + * Created by https://github.com/kuangcp + * + * @author kuangcp + */ +public class SpeedTest { + + private static final int DATA_SIZE = 3000; + + private List> list = Arrays + .asList(new GsonTool(), new JacksonTool(), new FastJsonTool()); + + private List personList = IntStream.range(1, DATA_SIZE) + .mapToObj(i -> new Person("name" + i)).collect(Collectors.toList()); + + @Test + public void compareRead() { + + } + + @Test + public void compareWrite() throws JsonProcessingException { + GetRunTime getRunTime = new GetRunTime(); + for (JsonTool tool : list) { + getRunTime.startCount(); + tool.toJSON(personList); + getRunTime.endCountOneLine(tool.getName()); + } + } +} \ No newline at end of file diff --git a/java-class/src/test/java/com/github/kuangcp/serialize/json/speed/JsonTool.java b/java-class/src/test/java/com/github/kuangcp/serialize/json/speed/JsonTool.java deleted file mode 100644 index d9741504..00000000 --- a/java-class/src/test/java/com/github/kuangcp/serialize/json/speed/JsonTool.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.github.kuangcp.serialize.json.speed; - -import java.util.List; - -/** - * Created by https://github.com/kuangcp - * - * @author kuangcp - */ -public interface JsonTool { - - void read(); - - void write(List dataList); - - String getName(); -} diff --git a/java-class/src/test/java/com/github/kuangcp/serialize/json/speed/README.md b/java-class/src/test/java/com/github/kuangcp/serialize/json/speed/README.md deleted file mode 100644 index df50f7bd..00000000 --- a/java-class/src/test/java/com/github/kuangcp/serialize/json/speed/README.md +++ /dev/null @@ -1,93 +0,0 @@ -# 硬件配置 -> 内存 8G 2400MHZ -> CPU 8核 i7-7700 - -# 测试生成JSON - -> 2000000 个 Person 对象, 启动测试 直接吃掉 1.7G 内存 - -测试结果如下 - -``` -第一次 ->>>>>> GSON -耗时:<2581ms> -格式:<0h:0m:2s:581ms> <<<<<< ->>>>>> Jackson -耗时:<1292ms> -格式:<0h:0m:1s:292ms> <<<<<< ->>>>>> FastJson -耗时:<7237ms> -格式:<0h:0m:7s:237ms> <<<<<< - -第二次 ->>>>>> GSON -耗时:<2498ms> -格式:<0h:0m:2s:498ms> <<<<<< ->>>>>> Jackson -耗时:<1316ms> -格式:<0h:0m:1s:316ms> <<<<<< ->>>>>> FastJson -耗时:<7548ms> -格式:<0h:0m:7s:548ms> <<<<<< - -第三次 ->>>>>> GSON -耗时:<2465ms> -格式:<0h:0m:2s:465ms> <<<<<< ->>>>>> Jackson -耗时:<1483ms> -格式:<0h:0m:1s:483ms> <<<<<< ->>>>>> FastJson -耗时:<7432ms> -格式:<0h:0m:7s:432ms> <<<<<< -``` - -> 2000 个对象 -``` ->>>>>> GSON -耗时:<51ms> -格式:<0h:0m:0s:51ms> <<<<<< ->>>>>> Jackson -耗时:<176ms> -格式:<0h:0m:0s:176ms> <<<<<< ->>>>>> FastJson -耗时:<126ms> -格式:<0h:0m:0s:126ms> <<<<<< - - ->>>>>> GSON -耗时:<53ms> -格式:<0h:0m:0s:53ms> <<<<<< ->>>>>> Jackson -耗时:<170ms> -格式:<0h:0m:0s:170ms> <<<<<< ->>>>>> FastJson -耗时:<166ms> -格式:<0h:0m:0s:166ms> <<<<<< - ->>>>>> GSON -耗时:<57ms> -格式:<0h:0m:0s:57ms> <<<<<< ->>>>>> Jackson -耗时:<178ms> -格式:<0h:0m:0s:178ms> <<<<<< ->>>>>> FastJson -耗时:<129ms> -格式:<0h:0m:0s:129ms> <<<<<< - - ->>>>>> GSON -耗时:<49ms> -格式:<0h:0m:0s:49ms> <<<<<< ->>>>>> Jackson -耗时:<160ms> -格式:<0h:0m:0s:160ms> <<<<<< ->>>>>> FastJson -耗时:<126ms> -格式:<0h:0m:0s:126ms> <<<<<< -``` - -# 测试读取JSON - - diff --git a/java-class/src/test/java/com/github/kuangcp/serialize/json/speed/SpeedTest.java b/java-class/src/test/java/com/github/kuangcp/serialize/json/speed/SpeedTest.java deleted file mode 100644 index 5e7d7e65..00000000 --- a/java-class/src/test/java/com/github/kuangcp/serialize/json/speed/SpeedTest.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.github.kuangcp.serialize.json.speed; - -import com.github.kuangcp.serialize.Person; -import com.github.kuangcp.time.GetRunTime; -import java.util.Arrays; -import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.IntStream; -import org.junit.Test; - -/** - * Created by https://github.com/kuangcp - * - * @author kuangcp - */ -public class SpeedTest { - - private List> list = Arrays - .asList(new GsonTool(), new JacksonTool(), new FastJsonTool()); - - private List personList = IntStream.range(1, 2000) - .mapToObj(i -> new Person("name" + i)).collect(Collectors.toList()); - - @Test - public void compareRead() { - - } - - @Test - public void compareWrite() { - for (JsonTool tool : list) { - GetRunTime getRunTime = new GetRunTime().startCount(); - - tool.write(personList); - - getRunTime.endCount(tool.getName()); - } - } -} \ No newline at end of file diff --git a/java-class/src/test/java/jmh/StringBuilderBenchmarkTest.java b/java-class/src/test/java/jmh/StringBuilderBenchmarkTest.java new file mode 100644 index 00000000..ec57f585 --- /dev/null +++ b/java-class/src/test/java/jmh/StringBuilderBenchmarkTest.java @@ -0,0 +1,21 @@ +package jmh; + +import org.junit.Test; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +/** + * @author kuangcp on 2019-04-21 11:39 PM + */ +public class StringBuilderBenchmarkTest { + + @Test + public void test() throws RunnerException { + Options options = new OptionsBuilder() + .include(StringBuilderBenchmark.class.getSimpleName()) + .output("/home/kcp/test/jmh/Benchmark.log").build(); + new Runner(options).run(); + } +} diff --git a/java-concurrency/src/test/java/com/github/kuangcp/lock/pvp/PlayerTest.java b/java-concurrency/src/test/java/com/github/kuangcp/lock/pvp/PlayerTest.java new file mode 100644 index 00000000..62b11321 --- /dev/null +++ b/java-concurrency/src/test/java/com/github/kuangcp/lock/pvp/PlayerTest.java @@ -0,0 +1,41 @@ +package com.github.kuangcp.lock.pvp; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.ReentrantLock; +import org.junit.jupiter.api.Test; + +/** + * @author kuangcp on 2019-04-21 11:14 AM + */ +public class PlayerTest { + + private ReentrantLock lock = new ReentrantLock(); + + // TODO 锁 + @Test + public void testRead() throws InterruptedException { + + Thread thread = new Thread(() -> { + for (int i = 0; i < 40; i++) { + try { + Thread.sleep(100); + } catch (InterruptedException e) { + e.printStackTrace(); + } + lock.lock(); + } + }); + thread.setDaemon(true); + thread.start(); + + TimeUnit.SECONDS.sleep(2); + try { + // 如果这里获取锁失败, finally 里又去释放锁, 会得到IllegalMonitorStateException, 掩盖了这里获取锁的异常 + lock.lock(); + } catch (Exception e) { + e.printStackTrace(); + } finally { + lock.unlock(); + } + } +} From 2490760de66609e6405132e139bf833400d3640b Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Mon, 22 Apr 2019 11:57:20 +0800 Subject: [PATCH 013/476] +) generic with array --- .../java/com/github/kuangcp/ArrayMaker.java | 32 -------- .../java/com/github/kuangcp/ArrayUtils.java | 26 +++++++ ...leMethod.java => SimpleGenericMethod.java} | 4 +- .../com/github/kuangcp/ArrayUtilsTest.java | 40 ++++++++++ ...Test.java => SimpleGenericMethodTest.java} | 6 +- .../ShowCreateThreadForSimpleMain.java | 3 +- ...rupted.java => ThreadInterruptedDemo.java} | 4 +- .../com/github/kuangcp/ThreadJoinDemo.java | 46 ++++++++++++ .../github/kuangcp/ThreadStatusTransfer.java | 74 +++++++++---------- .../kuangcp/ThreadStatusTransferTest.java | 23 ++++++ 10 files changed, 176 insertions(+), 82 deletions(-) delete mode 100644 java-generic/src/main/java/com/github/kuangcp/ArrayMaker.java create mode 100644 java-generic/src/main/java/com/github/kuangcp/ArrayUtils.java rename java-generic/src/main/java/com/github/kuangcp/simple/{SimpleMethod.java => SimpleGenericMethod.java} (90%) create mode 100644 java-generic/src/test/java/com/github/kuangcp/ArrayUtilsTest.java rename java-generic/src/test/java/com/github/kuangcp/simple/{SimpleMethodTest.java => SimpleGenericMethodTest.java} (85%) rename java-thread/src/main/java/com/github/kuangcp/{ShowInterrupted.java => ThreadInterruptedDemo.java} (86%) create mode 100644 java-thread/src/main/java/com/github/kuangcp/ThreadJoinDemo.java create mode 100644 java-thread/src/test/java/com/github/kuangcp/ThreadStatusTransferTest.java diff --git a/java-generic/src/main/java/com/github/kuangcp/ArrayMaker.java b/java-generic/src/main/java/com/github/kuangcp/ArrayMaker.java deleted file mode 100644 index a43f97af..00000000 --- a/java-generic/src/main/java/com/github/kuangcp/ArrayMaker.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.github.kuangcp; - -import java.lang.reflect.Array; -import java.util.Arrays; - -/** - * 泛型类中创建, 类型变量类型的数组,使用Array.newInstance()是推荐的方式 - * 那么create方法就能创建类型变量约束的数组, 初始化为空 - * 暂时想到的用途就是泛型约束的创建 一个数组出来 - */ -public class ArrayMaker { - - private Class kind; - - private ArrayMaker(Class kind) { - this.kind = kind; - } - - @SuppressWarnings("unchecked") - private T[] create(int size) { - return (T[]) Array.newInstance(kind, size); - } - - public static void main(String[] args) { - ArrayMaker stringMaker = new ArrayMaker<>(String.class); - String[] stringArray = stringMaker.create(9); - System.out.println(Arrays.toString(stringArray)); - } -} -/* Output: -[null, null, null, null, null, null, null, null, null] -*/ diff --git a/java-generic/src/main/java/com/github/kuangcp/ArrayUtils.java b/java-generic/src/main/java/com/github/kuangcp/ArrayUtils.java new file mode 100644 index 00000000..76b0a72e --- /dev/null +++ b/java-generic/src/main/java/com/github/kuangcp/ArrayUtils.java @@ -0,0 +1,26 @@ +package com.github.kuangcp; + +import java.lang.reflect.Array; +import java.util.Arrays; +import java.util.List; + +@SuppressWarnings("unchecked") +public class ArrayUtils { + + /** + * 泛型类中创建, 类型变量类型的数组,使用Array.newInstance()是推荐的方式 + */ + static T[] create(Class target, int size) { + return (T[]) Array.newInstance(target, size); + } + + static > List sort(List list) { + return Arrays.asList(list.toArray((T[]) new Comparable[list.size()])); + } + + // 字节码中看到的是返回值强转为 (Comparable[]) 实际上不能满足调用方的需求, 这个接口必然报错 + // TODO 为什么这么做 不强转为 T[] ? + static > T[] sortToArray(List list) { + return list.toArray((T[]) new Comparable[list.size()]); + } +} diff --git a/java-generic/src/main/java/com/github/kuangcp/simple/SimpleMethod.java b/java-generic/src/main/java/com/github/kuangcp/simple/SimpleGenericMethod.java similarity index 90% rename from java-generic/src/main/java/com/github/kuangcp/simple/SimpleMethod.java rename to java-generic/src/main/java/com/github/kuangcp/simple/SimpleGenericMethod.java index 8efae464..c66416a9 100644 --- a/java-generic/src/main/java/com/github/kuangcp/simple/SimpleMethod.java +++ b/java-generic/src/main/java/com/github/kuangcp/simple/SimpleGenericMethod.java @@ -8,11 +8,9 @@ * * @author kuangcp */ -class SimpleMethod { +class SimpleGenericMethod { /** - * 简单的泛型方法 可以取代掉以往的Object的工具类的方法 - * * @param list 对象集合 * @param 类型必须是对象, 即使是基本数据类型,也要使用对应的包装类 * @return 返回中间的对象 diff --git a/java-generic/src/test/java/com/github/kuangcp/ArrayUtilsTest.java b/java-generic/src/test/java/com/github/kuangcp/ArrayUtilsTest.java new file mode 100644 index 00000000..096f70b2 --- /dev/null +++ b/java-generic/src/test/java/com/github/kuangcp/ArrayUtilsTest.java @@ -0,0 +1,40 @@ +package com.github.kuangcp; + +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; + +/** + * @author kuangcp on 2019-04-22 11:39 AM + */ +@Slf4j +public class ArrayUtilsTest { + + @Test + public void testCreateArray() { + String[] stringArray = ArrayUtils.create(String.class, 9); + + System.out.println(Arrays.toString(stringArray)); + + assert Arrays.stream(stringArray).allMatch(Objects::isNull); + } + + @Test + public void testSort() { + List list = Arrays.asList(3, 2, 5); + List result = ArrayUtils.sort(list); + + log.info("result={}", result); + } + + @Test + public void testSortToArray() { + List list = Arrays.asList(3, 2, 5); + // ArrayUtils.sortToArray(list) 实际返回的是 (Comparable[]) 但是这里需要强转为 Integer[] 这个转型失败了 + Integer[] result = ArrayUtils.sortToArray(list); + + log.info("result={}", Arrays.toString(result)); + } +} diff --git a/java-generic/src/test/java/com/github/kuangcp/simple/SimpleMethodTest.java b/java-generic/src/test/java/com/github/kuangcp/simple/SimpleGenericMethodTest.java similarity index 85% rename from java-generic/src/test/java/com/github/kuangcp/simple/SimpleMethodTest.java rename to java-generic/src/test/java/com/github/kuangcp/simple/SimpleGenericMethodTest.java index bc6b2d16..df0cd8f0 100644 --- a/java-generic/src/test/java/com/github/kuangcp/simple/SimpleMethodTest.java +++ b/java-generic/src/test/java/com/github/kuangcp/simple/SimpleGenericMethodTest.java @@ -8,7 +8,7 @@ * * @author kuangcp */ -public class SimpleMethodTest { +public class SimpleGenericMethodTest { /** * 和核心技术卷上不一样,应该是版本的问题, 入参就已经限制成数组了,怎么可能会有别的类型混进来 @@ -17,7 +17,7 @@ public class SimpleMethodTest { @Test public void testGetMiddle() { Float[] arrays = {2.1f, 4.2f, 3.5f}; - Float result = SimpleMethod.getMiddle(arrays); + Float result = SimpleGenericMethod.getMiddle(arrays); assert result == 4.2f; } @@ -31,7 +31,7 @@ public void testGetMax() { new Score(70f, 80f), new Score(100f, 70f) }; - Score result = SimpleMethod.getMax(scores); + Score result = SimpleGenericMethod.getMax(scores); assert result.getNormal() == 100; } } \ No newline at end of file diff --git a/java-thread/src/main/java/com/github/kuangcp/ShowCreateThreadForSimpleMain.java b/java-thread/src/main/java/com/github/kuangcp/ShowCreateThreadForSimpleMain.java index 8d8b8bbc..5d086b0f 100644 --- a/java-thread/src/main/java/com/github/kuangcp/ShowCreateThreadForSimpleMain.java +++ b/java-thread/src/main/java/com/github/kuangcp/ShowCreateThreadForSimpleMain.java @@ -18,12 +18,11 @@ public static void main(String[] args) { log.info("id={} name={}", info.getThreadId(), info.getThreadName()); } } + // 预期输出 //name=Monitor Ctrl-Break //name=Signal Dispatcher 分发处理发送给JVM进程信号 //name=Finalizer 调用对象 finalize() //name=Reference Handler 清除 Reference //name=main - - } diff --git a/java-thread/src/main/java/com/github/kuangcp/ShowInterrupted.java b/java-thread/src/main/java/com/github/kuangcp/ThreadInterruptedDemo.java similarity index 86% rename from java-thread/src/main/java/com/github/kuangcp/ShowInterrupted.java rename to java-thread/src/main/java/com/github/kuangcp/ThreadInterruptedDemo.java index 70ac10de..fb1b3f02 100644 --- a/java-thread/src/main/java/com/github/kuangcp/ShowInterrupted.java +++ b/java-thread/src/main/java/com/github/kuangcp/ThreadInterruptedDemo.java @@ -7,7 +7,7 @@ * @author kuangcp on 2019-04-19 9:07 PM */ @Slf4j -public class ShowInterrupted { +public class ThreadInterruptedDemo { public static void main(String[] args) throws InterruptedException { Thread sleep = new Thread(new SleepRunner(), "Sleep"); @@ -23,6 +23,8 @@ public static void main(String[] args) throws InterruptedException { busy.interrupt(); log.info("sleep={}", sleep.isInterrupted()); log.info("busy={}", busy.isInterrupted()); + + // sleep 方法收到中断信号后, 会抛出中断异常并返回 false 所以这里的输出 是 false true } static class SleepRunner implements Runnable { diff --git a/java-thread/src/main/java/com/github/kuangcp/ThreadJoinDemo.java b/java-thread/src/main/java/com/github/kuangcp/ThreadJoinDemo.java new file mode 100644 index 00000000..11a678d7 --- /dev/null +++ b/java-thread/src/main/java/com/github/kuangcp/ThreadJoinDemo.java @@ -0,0 +1,46 @@ +package com.github.kuangcp; + +import java.util.concurrent.TimeUnit; +import lombok.extern.slf4j.Slf4j; + +/** + * @author kuangcp on 2019-04-22 9:55 AM + */ +@Slf4j +public class ThreadJoinDemo { + + static class Domino implements Runnable { + + private Thread thread; + + public Domino(Thread thread) { + this.thread = thread; + } + + @Override + public void run() { + try { + thread.join(); + } catch (InterruptedException e) { + log.error(e.getMessage(), e); + } + + log.info("{} terminate.", Thread.currentThread().getName()); + } + + } + + // 每个线程的需要等待前驱线程执行完成才能退出 + public static void main(String[] args) throws InterruptedException { + Thread previous = Thread.currentThread(); + + for (int i = 0; i < 10; i++) { + Thread thread = new Thread(new Domino(previous), i + ""); + thread.start(); + previous = thread; + + } + TimeUnit.SECONDS.sleep(3); + log.info("{} terminate.", Thread.currentThread().getName()); + } +} diff --git a/java-thread/src/main/java/com/github/kuangcp/ThreadStatusTransfer.java b/java-thread/src/main/java/com/github/kuangcp/ThreadStatusTransfer.java index 8809ec5d..bc6fd0d6 100644 --- a/java-thread/src/main/java/com/github/kuangcp/ThreadStatusTransfer.java +++ b/java-thread/src/main/java/com/github/kuangcp/ThreadStatusTransfer.java @@ -1,64 +1,56 @@ package com.github.kuangcp; +import lombok.extern.slf4j.Slf4j; + /** * Created by https://github.com/kuangcp on 17-8-20 下午8:46 - * TODO 线程的生命周期,状态转化 + * 1. 使用 wait notify notifyAll 需要对调用对象加锁 + * 2. 调用 wait 后 线程状态Running变为Waiting, 将当前线程放入对象的等待队列上 + * 3. notify 调用后, 等待线程 wait 处 不会立即返回, 需要等到 notify 这个线程释放锁后才有机会 + * 4. notify 调用后 将等待线程从等待队列放入同步队列, 线程状态 Waiting 变成 Blocked */ +@Slf4j public class ThreadStatusTransfer { - class ThisThread extends Thread { + private static boolean flag = true; + + private static Object lock = new Object(); - private boolean runFlag; + static class Wait implements Runnable { @Override public void run() { - while (runFlag) { - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - System.out.println("Running"); - synchronized (this) { - System.out.println("同步块"); + // IDE 警告: + // synchronized 一个非 final 的变量, 容易发生 当该对象的引用地址更改后, 同步块里的代码能被并行运行, 因为锁的是真实堆上的对象 + synchronized (lock) { + while (flag) { try { - wait(); + log.info("flag is true: start wait"); + lock.wait(); } catch (InterruptedException e) { - e.printStackTrace(); + log.error(e.getMessage(), e); } - setRunFlag(false); - notifyAll(); } + log.info("flag is false. running"); } } - - public boolean isRunFlag() { - return runFlag; - } - - public void setRunFlag(boolean runFlag) { - this.runFlag = runFlag; - } } - public static void main(String[] a) { - ThreadStatusTransfer transfer = new ThreadStatusTransfer(); - transfer.test(); - } + static class Notify implements Runnable { - public void test() { - ThisThread thisThread = new ThreadStatusTransfer().new ThisThread(); - thisThread.setRunFlag(true); - thisThread.start(); - try { - Thread.sleep(2000); - System.out.println("main"); -// ThreadStatusTransfer.class.notify(); - } catch (InterruptedException e) { - e.printStackTrace(); + @Override + public void run() { + synchronized (lock) { + log.info("hold lock. notify"); + lock.notify(); + flag = false; + } + + // 这一段就是重新获取锁, 会和wait进行竞争, 所以执行顺序不定 + synchronized (lock) { + log.info("hold lock again"); + } } - System.out.println("结束?"); - System.out.println(thisThread.isRunFlag()); - thisThread.setRunFlag(false); } + } diff --git a/java-thread/src/test/java/com/github/kuangcp/ThreadStatusTransferTest.java b/java-thread/src/test/java/com/github/kuangcp/ThreadStatusTransferTest.java new file mode 100644 index 00000000..0de36fc4 --- /dev/null +++ b/java-thread/src/test/java/com/github/kuangcp/ThreadStatusTransferTest.java @@ -0,0 +1,23 @@ +package com.github.kuangcp; + +import com.github.kuangcp.ThreadStatusTransfer.Notify; +import com.github.kuangcp.ThreadStatusTransfer.Wait; +import java.util.concurrent.TimeUnit; +import org.junit.Test; + +/** + * @author kuangcp on 2019-04-22 9:40 AM + */ +public class ThreadStatusTransferTest { + + @Test + public void testMain() throws InterruptedException { + Thread waitThread = new Thread(new Wait(), "WaitThread"); + waitThread.start(); + + TimeUnit.SECONDS.sleep(1); + + Thread notifyThread = new Thread(new Notify(), "NotifyThread"); + notifyThread.start(); + } +} From 79eb874be09906eccfa773af8c175790a3a5be23 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Mon, 22 Apr 2019 17:24:37 +0800 Subject: [PATCH 014/476] x) learn NIO on computer and Java NIO --- .../kuangcp/nio/{selector => }/EchoNIOServer.java | 4 ++-- .../nio/{selector/NioClient.java => NIOClient.java} | 12 ++++++------ .../nio/{selector/NioServer.java => NIOServer.java} | 6 +++--- .../com/github/kuangcp/singleton/DoubleCheck.java | 8 +++++++- .../main/java/com/github/kuangcp/singleton/Readme.md | 9 ++++++++- .../com/github/kuangcp/singleton/StaticInit.java | 2 +- .../github/kuangcp/singleton/StaticInitWithSync.java | 2 +- .../github/kuangcp/singleton/StaticInnerClass.java | 2 +- 8 files changed, 29 insertions(+), 16 deletions(-) rename java-network/src/main/java/com/github/kuangcp/nio/{selector => }/EchoNIOServer.java (96%) rename java-network/src/main/java/com/github/kuangcp/nio/{selector/NioClient.java => NIOClient.java} (88%) rename java-network/src/main/java/com/github/kuangcp/nio/{selector/NioServer.java => NIOServer.java} (98%) diff --git a/java-network/src/main/java/com/github/kuangcp/nio/selector/EchoNIOServer.java b/java-network/src/main/java/com/github/kuangcp/nio/EchoNIOServer.java similarity index 96% rename from java-network/src/main/java/com/github/kuangcp/nio/selector/EchoNIOServer.java rename to java-network/src/main/java/com/github/kuangcp/nio/EchoNIOServer.java index 5e66d8ca..0a56fc81 100644 --- a/java-network/src/main/java/com/github/kuangcp/nio/selector/EchoNIOServer.java +++ b/java-network/src/main/java/com/github/kuangcp/nio/EchoNIOServer.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.nio.selector; +package com.github.kuangcp.nio; import java.io.IOException; import java.net.InetSocketAddress; @@ -26,7 +26,7 @@ public void start() throws IOException { //通过OPEN方法来打开一个未绑定的ServerSocketChannel 实例 ServerSocketChannel server = ServerSocketChannel.open(); //将该ServerSocketChannel绑定到指定ip - server.bind(new InetSocketAddress(NioServer.PORT)); + server.bind(new InetSocketAddress(NIOServer.PORT)); //设置是NIO 非阻塞模式 server.configureBlocking(false); diff --git a/java-network/src/main/java/com/github/kuangcp/nio/selector/NioClient.java b/java-network/src/main/java/com/github/kuangcp/nio/NIOClient.java similarity index 88% rename from java-network/src/main/java/com/github/kuangcp/nio/selector/NioClient.java rename to java-network/src/main/java/com/github/kuangcp/nio/NIOClient.java index c8775fe6..5a24915f 100644 --- a/java-network/src/main/java/com/github/kuangcp/nio/selector/NioClient.java +++ b/java-network/src/main/java/com/github/kuangcp/nio/NIOClient.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.nio.selector; +package com.github.kuangcp.nio; import java.io.IOException; import java.net.InetSocketAddress; @@ -13,19 +13,19 @@ * Created by Myth on 2017/4/5 0005 */ @Slf4j -public class NioClient { +class NIOClient { private Selector selector = null; private boolean stop = false; public static void main(String[] s) throws Exception { - new NioClient().init(); + new NIOClient().init(); } private void init() throws IOException { selector = Selector.open(); - InetSocketAddress address = new InetSocketAddress(NioServer.PORT); + InetSocketAddress address = new InetSocketAddress(NIOServer.PORT); SocketChannel channel = SocketChannel.open(address); channel.configureBlocking(false); channel.register(selector, SelectionKey.OP_READ); @@ -36,7 +36,7 @@ private void init() throws IOException { try { while (scan.hasNextLine()) { String line = scan.nextLine(); - channel.write(NioServer.charset.encode(line)); + channel.write(NIOServer.charset.encode(line)); if ("exit".equalsIgnoreCase(line)) { stop(); @@ -81,7 +81,7 @@ private void readContent(SelectionKey sk) throws IOException { while (sc.read(buff) > 0) { sc.read(buff); buff.flip(); - content.append(NioServer.charset.decode(buff)); + content.append(NIOServer.charset.decode(buff)); } if (content.length() == 0) { return; diff --git a/java-network/src/main/java/com/github/kuangcp/nio/selector/NioServer.java b/java-network/src/main/java/com/github/kuangcp/nio/NIOServer.java similarity index 98% rename from java-network/src/main/java/com/github/kuangcp/nio/selector/NioServer.java rename to java-network/src/main/java/com/github/kuangcp/nio/NIOServer.java index 64fd8a2c..bb231e07 100644 --- a/java-network/src/main/java/com/github/kuangcp/nio/selector/NioServer.java +++ b/java-network/src/main/java/com/github/kuangcp/nio/NIOServer.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.nio.selector; +package com.github.kuangcp.nio; import com.github.kuangcp.io.ResourceTool; import java.io.IOException; @@ -25,7 +25,7 @@ * 原先的BIO是 1:1 */ @Slf4j -public class NioServer { +class NIOServer { static final int PORT = 30000; @@ -35,7 +35,7 @@ public class NioServer { private volatile boolean stop = false; public static void main(String[] s) throws Exception { - new NioServer().start(); + new NIOServer().start(); } // selector 模型 轮询 diff --git a/java-pattern/src/main/java/com/github/kuangcp/singleton/DoubleCheck.java b/java-pattern/src/main/java/com/github/kuangcp/singleton/DoubleCheck.java index 73476de5..443fc981 100644 --- a/java-pattern/src/main/java/com/github/kuangcp/singleton/DoubleCheck.java +++ b/java-pattern/src/main/java/com/github/kuangcp/singleton/DoubleCheck.java @@ -1,7 +1,7 @@ package com.github.kuangcp.singleton; /** - * 双重校验 (线程安全,同步代码块)[不可用] + * 双重校验方式 线程不安全 * * @author kuangcp on 2019-04-11 12:41 PM */ @@ -15,6 +15,12 @@ private DoubleCheck() { public static DoubleCheck getInstance() { if (singleton == null) { synchronized (DoubleCheck.class) { + // 因为 new 这个操作是分为三步 + // 1.分配内存空间, 2.初始化对象, 3.singleton 引用变量指向内存空间 + // 2 3 步可能发生指令重排序 + + // 线程A执行1、3后让出cpu,此时还未执行2,别的线程拿到cpu,发现instance不为null,直接返回使用,而此时 instance还未初始化。 + singleton = new DoubleCheck(); } } diff --git a/java-pattern/src/main/java/com/github/kuangcp/singleton/Readme.md b/java-pattern/src/main/java/com/github/kuangcp/singleton/Readme.md index fcdd6fb3..a6116047 100644 --- a/java-pattern/src/main/java/com/github/kuangcp/singleton/Readme.md +++ b/java-pattern/src/main/java/com/github/kuangcp/singleton/Readme.md @@ -1,7 +1,14 @@ # 单例模式 -- 双重校验加上 volatile +推荐: +- 双重校验 (DLC: Double Lock Check) 并加上 volatile 修饰 - 静态内部类 - 枚举 https://www.cnblogs.com/zhaoyan001/p/6365064.html +饿汉式: 静态常量, 静态代码块, 枚举 +懒汉式: 静态内部类 + +以上除了 加强的DLC的方式 都是属于借助Class类加载机制实现线程安全 +因为 ClassLoader 中 loadClass 方法是 synchronized 修饰的, 除非被重写 +所以即使没有显式的使用 synchronized, 但是底层实现上使用了 diff --git a/java-pattern/src/main/java/com/github/kuangcp/singleton/StaticInit.java b/java-pattern/src/main/java/com/github/kuangcp/singleton/StaticInit.java index 0c8fb6bc..d8a5212a 100644 --- a/java-pattern/src/main/java/com/github/kuangcp/singleton/StaticInit.java +++ b/java-pattern/src/main/java/com/github/kuangcp/singleton/StaticInit.java @@ -1,7 +1,7 @@ package com.github.kuangcp.singleton; /** - * 懒汉式(线程不安全)[不可用] + * 懒汉式 (线程不安全)[不可用] * * @author kuangcp on 2019-04-11 12:39 PM */ diff --git a/java-pattern/src/main/java/com/github/kuangcp/singleton/StaticInitWithSync.java b/java-pattern/src/main/java/com/github/kuangcp/singleton/StaticInitWithSync.java index a1aabaeb..602b3575 100644 --- a/java-pattern/src/main/java/com/github/kuangcp/singleton/StaticInitWithSync.java +++ b/java-pattern/src/main/java/com/github/kuangcp/singleton/StaticInitWithSync.java @@ -1,7 +1,7 @@ package com.github.kuangcp.singleton; /** - * 懒汉式(线程安全,同步方法)[不推荐用] + * 懒汉式 (线程安全,同步方法) [不推荐用] 有额外的锁, 同步消耗 * * @author kuangcp on 2019-04-11 12:40 PM */ diff --git a/java-pattern/src/main/java/com/github/kuangcp/singleton/StaticInnerClass.java b/java-pattern/src/main/java/com/github/kuangcp/singleton/StaticInnerClass.java index a16b1fec..16ba786f 100644 --- a/java-pattern/src/main/java/com/github/kuangcp/singleton/StaticInnerClass.java +++ b/java-pattern/src/main/java/com/github/kuangcp/singleton/StaticInnerClass.java @@ -1,7 +1,7 @@ package com.github.kuangcp.singleton; /** - * 静态内部类[推荐用] + * 静态内部类 [推荐用] * * @author kuangcp on 2019-04-11 12:42 PM */ From 1be50f3cccb31ef6c31205f7661eb4fa9663d58e Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Mon, 22 Apr 2019 17:56:49 +0800 Subject: [PATCH 015/476] *) extract proxy for aop --- java-pattern/src/main/resources/logback.xml | 82 +++++++++++++++++++ .../github/kuangcp/proxy/dao/base/Person.java | 11 +++ .../kuangcp/proxy/dao/base/Transaction.java | 15 ++++ .../proxy/dao/cglibproxy/PersonDaoImpl.java | 34 ++++++++ .../dao/cglibproxy/PersonDaoInterceptor.java} | 5 +- .../kuangcp/proxy/dao/jdkproxy/PersonDao.java | 1 + .../proxy/dao/jdkproxy/PersonDaoImpl.java | 19 ++--- .../dao/jdkproxy/PersonDaoInterceptor.java} | 9 +- java-spring/src/main/resources/logback.xml | 82 +++++++++++++++++++ .../proxy/dao/cglibproxy/CGlibProxyTest.java | 16 ++++ .../kuangcp/proxy/dao/cglibproxy/Person.java | 23 ------ .../proxy/dao/cglibproxy/PersonDaoImpl.java | 35 -------- .../proxy/dao/cglibproxy/PersonDaoTest.java | 15 ---- .../proxy/dao/cglibproxy/Transaction.java | 11 --- .../{PersonDaoTest.java => JDKProxyTest.java} | 9 +- .../kuangcp/proxy/dao/jdkproxy/Person.java | 23 ------ .../kuangcp/proxy/dao/jdkproxy/Readme.md | 16 ++-- .../proxy/dao/jdkproxy/Transaction.java | 12 --- 18 files changed, 270 insertions(+), 148 deletions(-) create mode 100644 java-pattern/src/main/resources/logback.xml create mode 100644 java-spring/src/main/java/com/github/kuangcp/proxy/dao/base/Person.java create mode 100644 java-spring/src/main/java/com/github/kuangcp/proxy/dao/base/Transaction.java create mode 100644 java-spring/src/main/java/com/github/kuangcp/proxy/dao/cglibproxy/PersonDaoImpl.java rename java-spring/src/{test/java/com/github/kuangcp/proxy/dao/cglibproxy/PersonDaoInteceptor.java => main/java/com/github/kuangcp/proxy/dao/cglibproxy/PersonDaoInterceptor.java} (86%) rename java-spring/src/{test => main}/java/com/github/kuangcp/proxy/dao/jdkproxy/PersonDao.java (83%) rename java-spring/src/{test => main}/java/com/github/kuangcp/proxy/dao/jdkproxy/PersonDaoImpl.java (58%) rename java-spring/src/{test/java/com/github/kuangcp/proxy/dao/jdkproxy/PersonDaoInteceptor.java => main/java/com/github/kuangcp/proxy/dao/jdkproxy/PersonDaoInterceptor.java} (81%) create mode 100644 java-spring/src/main/resources/logback.xml create mode 100644 java-spring/src/test/java/com/github/kuangcp/proxy/dao/cglibproxy/CGlibProxyTest.java delete mode 100644 java-spring/src/test/java/com/github/kuangcp/proxy/dao/cglibproxy/Person.java delete mode 100644 java-spring/src/test/java/com/github/kuangcp/proxy/dao/cglibproxy/PersonDaoImpl.java delete mode 100644 java-spring/src/test/java/com/github/kuangcp/proxy/dao/cglibproxy/PersonDaoTest.java delete mode 100644 java-spring/src/test/java/com/github/kuangcp/proxy/dao/cglibproxy/Transaction.java rename java-spring/src/test/java/com/github/kuangcp/proxy/dao/jdkproxy/{PersonDaoTest.java => JDKProxyTest.java} (74%) delete mode 100644 java-spring/src/test/java/com/github/kuangcp/proxy/dao/jdkproxy/Person.java delete mode 100644 java-spring/src/test/java/com/github/kuangcp/proxy/dao/jdkproxy/Transaction.java diff --git a/java-pattern/src/main/resources/logback.xml b/java-pattern/src/main/resources/logback.xml new file mode 100644 index 00000000..5e375431 --- /dev/null +++ b/java-pattern/src/main/resources/logback.xml @@ -0,0 +1,82 @@ + + + + + + + + %d{HH:mm:ss.SSS} %highlight(%-5level) [%thread] %logger{30}:%yellow(%-3L) %msg%n + + + DEBUG + + + + + + ${log.base}debug.log + true + + ${log.base}debug.%d{yyyy-MM-dd}.log + + + %d{MM-dd HH:mm:ss.SSS} |-%-5level |%thread| %logger{36}:%L - %msg%n + + + DEBUG + + + + + + ${log.base}warn.log + true + + ${log.base}warn.%d{yyyy-MM-dd}.log + + + %d{MM-dd HH:mm:ss.SSS} |-%-5level %logger{36}:%L - %msg%n + + + WARN + + + + + + ${log.base}info.log + true + + ${log.base}info.%d{yyyy-MM-dd}.log + + + %d{MM-dd HH:mm:ss.SSS} |-%-5level %logger{36}:%L - %msg%n + + + INFO + + + + + + ${log.base}error.log + true + + ${log.base}error.%d{yyyy-MM-dd}.log + + + %d{MM-dd HH:mm:ss.SSS} | %thread |-%-5level %logger{36}:%L - %msg%n + + + ERROR + + + + + + + + + + + \ No newline at end of file diff --git a/java-spring/src/main/java/com/github/kuangcp/proxy/dao/base/Person.java b/java-spring/src/main/java/com/github/kuangcp/proxy/dao/base/Person.java new file mode 100644 index 00000000..76999002 --- /dev/null +++ b/java-spring/src/main/java/com/github/kuangcp/proxy/dao/base/Person.java @@ -0,0 +1,11 @@ +package com.github.kuangcp.proxy.dao.base; + +import lombok.Data; + +@Data +public class Person { + + private Long id; + private String name; + +} diff --git a/java-spring/src/main/java/com/github/kuangcp/proxy/dao/base/Transaction.java b/java-spring/src/main/java/com/github/kuangcp/proxy/dao/base/Transaction.java new file mode 100644 index 00000000..42821537 --- /dev/null +++ b/java-spring/src/main/java/com/github/kuangcp/proxy/dao/base/Transaction.java @@ -0,0 +1,15 @@ +package com.github.kuangcp.proxy.dao.base; + +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class Transaction { + + public void beginTransaction() { + log.info("begin transaction"); + } + + public void commit() { + log.info("commit"); + } +} diff --git a/java-spring/src/main/java/com/github/kuangcp/proxy/dao/cglibproxy/PersonDaoImpl.java b/java-spring/src/main/java/com/github/kuangcp/proxy/dao/cglibproxy/PersonDaoImpl.java new file mode 100644 index 00000000..0816f5f4 --- /dev/null +++ b/java-spring/src/main/java/com/github/kuangcp/proxy/dao/cglibproxy/PersonDaoImpl.java @@ -0,0 +1,34 @@ +package com.github.kuangcp.proxy.dao.cglibproxy; + +import com.github.kuangcp.proxy.dao.base.Person; +import java.util.ArrayList; +import java.util.List; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class PersonDaoImpl { + + public void savePerson() { + log.info("save person"); + } + + public void updatePerson() { + log.info("update person"); + } + + public void deletePerson() { + log.info("delete person"); + } + + public List getPerson() { + List personList = new ArrayList<>(); + Person person = new Person(); + person.setId(1L); + person.setName("who"); + personList.add(person); + + personList.stream().map(Person::toString).forEach(log::info); + + return personList; + } +} diff --git a/java-spring/src/test/java/com/github/kuangcp/proxy/dao/cglibproxy/PersonDaoInteceptor.java b/java-spring/src/main/java/com/github/kuangcp/proxy/dao/cglibproxy/PersonDaoInterceptor.java similarity index 86% rename from java-spring/src/test/java/com/github/kuangcp/proxy/dao/cglibproxy/PersonDaoInteceptor.java rename to java-spring/src/main/java/com/github/kuangcp/proxy/dao/cglibproxy/PersonDaoInterceptor.java index 84bce54e..37046e71 100644 --- a/java-spring/src/test/java/com/github/kuangcp/proxy/dao/cglibproxy/PersonDaoInteceptor.java +++ b/java-spring/src/main/java/com/github/kuangcp/proxy/dao/cglibproxy/PersonDaoInterceptor.java @@ -1,16 +1,17 @@ package com.github.kuangcp.proxy.dao.cglibproxy; +import com.github.kuangcp.proxy.dao.base.Transaction; import java.lang.reflect.Method; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; -public class PersonDaoInteceptor implements MethodInterceptor { +public class PersonDaoInterceptor implements MethodInterceptor { private Transaction transaction; private Object target; - public PersonDaoInteceptor(Transaction transaction, Object target) { + public PersonDaoInterceptor(Transaction transaction, Object target) { this.transaction = transaction; this.target = target; } diff --git a/java-spring/src/test/java/com/github/kuangcp/proxy/dao/jdkproxy/PersonDao.java b/java-spring/src/main/java/com/github/kuangcp/proxy/dao/jdkproxy/PersonDao.java similarity index 83% rename from java-spring/src/test/java/com/github/kuangcp/proxy/dao/jdkproxy/PersonDao.java rename to java-spring/src/main/java/com/github/kuangcp/proxy/dao/jdkproxy/PersonDao.java index 141a36b9..28b4b7dc 100644 --- a/java-spring/src/test/java/com/github/kuangcp/proxy/dao/jdkproxy/PersonDao.java +++ b/java-spring/src/main/java/com/github/kuangcp/proxy/dao/jdkproxy/PersonDao.java @@ -1,5 +1,6 @@ package com.github.kuangcp.proxy.dao.jdkproxy; +import com.github.kuangcp.proxy.dao.base.Person; import java.util.List; /** diff --git a/java-spring/src/test/java/com/github/kuangcp/proxy/dao/jdkproxy/PersonDaoImpl.java b/java-spring/src/main/java/com/github/kuangcp/proxy/dao/jdkproxy/PersonDaoImpl.java similarity index 58% rename from java-spring/src/test/java/com/github/kuangcp/proxy/dao/jdkproxy/PersonDaoImpl.java rename to java-spring/src/main/java/com/github/kuangcp/proxy/dao/jdkproxy/PersonDaoImpl.java index 0dd88e30..41f6a5d7 100644 --- a/java-spring/src/test/java/com/github/kuangcp/proxy/dao/jdkproxy/PersonDaoImpl.java +++ b/java-spring/src/main/java/com/github/kuangcp/proxy/dao/jdkproxy/PersonDaoImpl.java @@ -1,38 +1,37 @@ package com.github.kuangcp.proxy.dao.jdkproxy; +import com.github.kuangcp.proxy.dao.base.Person; import java.util.ArrayList; import java.util.List; +import lombok.extern.slf4j.Slf4j; +@Slf4j public class PersonDaoImpl implements PersonDao { @Override public void savePerson() { - // TODO Auto-generated method stub - System.out.println("save person"); + log.info("save person"); } @Override public void updatePerson() { - // TODO Auto-generated method stub - System.out.println("update person"); + log.info("update person"); } @Override public void deletePerson() { - // TODO Auto-generated method stub - System.out.println("delete person"); + log.info("delete person"); } @Override public List getPerson() { - // TODO Auto-generated method stub Person person = new Person(); - person.setPid(1L); - person.setPname("aaa"); + person.setId(1L); + person.setName("aaa"); List personList = new ArrayList(); personList.add(person); for (Person person2 : personList) { - System.out.println(person2.getPname()); + log.info("{}", person2); } return personList; } diff --git a/java-spring/src/test/java/com/github/kuangcp/proxy/dao/jdkproxy/PersonDaoInteceptor.java b/java-spring/src/main/java/com/github/kuangcp/proxy/dao/jdkproxy/PersonDaoInterceptor.java similarity index 81% rename from java-spring/src/test/java/com/github/kuangcp/proxy/dao/jdkproxy/PersonDaoInteceptor.java rename to java-spring/src/main/java/com/github/kuangcp/proxy/dao/jdkproxy/PersonDaoInterceptor.java index 6b05980a..40524091 100644 --- a/java-spring/src/test/java/com/github/kuangcp/proxy/dao/jdkproxy/PersonDaoInteceptor.java +++ b/java-spring/src/main/java/com/github/kuangcp/proxy/dao/jdkproxy/PersonDaoInterceptor.java @@ -1,5 +1,6 @@ package com.github.kuangcp.proxy.dao.jdkproxy; +import com.github.kuangcp.proxy.dao.base.Transaction; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; @@ -8,20 +9,18 @@ * * @author Myth */ -public class PersonDaoInteceptor implements InvocationHandler { +public class PersonDaoInterceptor implements InvocationHandler { private Transaction transaction; private Object target; - public PersonDaoInteceptor(Transaction transaction, Object target) { + public PersonDaoInterceptor(Transaction transaction, Object target) { this.transaction = transaction; this.target = target; } @Override - public Object invoke(Object proxy, Method method, Object[] args) - throws Throwable { - // TODO Auto-generated method stub + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object obj; String methodName = method.getName(); //对指定的方法增强 diff --git a/java-spring/src/main/resources/logback.xml b/java-spring/src/main/resources/logback.xml new file mode 100644 index 00000000..5e375431 --- /dev/null +++ b/java-spring/src/main/resources/logback.xml @@ -0,0 +1,82 @@ + + + + + + + + %d{HH:mm:ss.SSS} %highlight(%-5level) [%thread] %logger{30}:%yellow(%-3L) %msg%n + + + DEBUG + + + + + + ${log.base}debug.log + true + + ${log.base}debug.%d{yyyy-MM-dd}.log + + + %d{MM-dd HH:mm:ss.SSS} |-%-5level |%thread| %logger{36}:%L - %msg%n + + + DEBUG + + + + + + ${log.base}warn.log + true + + ${log.base}warn.%d{yyyy-MM-dd}.log + + + %d{MM-dd HH:mm:ss.SSS} |-%-5level %logger{36}:%L - %msg%n + + + WARN + + + + + + ${log.base}info.log + true + + ${log.base}info.%d{yyyy-MM-dd}.log + + + %d{MM-dd HH:mm:ss.SSS} |-%-5level %logger{36}:%L - %msg%n + + + INFO + + + + + + ${log.base}error.log + true + + ${log.base}error.%d{yyyy-MM-dd}.log + + + %d{MM-dd HH:mm:ss.SSS} | %thread |-%-5level %logger{36}:%L - %msg%n + + + ERROR + + + + + + + + + + + \ No newline at end of file diff --git a/java-spring/src/test/java/com/github/kuangcp/proxy/dao/cglibproxy/CGlibProxyTest.java b/java-spring/src/test/java/com/github/kuangcp/proxy/dao/cglibproxy/CGlibProxyTest.java new file mode 100644 index 00000000..b4a96b67 --- /dev/null +++ b/java-spring/src/test/java/com/github/kuangcp/proxy/dao/cglibproxy/CGlibProxyTest.java @@ -0,0 +1,16 @@ +package com.github.kuangcp.proxy.dao.cglibproxy; + +import com.github.kuangcp.proxy.dao.base.Transaction; +import org.junit.Test; + +public class CGlibProxyTest { + + @Test + public void testMain() { + Transaction transaction = new Transaction(); + PersonDaoImpl target = new PersonDaoImpl(); + PersonDaoInterceptor interceptor = new PersonDaoInterceptor(transaction, target); + PersonDaoImpl proxy = (PersonDaoImpl) interceptor.createProxy(); + proxy.updatePerson(); + } +} diff --git a/java-spring/src/test/java/com/github/kuangcp/proxy/dao/cglibproxy/Person.java b/java-spring/src/test/java/com/github/kuangcp/proxy/dao/cglibproxy/Person.java deleted file mode 100644 index 56777ccc..00000000 --- a/java-spring/src/test/java/com/github/kuangcp/proxy/dao/cglibproxy/Person.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.github.kuangcp.proxy.dao.cglibproxy; - -public class Person { - - private Long pid; - private String pname; - - public Long getPid() { - return pid; - } - - public void setPid(Long pid) { - this.pid = pid; - } - - public String getPname() { - return pname; - } - - public void setPname(String pname) { - this.pname = pname; - } -} diff --git a/java-spring/src/test/java/com/github/kuangcp/proxy/dao/cglibproxy/PersonDaoImpl.java b/java-spring/src/test/java/com/github/kuangcp/proxy/dao/cglibproxy/PersonDaoImpl.java deleted file mode 100644 index fe42e3f0..00000000 --- a/java-spring/src/test/java/com/github/kuangcp/proxy/dao/cglibproxy/PersonDaoImpl.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.github.kuangcp.proxy.dao.cglibproxy; - -import java.util.ArrayList; -import java.util.List; - -public class PersonDaoImpl { - - public void savePerson() { - // TODO Auto-generated method stub - System.out.println("save person"); - } - - public void updatePerson() { - // TODO Auto-generated method stub - System.out.println("update person"); - } - - public void deletePerson() { - // TODO Auto-generated method stub - System.out.println("delete person"); - } - - public List getPerson() { - // TODO Auto-generated method stub - Person person = new Person(); - person.setPid(1L); - person.setPname("aaa"); - List personList = new ArrayList(); - personList.add(person); - for (Person person2 : personList) { - System.out.println(person2.getPname()); - } - return personList; - } -} diff --git a/java-spring/src/test/java/com/github/kuangcp/proxy/dao/cglibproxy/PersonDaoTest.java b/java-spring/src/test/java/com/github/kuangcp/proxy/dao/cglibproxy/PersonDaoTest.java deleted file mode 100644 index bd359a24..00000000 --- a/java-spring/src/test/java/com/github/kuangcp/proxy/dao/cglibproxy/PersonDaoTest.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.github.kuangcp.proxy.dao.cglibproxy; - -import org.junit.Test; - -public class PersonDaoTest { - - @Test - public void test() { - Transaction transaction = new Transaction(); - PersonDaoImpl target = new PersonDaoImpl(); - PersonDaoInteceptor inteceptor = new PersonDaoInteceptor(transaction, target); - PersonDaoImpl proxy = (PersonDaoImpl) inteceptor.createProxy(); - proxy.updatePerson(); - } -} diff --git a/java-spring/src/test/java/com/github/kuangcp/proxy/dao/cglibproxy/Transaction.java b/java-spring/src/test/java/com/github/kuangcp/proxy/dao/cglibproxy/Transaction.java deleted file mode 100644 index b5474975..00000000 --- a/java-spring/src/test/java/com/github/kuangcp/proxy/dao/cglibproxy/Transaction.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.github.kuangcp.proxy.dao.cglibproxy; - -public class Transaction { - public void beginTransaction(){ - System.out.println("begin transaction"); - } - - public void commit(){ - System.out.println("commit"); - } -} diff --git a/java-spring/src/test/java/com/github/kuangcp/proxy/dao/jdkproxy/PersonDaoTest.java b/java-spring/src/test/java/com/github/kuangcp/proxy/dao/jdkproxy/JDKProxyTest.java similarity index 74% rename from java-spring/src/test/java/com/github/kuangcp/proxy/dao/jdkproxy/PersonDaoTest.java rename to java-spring/src/test/java/com/github/kuangcp/proxy/dao/jdkproxy/JDKProxyTest.java index 576b3c8f..a591460b 100644 --- a/java-spring/src/test/java/com/github/kuangcp/proxy/dao/jdkproxy/PersonDaoTest.java +++ b/java-spring/src/test/java/com/github/kuangcp/proxy/dao/jdkproxy/JDKProxyTest.java @@ -1,23 +1,24 @@ package com.github.kuangcp.proxy.dao.jdkproxy; +import com.github.kuangcp.proxy.dao.base.Transaction; import java.lang.reflect.Proxy; import org.junit.Test; -public class PersonDaoTest { +public class JDKProxyTest { @Test - public void test() { + public void testMain() { //增强 Transaction transaction = new Transaction(); //切点 PersonDao target = new PersonDaoImpl(); //拦截器 重写了invoke方法,将需要增强的对象target传入 - PersonDaoInteceptor inteceptor = new PersonDaoInteceptor(transaction, target); + PersonDaoInterceptor interceptor = new PersonDaoInterceptor(transaction, target); //使用jdk的代理对象来实例化, //参数解释:切点类装载器,切点类接口,实现了接口的(将增强织入切点的)实例 PersonDao proxy = (PersonDao) Proxy .newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), - inteceptor); + interceptor); // proxy.getPerson(); proxy.deletePerson(); } diff --git a/java-spring/src/test/java/com/github/kuangcp/proxy/dao/jdkproxy/Person.java b/java-spring/src/test/java/com/github/kuangcp/proxy/dao/jdkproxy/Person.java deleted file mode 100644 index a50116ca..00000000 --- a/java-spring/src/test/java/com/github/kuangcp/proxy/dao/jdkproxy/Person.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.github.kuangcp.proxy.dao.jdkproxy; - -public class Person { - - private Long pid; - private String pname; - - public Long getPid() { - return pid; - } - - public void setPid(Long pid) { - this.pid = pid; - } - - public String getPname() { - return pname; - } - - public void setPname(String pname) { - this.pname = pname; - } -} diff --git a/java-spring/src/test/java/com/github/kuangcp/proxy/dao/jdkproxy/Readme.md b/java-spring/src/test/java/com/github/kuangcp/proxy/dao/jdkproxy/Readme.md index a849400e..743dc9ce 100644 --- a/java-spring/src/test/java/com/github/kuangcp/proxy/dao/jdkproxy/Readme.md +++ b/java-spring/src/test/java/com/github/kuangcp/proxy/dao/jdkproxy/Readme.md @@ -1,12 +1,12 @@ -一个使用jdk的内置类来实现AOP +## 使用 JDK 的内置类来实现 AOP -1.具有基本对象 -2.对象的操作接口 dao -3.对象的操作接口的实现类 daoimpl 切点 目标类 -4.增强类 若干 -5.实现了invocationHandler接口的织入器 inteceptor - 织入器将所有增强 和 目标类(切点类)在invoke(之后的代理对象调用任何方法都是调用这个方法) - 方法中进行业务逻辑组合 +1. 具有基本对象 +2. 对象的操作接口 dao +3. 对象的操作接口的实现类 daoimpl 切点 目标类 +4. 增强类 若干 +5. 实现了invocationHandler接口的织入器 inteceptor + +织入器将所有增强 和 目标类(切点类)在invoke(之后的代理对象调用任何方法都是调用这个方法) 方法中进行业务逻辑组合 运行:实例化 切点类和增强,实例化织入器(传入切点和增强) 使用Proxy实例化代理对象 dao接收,然后调用方法就是增强后的方法 diff --git a/java-spring/src/test/java/com/github/kuangcp/proxy/dao/jdkproxy/Transaction.java b/java-spring/src/test/java/com/github/kuangcp/proxy/dao/jdkproxy/Transaction.java deleted file mode 100644 index fa6a0dd4..00000000 --- a/java-spring/src/test/java/com/github/kuangcp/proxy/dao/jdkproxy/Transaction.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.github.kuangcp.proxy.dao.jdkproxy; - -public class Transaction { - - public void beginTransaction() { - System.out.println("begin transaction"); - } - - public void commit() { - System.out.println("commit"); - } -} From 350b361396ff2533118f204403c9b8352e9d22ea Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Mon, 22 Apr 2019 22:23:06 +0800 Subject: [PATCH 016/476] *) learn thread with other threads --- java-class/src/main/java/jvm/oom/HeapOOM.java | 18 ++++--- .../src/test/java/jvm/oom/HeapOOMTest.java | 48 +++++++++++++++++- .../com/github/kuangcp/lock/sync/Action.java | 49 +++++++++++++++++++ .../github/kuangcp/lock/sync/ActionTest.java | 37 ++++++++++++++ .../proxy/dao/base/CustomInterceptor.java | 16 ++++++ .../proxy/dao/cglibproxy/PersonDaoImpl.java | 15 ------ .../dao/cglibproxy/PersonDaoInterceptor.java | 29 ++++++----- .../proxy/dao/jdkproxy/PersonDaoImpl.java | 11 +---- .../dao/jdkproxy/PersonDaoInterceptor.java | 16 +++--- .../com/github/kuangcp/proxy/dao/Readme.md | 3 ++ .../proxy/dao/jdkproxy/JDKProxyTest.java | 1 - 11 files changed, 185 insertions(+), 58 deletions(-) create mode 100644 java-concurrency/src/main/java/com/github/kuangcp/lock/sync/Action.java create mode 100644 java-concurrency/src/test/java/com/github/kuangcp/lock/sync/ActionTest.java create mode 100644 java-spring/src/main/java/com/github/kuangcp/proxy/dao/base/CustomInterceptor.java create mode 100644 java-spring/src/test/java/com/github/kuangcp/proxy/dao/Readme.md diff --git a/java-class/src/main/java/jvm/oom/HeapOOM.java b/java-class/src/main/java/jvm/oom/HeapOOM.java index ec36bff3..d14b64da 100644 --- a/java-class/src/main/java/jvm/oom/HeapOOM.java +++ b/java-class/src/main/java/jvm/oom/HeapOOM.java @@ -4,6 +4,8 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.TimeUnit; +import lombok.extern.slf4j.Slf4j; /** * -Xms5m -Xmx5m -XX:+HeapDumpOnOutOfMemoryError @@ -11,14 +13,18 @@ * * @author kuangcp on 4/3/19-10:11 PM */ +@Slf4j public class HeapOOM { - private byte[] data = new byte[1024 * 1024]; // 1 mib - void createArray() { - List data = new ArrayList<>(); + List data = new ArrayList<>(); while (true) { - data.add(new HeapOOM()); + data.add(new byte[1024 * 1024]); + try { + TimeUnit.SECONDS.sleep(1); + } catch (InterruptedException e) { + e.printStackTrace(); + } } } @@ -30,7 +36,7 @@ static class Key { this.id = id; } - // 重写了hashCode 没有重写 equals 导致了该对象在Map这样的集合中作Key时, 不能正确的覆盖值 + // 重写了hashCode 没有重写 equals 导致了该对象在Map这样的集合中作Key时, 不能按预期的覆盖旧值而是共存 @Override public int hashCode() { return id.hashCode(); @@ -45,7 +51,7 @@ void createMap() { m.put(new Key(i), "Number:" + i); } } - System.out.println("m.size()=" + m.size()); + log.info("m.size()=" + m.size()); } } } diff --git a/java-class/src/test/java/jvm/oom/HeapOOMTest.java b/java-class/src/test/java/jvm/oom/HeapOOMTest.java index 6107076e..6341570e 100644 --- a/java-class/src/test/java/jvm/oom/HeapOOMTest.java +++ b/java-class/src/test/java/jvm/oom/HeapOOMTest.java @@ -1,18 +1,62 @@ package jvm.oom; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; +import lombok.extern.slf4j.Slf4j; import org.junit.Ignore; import org.junit.Test; /** + * -Xms5m -Xmx5m + * * @author kuangcp on 4/5/19-9:45 AM */ +@Ignore +@Slf4j public class HeapOOMTest { private HeapOOM heapOOM = new HeapOOM(); @Test - @Ignore - public void testCreateArray() throws Exception { + public void testOOMWithOtherThread() throws Exception { + // 如果将该集合放在这里, 那么就不会被回收, 导致OOM 所有线程都玩完 + // List data = new ArrayList<>(); + + Thread main = new Thread(() -> { + List data = new ArrayList<>(); + while (true) { + // 因为当这里无法分配内存 OOM 后就出循环了, 对象也就可以被回收了 + data.add(new byte[1024 * 256]); + try { + TimeUnit.SECONDS.sleep(1); + } catch (InterruptedException e) { + e.printStackTrace(); + } + log.info("allocate memory"); + } + }); + + Thread thread = new Thread(() -> { + while (true) { + try { + TimeUnit.SECONDS.sleep(1); + } catch (InterruptedException e) { + e.printStackTrace(); + } + log.info("timer"); + } + }); + + main.start(); + thread.start(); + + main.join(); + thread.join(); + } + + @Test + public void testCreateArray() { heapOOM.createArray(); } diff --git a/java-concurrency/src/main/java/com/github/kuangcp/lock/sync/Action.java b/java-concurrency/src/main/java/com/github/kuangcp/lock/sync/Action.java new file mode 100644 index 00000000..3cf5336d --- /dev/null +++ b/java-concurrency/src/main/java/com/github/kuangcp/lock/sync/Action.java @@ -0,0 +1,49 @@ +package com.github.kuangcp.lock.sync; + +import java.util.concurrent.TimeUnit; +import lombok.extern.slf4j.Slf4j; + +/** + * @author kuangcp on 2019-04-22 6:33 PM + */ +@Slf4j +public class Action { + + private Object lock = new Object(); + + synchronized void syncMethod() { + log.info("invoke syncMethod"); + sleep(100); + log.info("exit syncMethod"); + } + + synchronized void syncMethod2() { + log.info("invoke syncMethod2"); + sleep(200); + log.info("exit syncMethod2"); + } + + void syncMethodWithLock() { + synchronized (lock) { + log.info("invoke syncMethodWithLock"); + sleep(100); + log.info("exit syncMethodWithLock"); + } + } + + void syncMethodWithLock2() { + synchronized (lock) { + log.info("invoke syncMethodWithLock2"); + sleep(200); + log.info("exit syncMethodWithLock2"); + } + } + + private void sleep(int mills) { + try { + TimeUnit.MILLISECONDS.sleep(mills); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } +} diff --git a/java-concurrency/src/test/java/com/github/kuangcp/lock/sync/ActionTest.java b/java-concurrency/src/test/java/com/github/kuangcp/lock/sync/ActionTest.java new file mode 100644 index 00000000..7ac2149d --- /dev/null +++ b/java-concurrency/src/test/java/com/github/kuangcp/lock/sync/ActionTest.java @@ -0,0 +1,37 @@ +package com.github.kuangcp.lock.sync; + +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; + +/** + * @author kuangcp on 2019-04-22 6:38 PM + */ +@Slf4j +public class ActionTest { + + + @Test + public void testSyncMethod() throws InterruptedException { + Action action = new Action(); + + Thread thread0 = new Thread(() -> { + action.syncMethod(); + action.syncMethod2(); + }); + Thread thread1 = new Thread(() -> { + action.syncMethod(); + action.syncMethod2(); + }); + + thread0.start(); + thread1.start(); + + // 如果两个方法睡眠时间一致, 很有可能会一直出现 0 线程先执行完两个方法才轮到线程 1 + thread0.join(); + thread1.join(); + +// while(Thread.activeCount() > 1){ +// Thread.yield(); +// } + } +} diff --git a/java-spring/src/main/java/com/github/kuangcp/proxy/dao/base/CustomInterceptor.java b/java-spring/src/main/java/com/github/kuangcp/proxy/dao/base/CustomInterceptor.java new file mode 100644 index 00000000..c129d08e --- /dev/null +++ b/java-spring/src/main/java/com/github/kuangcp/proxy/dao/base/CustomInterceptor.java @@ -0,0 +1,16 @@ +package com.github.kuangcp.proxy.dao.base; + +/** + * @author kuangcp on 2019-04-22 6:05 PM + */ +public interface CustomInterceptor { + + default boolean isNeedTransaction(String methodName) { + return "savePerson".equals(methodName) || "updatePerson".equals(methodName) || + "deletePerson".equals(methodName); + } + + static boolean a() { + return true; + } +} diff --git a/java-spring/src/main/java/com/github/kuangcp/proxy/dao/cglibproxy/PersonDaoImpl.java b/java-spring/src/main/java/com/github/kuangcp/proxy/dao/cglibproxy/PersonDaoImpl.java index 0816f5f4..9f7b79f9 100644 --- a/java-spring/src/main/java/com/github/kuangcp/proxy/dao/cglibproxy/PersonDaoImpl.java +++ b/java-spring/src/main/java/com/github/kuangcp/proxy/dao/cglibproxy/PersonDaoImpl.java @@ -1,8 +1,5 @@ package com.github.kuangcp.proxy.dao.cglibproxy; -import com.github.kuangcp.proxy.dao.base.Person; -import java.util.ArrayList; -import java.util.List; import lombok.extern.slf4j.Slf4j; @Slf4j @@ -19,16 +16,4 @@ public void updatePerson() { public void deletePerson() { log.info("delete person"); } - - public List getPerson() { - List personList = new ArrayList<>(); - Person person = new Person(); - person.setId(1L); - person.setName("who"); - personList.add(person); - - personList.stream().map(Person::toString).forEach(log::info); - - return personList; - } } diff --git a/java-spring/src/main/java/com/github/kuangcp/proxy/dao/cglibproxy/PersonDaoInterceptor.java b/java-spring/src/main/java/com/github/kuangcp/proxy/dao/cglibproxy/PersonDaoInterceptor.java index 37046e71..df6cb02f 100644 --- a/java-spring/src/main/java/com/github/kuangcp/proxy/dao/cglibproxy/PersonDaoInterceptor.java +++ b/java-spring/src/main/java/com/github/kuangcp/proxy/dao/cglibproxy/PersonDaoInterceptor.java @@ -1,12 +1,13 @@ package com.github.kuangcp.proxy.dao.cglibproxy; +import com.github.kuangcp.proxy.dao.base.CustomInterceptor; import com.github.kuangcp.proxy.dao.base.Transaction; import java.lang.reflect.Method; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; -public class PersonDaoInterceptor implements MethodInterceptor { +public class PersonDaoInterceptor implements MethodInterceptor, CustomInterceptor { private Transaction transaction; private Object target; @@ -16,29 +17,27 @@ public PersonDaoInterceptor(Transaction transaction, Object target) { this.target = target; } + // cglib 方式就是动态创建一个子类 public Object createProxy() { Enhancer enhancer = new Enhancer(); - enhancer.setSuperclass(this.target.getClass());//设置目标类为代理类的父类 - enhancer.setCallback(this);//设置拦截器为回调函数 + //设置代理类为目标类的子类 + enhancer.setSuperclass(this.target.getClass()); + //设置拦截器为回调函数 + enhancer.setCallback(this); return enhancer.create(); } - @Override - public Object intercept(Object arg0, Method method, Object[] arg2, - MethodProxy arg3) throws Throwable { - Object obj; - String methodName = method.getName(); - if ("savePerson".equals(methodName) || - "updatePerson".equals(methodName) || - "deletePerson".equals(methodName)) { + public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) + throws Throwable { + Object result; + if (isNeedTransaction(method.getName())) { this.transaction.beginTransaction(); - obj = method.invoke(this.target, arg2);//调用目标类的目标方法 + result = method.invoke(this.target, method);//调用目标类的目标方法 this.transaction.commit(); } else { - obj = method.invoke(this.target, arg2);//调用目标类的目标方法 + result = method.invoke(this.target, method);//调用目标类的目标方法 } - return obj; + return result; } - } diff --git a/java-spring/src/main/java/com/github/kuangcp/proxy/dao/jdkproxy/PersonDaoImpl.java b/java-spring/src/main/java/com/github/kuangcp/proxy/dao/jdkproxy/PersonDaoImpl.java index 41f6a5d7..2c928879 100644 --- a/java-spring/src/main/java/com/github/kuangcp/proxy/dao/jdkproxy/PersonDaoImpl.java +++ b/java-spring/src/main/java/com/github/kuangcp/proxy/dao/jdkproxy/PersonDaoImpl.java @@ -1,7 +1,6 @@ package com.github.kuangcp.proxy.dao.jdkproxy; import com.github.kuangcp.proxy.dao.base.Person; -import java.util.ArrayList; import java.util.List; import lombok.extern.slf4j.Slf4j; @@ -25,15 +24,7 @@ public void deletePerson() { @Override public List getPerson() { - Person person = new Person(); - person.setId(1L); - person.setName("aaa"); - List personList = new ArrayList(); - personList.add(person); - for (Person person2 : personList) { - log.info("{}", person2); - } - return personList; + return null; } } diff --git a/java-spring/src/main/java/com/github/kuangcp/proxy/dao/jdkproxy/PersonDaoInterceptor.java b/java-spring/src/main/java/com/github/kuangcp/proxy/dao/jdkproxy/PersonDaoInterceptor.java index 40524091..674728fa 100644 --- a/java-spring/src/main/java/com/github/kuangcp/proxy/dao/jdkproxy/PersonDaoInterceptor.java +++ b/java-spring/src/main/java/com/github/kuangcp/proxy/dao/jdkproxy/PersonDaoInterceptor.java @@ -1,5 +1,6 @@ package com.github.kuangcp.proxy.dao.jdkproxy; +import com.github.kuangcp.proxy.dao.base.CustomInterceptor; import com.github.kuangcp.proxy.dao.base.Transaction; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; @@ -9,7 +10,7 @@ * * @author Myth */ -public class PersonDaoInterceptor implements InvocationHandler { +public class PersonDaoInterceptor implements InvocationHandler, CustomInterceptor { private Transaction transaction; private Object target; @@ -21,19 +22,16 @@ public PersonDaoInterceptor(Transaction transaction, Object target) { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { - Object obj; - String methodName = method.getName(); + Object result; //对指定的方法增强 - if ("savePerson".equals(methodName) || - "updatePerson".equals(methodName) || - "deletePerson".equals(methodName)) { + if (isNeedTransaction(method.getName())) { this.transaction.beginTransaction(); - obj = method.invoke(this.target, args);//调用目标类的目标方法 + result = method.invoke(this.target, args);//调用目标类的目标方法 this.transaction.commit(); } else { - obj = method.invoke(this.target, args);//调用目标类的目标方法 + result = method.invoke(this.target, args);//调用目标类的目标方法 } - return obj; + return result; } } diff --git a/java-spring/src/test/java/com/github/kuangcp/proxy/dao/Readme.md b/java-spring/src/test/java/com/github/kuangcp/proxy/dao/Readme.md new file mode 100644 index 00000000..65c8b59e --- /dev/null +++ b/java-spring/src/test/java/com/github/kuangcp/proxy/dao/Readme.md @@ -0,0 +1,3 @@ +# AOP 手动实现方式 + +针对 Person 类, 增删改操作, AOP 的方式添加事务控制 diff --git a/java-spring/src/test/java/com/github/kuangcp/proxy/dao/jdkproxy/JDKProxyTest.java b/java-spring/src/test/java/com/github/kuangcp/proxy/dao/jdkproxy/JDKProxyTest.java index a591460b..be326074 100644 --- a/java-spring/src/test/java/com/github/kuangcp/proxy/dao/jdkproxy/JDKProxyTest.java +++ b/java-spring/src/test/java/com/github/kuangcp/proxy/dao/jdkproxy/JDKProxyTest.java @@ -19,7 +19,6 @@ public void testMain() { PersonDao proxy = (PersonDao) Proxy .newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), interceptor); -// proxy.getPerson(); proxy.deletePerson(); } } From b04417e88b57bc6aa2968794ab3a6a021d9ec422 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Tue, 23 Apr 2019 10:36:00 +0800 Subject: [PATCH 017/476] *) use netty 4 --- dependency.gradle | 2 +- .../main/java/netty/timeServer/Command.java | 10 +++ .../java/netty/timeServer/TimeClient.java | 2 +- .../netty/timeServer/TimeClientHandler.java | 8 +- .../java/netty/timeServer/TimeServer.java | 20 +++-- .../netty/timeServer/TimeServerHandler.java | 22 +++-- .../main/resources/commons-logging.properties | 2 - .../src/main/resources/log4j.properties | 26 ------ java-netty/src/main/resources/logback.xml | 82 +++++++++++++++++++ .../java/netty/timeServer/TimeClientTest.java | 2 +- .../kuangcp/selfclose/SocketSelfClose.java | 1 + 11 files changed, 128 insertions(+), 49 deletions(-) create mode 100644 java-netty/src/main/java/netty/timeServer/Command.java delete mode 100644 java-netty/src/main/resources/commons-logging.properties delete mode 100644 java-netty/src/main/resources/log4j.properties create mode 100644 java-netty/src/main/resources/logback.xml diff --git a/dependency.gradle b/dependency.gradle index 6afe69e5..1c391014 100644 --- a/dependency.gradle +++ b/dependency.gradle @@ -11,7 +11,7 @@ ext { , jackson : '2.9.5' , hamcrest: '1.3' , kcp_tool: '1.0.3' - , netty : '5.0.0.Alpha1' + , netty : '4.1.35.Final' , guava : '27.1-jre' , jmh : '1.2.1' ] diff --git a/java-netty/src/main/java/netty/timeServer/Command.java b/java-netty/src/main/java/netty/timeServer/Command.java new file mode 100644 index 00000000..e7e8c2b0 --- /dev/null +++ b/java-netty/src/main/java/netty/timeServer/Command.java @@ -0,0 +1,10 @@ +package netty.timeServer; + +/** + * @author kuangcp on 2019-04-23 10:24 AM + */ +public interface Command { + + String QUERY_TIME = "QUERY TIME ORDER"; + String STOP_SERVER = "STOP"; +} diff --git a/java-netty/src/main/java/netty/timeServer/TimeClient.java b/java-netty/src/main/java/netty/timeServer/TimeClient.java index 4746baf0..6432768c 100644 --- a/java-netty/src/main/java/netty/timeServer/TimeClient.java +++ b/java-netty/src/main/java/netty/timeServer/TimeClient.java @@ -11,7 +11,7 @@ import lombok.extern.slf4j.Slf4j; @Slf4j -public class TimeClient { +class TimeClient { void connectLocal(int port) throws Exception { connect(port, "127.0.0.1"); diff --git a/java-netty/src/main/java/netty/timeServer/TimeClientHandler.java b/java-netty/src/main/java/netty/timeServer/TimeClientHandler.java index befcd380..7212bf01 100644 --- a/java-netty/src/main/java/netty/timeServer/TimeClientHandler.java +++ b/java-netty/src/main/java/netty/timeServer/TimeClientHandler.java @@ -2,8 +2,8 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelHandlerAdapter; import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.SimpleChannelInboundHandler; import java.nio.charset.StandardCharsets; import lombok.extern.slf4j.Slf4j; @@ -11,14 +11,14 @@ * 客户端 业务代码 */ @Slf4j -public class TimeClientHandler extends ChannelHandlerAdapter { +class TimeClientHandler extends SimpleChannelInboundHandler { /** * 当客户端和服务端成功建立连接后, 就会调用该方法 */ @Override public void channelActive(ChannelHandlerContext ctx) { - byte[] req = "QUERY TIME ORDER".getBytes(); + byte[] req = Command.QUERY_TIME.getBytes(); ByteBuf firstMessage = Unpooled.buffer(req.length); firstMessage.writeBytes(req); // 将消息发送至服务端 @@ -29,7 +29,7 @@ public void channelActive(ChannelHandlerContext ctx) { * 当服务端回应消息时,该方法被调用 */ @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + public void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception { // 读取服务端发来的回应消息 ByteBuf buf = (ByteBuf) msg; byte[] req = new byte[buf.readableBytes()]; diff --git a/java-netty/src/main/java/netty/timeServer/TimeServer.java b/java-netty/src/main/java/netty/timeServer/TimeServer.java index 2ac43844..f8687a48 100644 --- a/java-netty/src/main/java/netty/timeServer/TimeServer.java +++ b/java-netty/src/main/java/netty/timeServer/TimeServer.java @@ -8,14 +8,15 @@ import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; +import lombok.extern.slf4j.Slf4j; -public class TimeServer { +class TimeServer { static final int port = 8080; public void start() throws Exception { - // NIO 结合两个线程池 - // 配置服务端的NIO线程组 , 一个用于接受客户端的连接, 一个是处理SocketChannel的网络读写 + // NIO 结合两个线程池 , boss 用于接受客户端的连接 + // worker 处理SocketChannel的 read write 然后交由对应的Handler处理 EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); @@ -25,11 +26,12 @@ public void start() throws Exception { .channel(NioServerSocketChannel.class) .option(ChannelOption.SO_BACKLOG, 512) .childHandler(new ChildChannelHandler()); + // 绑定端口,同步等待成功 返回一个ChannelFuture, 用于异步操作的通知回调 - ChannelFuture f = serverBootstrap.bind(port).sync(); + ChannelFuture future = serverBootstrap.bind(port).sync(); // 等待服务端监听端口关闭 - f.channel().closeFuture().sync(); + future.channel().closeFuture().sync(); } finally { // 优雅退出,释放线程池资源 bossGroup.shutdownGracefully(); @@ -37,12 +39,16 @@ public void start() throws Exception { } } - private class ChildChannelHandler extends ChannelInitializer { + @Slf4j + private static class ChildChannelHandler extends ChannelInitializer { + + public ChildChannelHandler() { + log.info("init a initializer"); + } @Override protected void initChannel(SocketChannel arg0) { arg0.pipeline().addLast(new TimeServerHandler()); } - } } diff --git a/java-netty/src/main/java/netty/timeServer/TimeServerHandler.java b/java-netty/src/main/java/netty/timeServer/TimeServerHandler.java index e6ed6410..2c540be7 100644 --- a/java-netty/src/main/java/netty/timeServer/TimeServerHandler.java +++ b/java-netty/src/main/java/netty/timeServer/TimeServerHandler.java @@ -2,8 +2,8 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelHandlerAdapter; import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.SimpleChannelInboundHandler; import java.nio.charset.StandardCharsets; import java.time.LocalDateTime; import java.util.concurrent.atomic.AtomicInteger; @@ -13,7 +13,11 @@ * 服务端的业务代码 继承自 ChannelHandlerAdapter */ @Slf4j -public class TimeServerHandler extends ChannelHandlerAdapter { +class TimeServerHandler extends SimpleChannelInboundHandler { + + public TimeServerHandler() { + log.warn("init a instance"); + } private static AtomicInteger counter = new AtomicInteger(); @@ -21,7 +25,7 @@ public class TimeServerHandler extends ChannelHandlerAdapter { * 成功建立连接后, 读取服务端的消息 */ @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + public void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception { ByteBuf buf = (ByteBuf) msg; byte[] req = new byte[buf.readableBytes()]; buf.readBytes(req); @@ -29,10 +33,14 @@ public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception log.info("server receive msg: body={} count={}", body, counter.incrementAndGet()); - String currentTime = "QUERY TIME ORDER".equalsIgnoreCase(body) ? - LocalDateTime.now().toString() : "BAD ORDER"; - ByteBuf resp = Unpooled.copiedBuffer(currentTime.getBytes()); - ctx.write(resp); + String result; + if (Command.QUERY_TIME.equalsIgnoreCase(body)) { + result = LocalDateTime.now().toString(); + } else { + result = "BAD ORDER"; + } + ByteBuf response = Unpooled.copiedBuffer(result.getBytes()); + ctx.write(response); } /** diff --git a/java-netty/src/main/resources/commons-logging.properties b/java-netty/src/main/resources/commons-logging.properties deleted file mode 100644 index 5ec320ae..00000000 --- a/java-netty/src/main/resources/commons-logging.properties +++ /dev/null @@ -1,2 +0,0 @@ -# use LOG4J as the underlying logging toolkit -org.apache.commons.logging.Log=org.apache.commons.logging.impl.Log4JLogger \ No newline at end of file diff --git a/java-netty/src/main/resources/log4j.properties b/java-netty/src/main/resources/log4j.properties deleted file mode 100644 index 0a110dca..00000000 --- a/java-netty/src/main/resources/log4j.properties +++ /dev/null @@ -1,26 +0,0 @@ -# LOG4J configuration - -# default logging -#log4j.rootCategory=DEBUG, LOGFILE,file,CA -log4j.rootCategory=DEBUG, CA -## logging to file -#log4j.appender.LOGFILE=org.apache.log4j.DailyRollingFileAppender -#log4j.appender.LOGFILE.File=E:/logs/epcis-repository.log -#log4j.appender.LOGFILE.DatePattern='.'yyyy-MM-dd'.log' -#log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout -#log4j.appender.LOGFILE.layout.ConversionPattern=%5p (%d{yyyy-MM-dd HH:mm:ss,SSS}) [%C:%L] - %m%n -# -#log4j.appender.file=org.apache.log4j.RollingFileAppender -#log4j.appender.file.File=E:\\logs\\demolog.log -#log4j.appender.file.MaxFileSize=5MB -#log4j.appender.file.MaxBackupIndex=10 -#log4j.appender.file.layout=org.apache.log4j.PatternLayout -#log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n - -#Console Appender -log4j.appender.CA=org.apache.log4j.ConsoleAppender -log4j.appender.CA.layout=org.apache.log4j.PatternLayout -#log4j.appender.CA.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c %x - %m%n -#log4j.appender.CA.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n -#ʾ -log4j.appender.CA.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n \ No newline at end of file diff --git a/java-netty/src/main/resources/logback.xml b/java-netty/src/main/resources/logback.xml new file mode 100644 index 00000000..5e375431 --- /dev/null +++ b/java-netty/src/main/resources/logback.xml @@ -0,0 +1,82 @@ + + + + + + + + %d{HH:mm:ss.SSS} %highlight(%-5level) [%thread] %logger{30}:%yellow(%-3L) %msg%n + + + DEBUG + + + + + + ${log.base}debug.log + true + + ${log.base}debug.%d{yyyy-MM-dd}.log + + + %d{MM-dd HH:mm:ss.SSS} |-%-5level |%thread| %logger{36}:%L - %msg%n + + + DEBUG + + + + + + ${log.base}warn.log + true + + ${log.base}warn.%d{yyyy-MM-dd}.log + + + %d{MM-dd HH:mm:ss.SSS} |-%-5level %logger{36}:%L - %msg%n + + + WARN + + + + + + ${log.base}info.log + true + + ${log.base}info.%d{yyyy-MM-dd}.log + + + %d{MM-dd HH:mm:ss.SSS} |-%-5level %logger{36}:%L - %msg%n + + + INFO + + + + + + ${log.base}error.log + true + + ${log.base}error.%d{yyyy-MM-dd}.log + + + %d{MM-dd HH:mm:ss.SSS} | %thread |-%-5level %logger{36}:%L - %msg%n + + + ERROR + + + + + + + + + + + \ No newline at end of file diff --git a/java-netty/src/test/java/netty/timeServer/TimeClientTest.java b/java-netty/src/test/java/netty/timeServer/TimeClientTest.java index aa5a5abb..7832c59a 100644 --- a/java-netty/src/test/java/netty/timeServer/TimeClientTest.java +++ b/java-netty/src/test/java/netty/timeServer/TimeClientTest.java @@ -12,7 +12,7 @@ public class TimeClientTest { private TimeClient timeClient = new TimeClient(); - @Test(threadPoolSize = 20, invocationCount = 50) + @Test(threadPoolSize = 5, invocationCount = 20) // @Test public void testClient() throws Exception { timeClient.connectLocal(TimeServer.port); diff --git a/java-network/src/main/java/com/github/kuangcp/selfclose/SocketSelfClose.java b/java-network/src/main/java/com/github/kuangcp/selfclose/SocketSelfClose.java index 4dcdd0ca..ae3b2e33 100644 --- a/java-network/src/main/java/com/github/kuangcp/selfclose/SocketSelfClose.java +++ b/java-network/src/main/java/com/github/kuangcp/selfclose/SocketSelfClose.java @@ -8,6 +8,7 @@ /** * Created by Myth on 2017/4/3 * Socket 半关闭状态,可以输入输出流单独控制关闭,但是关闭后就不能开启了 + * * 只适用于一站式的通信协议,例如HTTP协议-客户端连接到服务器后,开始发送请求数据,发送完成后无需再次发送数据 * 客户端,使用chatting room下的客户端即可进行测试验证 */ From 68e9902d5e4a60cca8a196b2deca216330aff942 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Wed, 24 Apr 2019 00:10:33 +0800 Subject: [PATCH 018/476] *) rollback to 2018 --- build.gradle | 18 ++++++++--------- .../java/netty/timeServer/TimeServer.java | 15 ++++++++------ .../netty/timeServer/TimeServerHandler.java | 4 +++- .../java/netty/timeServer/TimeClientTest.java | 20 ------------------- .../java/netty/timeServer/TimeServerTest.java | 18 ++++++++++++----- 5 files changed, 34 insertions(+), 41 deletions(-) delete mode 100644 java-netty/src/test/java/netty/timeServer/TimeClientTest.java diff --git a/build.gradle b/build.gradle index a55457b7..63b121d5 100644 --- a/build.gradle +++ b/build.gradle @@ -33,16 +33,16 @@ allprojects { // All sub-modules add the following dependencies dependencies { - annotationProcessor rootProject.libs['lombok'] - compileOnly rootProject.libs['lombok'] - testAnnotationProcessor rootProject.libs['lombok'] - testCompileOnly rootProject.libs['lombok'] + annotationProcessor libs['lombok'] + compileOnly libs['lombok'] + testAnnotationProcessor libs['lombok'] + testCompileOnly libs['lombok'] - implementation rootProject.libs['logback-classic'] - implementation rootProject.libs['kcp-tool'] + implementation libs['logback-classic'] + implementation libs['kcp-tool'] - testImplementation rootProject.libs['junit'] - testImplementation rootProject.libs['hamcrest-core'] - testImplementation rootProject.libs['hamcrest-lib'] + testImplementation libs['junit'] + testImplementation libs['hamcrest-core'] + testImplementation libs['hamcrest-lib'] } } diff --git a/java-netty/src/main/java/netty/timeServer/TimeServer.java b/java-netty/src/main/java/netty/timeServer/TimeServer.java index f8687a48..719f184b 100644 --- a/java-netty/src/main/java/netty/timeServer/TimeServer.java +++ b/java-netty/src/main/java/netty/timeServer/TimeServer.java @@ -15,17 +15,18 @@ class TimeServer { static final int port = 8080; public void start() throws Exception { - // NIO 结合两个线程池 , boss 用于接受客户端的连接 - // worker 处理SocketChannel的 read write 然后交由对应的Handler处理 + // NIO 结合两个线程池 EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap serverBootstrap = new ServerBootstrap(); + // boss/acceptor 用于接受客户端的连接 worker 处理SocketChannel的 read write 然后交由对应的Handler处理 + // 可以看 group 的 doc serverBootstrap.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .option(ChannelOption.SO_BACKLOG, 512) - .childHandler(new ChildChannelHandler()); + .childHandler(new ChannelInit()); // 绑定端口,同步等待成功 返回一个ChannelFuture, 用于异步操作的通知回调 ChannelFuture future = serverBootstrap.bind(port).sync(); @@ -40,15 +41,17 @@ public void start() throws Exception { } @Slf4j - private static class ChildChannelHandler extends ChannelInitializer { + private static class ChannelInit extends ChannelInitializer { - public ChildChannelHandler() { + TimeServerHandler handler = new TimeServerHandler(); + + public ChannelInit() { log.info("init a initializer"); } @Override protected void initChannel(SocketChannel arg0) { - arg0.pipeline().addLast(new TimeServerHandler()); + arg0.pipeline().addLast(handler); } } } diff --git a/java-netty/src/main/java/netty/timeServer/TimeServerHandler.java b/java-netty/src/main/java/netty/timeServer/TimeServerHandler.java index 2c540be7..548e7e1e 100644 --- a/java-netty/src/main/java/netty/timeServer/TimeServerHandler.java +++ b/java-netty/src/main/java/netty/timeServer/TimeServerHandler.java @@ -2,6 +2,7 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; +import io.netty.channel.ChannelHandler.Sharable; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; import java.nio.charset.StandardCharsets; @@ -10,9 +11,10 @@ import lombok.extern.slf4j.Slf4j; /** - * 服务端的业务代码 继承自 ChannelHandlerAdapter + * 服务端的业务代码 继承自 5: ChannelHandlerAdapter 4: SimpleChannelInboundHandler */ @Slf4j +@Sharable // 可被注册到多个 pipeline class TimeServerHandler extends SimpleChannelInboundHandler { public TimeServerHandler() { diff --git a/java-netty/src/test/java/netty/timeServer/TimeClientTest.java b/java-netty/src/test/java/netty/timeServer/TimeClientTest.java deleted file mode 100644 index 7832c59a..00000000 --- a/java-netty/src/test/java/netty/timeServer/TimeClientTest.java +++ /dev/null @@ -1,20 +0,0 @@ -package netty.timeServer; - - -import org.testng.annotations.Test; - -/** - * Created by https://github.com/kuangcp - * - * @author kuangcp - */ -public class TimeClientTest { - - private TimeClient timeClient = new TimeClient(); - - @Test(threadPoolSize = 5, invocationCount = 20) -// @Test - public void testClient() throws Exception { - timeClient.connectLocal(TimeServer.port); - } -} \ No newline at end of file diff --git a/java-netty/src/test/java/netty/timeServer/TimeServerTest.java b/java-netty/src/test/java/netty/timeServer/TimeServerTest.java index fa6f2fff..29e16d4c 100644 --- a/java-netty/src/test/java/netty/timeServer/TimeServerTest.java +++ b/java-netty/src/test/java/netty/timeServer/TimeServerTest.java @@ -1,18 +1,26 @@ package netty.timeServer; -import org.junit.Test; + +import lombok.extern.slf4j.Slf4j; +import org.testng.annotations.Test; /** - * Created by https://github.com/kuangcp - * - * @author kuangcp on 18-3-20 上午10:53 + * @author kuangcp on 2019-04-23 10:58 AM */ +@Slf4j public class TimeServerTest { + private TimeClient timeClient = new TimeClient(); private TimeServer timeServer = new TimeServer(); + // @Test + @Test(threadPoolSize = 5, invocationCount = 20) + public void testClient() throws Exception { + timeClient.connectLocal(TimeServer.port); + } + @Test public void testServer() throws Exception { timeServer.start(); } -} \ No newline at end of file +} From c6a2c15630a4895ac2abd9b9bd48f51f5f62507d Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Thu, 25 Apr 2019 01:10:06 +0800 Subject: [PATCH 019/476] +) byte --- .../com/github/kuangcp/util/ShowBinary.java | 10 ++- .../java/syntax/bit/BitOperatorsTest.java | 3 +- .../test/java/syntax/bytes/OperatorTest.java | 45 ++++++++++++ .../java/syntax/doubles/DoubleConstTest.java | 2 +- .../com/github/kuangcp/valatile/Person.groovy | 29 -------- .../github/kuangcp/valatile/ReSortDemo.groovy | 70 ------------------- .../github/kuangcp/valatile/RepeatRead.groovy | 31 -------- .../kuangcp/valatile/RepeatReadDemo.groovy | 38 ---------- .../kuangcp/valatile/ResortJavaDemo.java | 32 --------- .../github/kuangcp/volatiles/FirstDemo.java | 61 ++++++++++++++++ .../kuangcp/volatiles/FirstDemoTest.java | 53 ++++++++++++++ 11 files changed, 169 insertions(+), 205 deletions(-) create mode 100644 java-class/src/test/java/syntax/bytes/OperatorTest.java delete mode 100644 java-concurrency/src/main/java/com/github/kuangcp/valatile/Person.groovy delete mode 100644 java-concurrency/src/main/java/com/github/kuangcp/valatile/ReSortDemo.groovy delete mode 100644 java-concurrency/src/main/java/com/github/kuangcp/valatile/RepeatRead.groovy delete mode 100644 java-concurrency/src/main/java/com/github/kuangcp/valatile/RepeatReadDemo.groovy delete mode 100644 java-concurrency/src/main/java/com/github/kuangcp/valatile/ResortJavaDemo.java create mode 100644 java-concurrency/src/main/java/com/github/kuangcp/volatiles/FirstDemo.java create mode 100644 java-concurrency/src/test/java/com/github/kuangcp/volatiles/FirstDemoTest.java diff --git a/java-class/src/main/java/com/github/kuangcp/util/ShowBinary.java b/java-class/src/main/java/com/github/kuangcp/util/ShowBinary.java index 85307649..3f490a05 100644 --- a/java-class/src/main/java/com/github/kuangcp/util/ShowBinary.java +++ b/java-class/src/main/java/com/github/kuangcp/util/ShowBinary.java @@ -5,15 +5,19 @@ */ public class ShowBinary { - public static String toBinaryString(Integer value) { + public static String toBinary(Byte value) { return Integer.toBinaryString(value); } - public static String toBinaryString(Double value) { + public static String toBinary(Integer value) { + return Integer.toBinaryString(value); + } + + public static String toBinary(Double value) { return Long.toBinaryString(Double.doubleToRawLongBits(value)); } - public static String toBinaryString(Float value) { + public static String toBinary(Float value) { return Float.toHexString(value); } } diff --git a/java-class/src/test/java/syntax/bit/BitOperatorsTest.java b/java-class/src/test/java/syntax/bit/BitOperatorsTest.java index b7de3017..e29bd07e 100644 --- a/java-class/src/test/java/syntax/bit/BitOperatorsTest.java +++ b/java-class/src/test/java/syntax/bit/BitOperatorsTest.java @@ -4,6 +4,7 @@ import static org.hamcrest.core.IsEqual.equalTo; import com.github.kuangcp.time.GetRunTime; +import com.github.kuangcp.util.ShowBinary; import java.util.stream.IntStream; import lombok.extern.slf4j.Slf4j; import org.junit.Test; @@ -99,7 +100,7 @@ public void testLeft() { } private static void show(int result) { - log.info("{} {}", String.format("%32s", Integer.toBinaryString(result)), result); + log.info("{} {}", String.format("%32s", ShowBinary.toBinary(result)), result); } @Test diff --git a/java-class/src/test/java/syntax/bytes/OperatorTest.java b/java-class/src/test/java/syntax/bytes/OperatorTest.java new file mode 100644 index 00000000..439e7c7d --- /dev/null +++ b/java-class/src/test/java/syntax/bytes/OperatorTest.java @@ -0,0 +1,45 @@ +package syntax.bytes; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.IsEqual.equalTo; + +import com.github.kuangcp.util.ShowBinary; +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; + +/** + * @author kuangcp on 2019-04-24 11:39 PM + */ +@Slf4j +public class OperatorTest { + + @Test + public void testAdd() { + byte a = 4; + byte b = 2; + + int result = a / b; + + assertThat(result, equalTo(2)); + log.info("{} {} {}", result, ShowBinary.toBinary(a), ShowBinary.toBinary(b)); + } + + @Test + public void testToInt() { + int a = 13232; + // 直接赋值 无法编译通过 + byte b = (byte)a; + + // int 赋值 byte 会截断后8位, 由于是有符号的, 结果是 10110000 + // 原码则是 01010000 : 所以 b是-80 + log.info("{}: {} -> {}", b, ShowBinary.toBinary(a), ShowBinary.toBinary(b)); + } + + @Test + public void testMod(){ + byte a = -64; + byte b = -4; + // TODO 取余操作 + log.info("{}", a%b); + } +} diff --git a/java-class/src/test/java/syntax/doubles/DoubleConstTest.java b/java-class/src/test/java/syntax/doubles/DoubleConstTest.java index e43331f5..53a70be9 100644 --- a/java-class/src/test/java/syntax/doubles/DoubleConstTest.java +++ b/java-class/src/test/java/syntax/doubles/DoubleConstTest.java @@ -13,6 +13,6 @@ public class DoubleConstTest { @Test public void testExtremum(){ - log.info("{} {}", ShowBinary.toBinaryString(Double.MIN_VALUE), ShowBinary.toBinaryString(Double.MIN_NORMAL)); + log.info("{} {}", ShowBinary.toBinary(Double.MIN_VALUE), ShowBinary.toBinary(Double.MIN_NORMAL)); } } diff --git a/java-concurrency/src/main/java/com/github/kuangcp/valatile/Person.groovy b/java-concurrency/src/main/java/com/github/kuangcp/valatile/Person.groovy deleted file mode 100644 index ac6a3b7c..00000000 --- a/java-concurrency/src/main/java/com/github/kuangcp/valatile/Person.groovy +++ /dev/null @@ -1,29 +0,0 @@ -package com.github.kuangcp.valatile -/** - * Created by https://github.com/kuangcp - * 指令重排问题 - * @author kuangcp* @date 18-4-2 上午9:20 - * - * https://www.hans-dev.com/java/double-checked-locking-is-broken.html - * http://gee.cs.oswego.edu/dl/cpj/jmm.html - * - */ -class Person { - - Person() { - } - private static Person instance = null - - // 双重校验锁, 这种方式是不能保证单例的 - // TODO 为何 - static Person getInstance() { - if (instance == null) { - synchronized (Person.class) { - if (instance == null) { - instance = new Person() - } - } - } - return instance - } -} \ No newline at end of file diff --git a/java-concurrency/src/main/java/com/github/kuangcp/valatile/ReSortDemo.groovy b/java-concurrency/src/main/java/com/github/kuangcp/valatile/ReSortDemo.groovy deleted file mode 100644 index 23c2df4d..00000000 --- a/java-concurrency/src/main/java/com/github/kuangcp/valatile/ReSortDemo.groovy +++ /dev/null @@ -1,70 +0,0 @@ -package com.github.kuangcp.valatile - -import java.util.concurrent.TimeUnit - -// 这种单例模式 https://www.oschina.net/question/2611757_2194452 -// DCL 写法 -class ReSortDemo { - - volatile static boolean stop - - static void main(String[] s) { - demo1() - demo2() - } - - static void demo2() { - new Thread() { - - @Override - void run() { - Person person - for (int i = 0; i < 5; i++) { - person = Person.getInstance() - println(person) - if (person != Person.getInstance()) { - println("不止一个实例") - } - } - } - }.start() - - new Thread() { - - @Override - void run() { - Person person - for (int i = 0; i < 3; i++) { - person = Person.getInstance() - println(person) - if (person != Person.getInstance()) { - println("不止一个实例") - } - } - } - }.start() - } - - static void demo1() { - Thread workThread = new Thread(new Runnable() { - - @Override - void run() { - int i = 0 - while (!stop) { - i++ - try { - TimeUnit.SECONDS.sleep(1) - } catch (InterruptedException e) { - e.printStackTrace() - } - println("i" + i) - } - } - }) - workThread.start() - TimeUnit.SECONDS.sleep(3) - stop = true - } -} - diff --git a/java-concurrency/src/main/java/com/github/kuangcp/valatile/RepeatRead.groovy b/java-concurrency/src/main/java/com/github/kuangcp/valatile/RepeatRead.groovy deleted file mode 100644 index 66a7f9e6..00000000 --- a/java-concurrency/src/main/java/com/github/kuangcp/valatile/RepeatRead.groovy +++ /dev/null @@ -1,31 +0,0 @@ -package com.github.kuangcp.valatile - -/** - * Created by https://github.com/kuangcp - * 多线程并发写和读 - * 怎么避免这种问题 ? - * TODO 解决方案 - * @author kuangcp - * @date 18-4-13 下午4:48 - */ -class RepeatRead { - volatile boolean close - - void start(String thread){ - if(!isClose()){ - return - } - setClose(false) - println("启动"+thread) - } - - void close(String thread){ - if(isClose()){ - return - } - setClose(true) - println("进行关闭"+thread) - } -} - - diff --git a/java-concurrency/src/main/java/com/github/kuangcp/valatile/RepeatReadDemo.groovy b/java-concurrency/src/main/java/com/github/kuangcp/valatile/RepeatReadDemo.groovy deleted file mode 100644 index c40e5d6a..00000000 --- a/java-concurrency/src/main/java/com/github/kuangcp/valatile/RepeatReadDemo.groovy +++ /dev/null @@ -1,38 +0,0 @@ -package com.github.kuangcp.valatile - -class RepeatReadDemo { - - // 输出没有 1 2 3 - static void main(String[] s) { - RepeatRead read = new RepeatRead() - new Thread(new Runnable() { - @Override - void run() { - println('线程1') - read.start('线程1') - sleep(300) - read.close('线程1') - } - }).start() - - new Thread(new Runnable() { - @Override - void run() { - println('线程2') - read.start('线程1') - sleep(1200) - read.close('线程2') - } - }).start() - - new Thread(new Runnable() { - @Override - void run() { - println('线程3') - read.start('线程1') - sleep(1300) - read.close('线程3') - } - }).start() - } -} \ No newline at end of file diff --git a/java-concurrency/src/main/java/com/github/kuangcp/valatile/ResortJavaDemo.java b/java-concurrency/src/main/java/com/github/kuangcp/valatile/ResortJavaDemo.java deleted file mode 100644 index b26dadf7..00000000 --- a/java-concurrency/src/main/java/com/github/kuangcp/valatile/ResortJavaDemo.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.github.kuangcp.valatile; - -import java.util.concurrent.TimeUnit; - -/** - * Created by https://github.com/kuangcp - * 因为怀疑是Groovy编译问题, 特意写了Java版, 还是正常的, 需要找另一个重排序Demo了 - * - * @author kuangcp - */ -public class ResortJavaDemo { - - private static boolean stop; - - public static void main(String[] s) throws InterruptedException { - Thread workThread = new Thread(() -> { - int i = 0; - while (!stop) { - i++; - try { - TimeUnit.SECONDS.sleep(1); - } catch (InterruptedException e) { - e.printStackTrace(); - } - System.out.println("i" + i); - } - }); - workThread.start(); - TimeUnit.SECONDS.sleep(3); - stop = true; - } -} diff --git a/java-concurrency/src/main/java/com/github/kuangcp/volatiles/FirstDemo.java b/java-concurrency/src/main/java/com/github/kuangcp/volatiles/FirstDemo.java new file mode 100644 index 00000000..503f5ba1 --- /dev/null +++ b/java-concurrency/src/main/java/com/github/kuangcp/volatiles/FirstDemo.java @@ -0,0 +1,61 @@ +package com.github.kuangcp.volatiles; + +import java.util.concurrent.TimeUnit; +import lombok.extern.slf4j.Slf4j; + +/** + * @author kuangcp on 2019-04-24 9:02 PM + */ +@Slf4j +class FirstDemo { + + private boolean stop = false; + + private boolean stopWithSleep = false; + private volatile boolean stopWithVolatile = false; + + void neverStop() { + while (!stop) { + } + + log.info("exit neverStop"); + } + + void stop() { + this.stop = true; + } + + // JVM 会尽力保证内存的可见性,即便这个变量没有加同步关键字。换句话说,只要 CPU 有时间,JVM 会尽力去保证变量值的更新。 + // 这种与 volatile 关键字的不同在于,volatile 关键字会强制的保证线程的可见性。 + // 使用 System.out.println(); 或者其他语句都是有可能达到效果, 只要能让CPU空闲下来 + void normalStopWithSleep() { + while (!stopWithSleep) { + try { + TimeUnit.MILLISECONDS.sleep(300); + } catch (InterruptedException e) { + e.printStackTrace(); + } + log.info("run with sleep"); + } + + log.info("exit normalStopWithSleep"); + } + + void stopWithSleep() { + this.stopWithSleep = true; + } + + + // 由于CPU调度的不可控性, 所以并不会在 stop 执行后就立马停掉 + void normalStopWithVolatile() { + while (!stopWithVolatile) { + } + + log.info("exit normalStopWithVolatile"); + } + + // 因为这个关键字保证了可见性, 所以能使得线程停下来 + void stopWithVolatile() { + this.stopWithVolatile = true; + } +} diff --git a/java-concurrency/src/test/java/com/github/kuangcp/volatiles/FirstDemoTest.java b/java-concurrency/src/test/java/com/github/kuangcp/volatiles/FirstDemoTest.java new file mode 100644 index 00000000..b9a487a4 --- /dev/null +++ b/java-concurrency/src/test/java/com/github/kuangcp/volatiles/FirstDemoTest.java @@ -0,0 +1,53 @@ +package com.github.kuangcp.volatiles; + +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; + +/** + * @author kuangcp on 2019-04-24 9:55 PM + */ +@Slf4j +public class FirstDemoTest { + + + @Test + public void testNeverStop() throws Exception { + FirstDemo demo = new FirstDemo(); + Thread thread = new Thread(demo::neverStop); + thread.start(); + + // 如果不加输出, 可能线程都还没创建起来, 这里就直接修改成了 false + log.info("prepare to stop"); + demo.stop(); + + thread.join(); + // 无法停止的原因是 JMM的原因 值在自己的缓存里, 所以这里改动了 stop 但是 thread 里的 stop 没有更新 + } + + @Test + public void testVolatile() throws InterruptedException { + FirstDemo demo = new FirstDemo(); + Thread thread = new Thread(demo::normalStopWithVolatile); + thread.start(); + + // 如果不加输出, 可能线程都还没创建起来, 这里就直接修改成了false + log.info("prepare to stop"); + demo.stopWithVolatile(); + + thread.join(); + } + + + @Test + public void testSleep() throws InterruptedException { + FirstDemo demo = new FirstDemo(); + Thread thread = new Thread(demo::normalStopWithSleep); + thread.start(); + + // 如果不加输出, 可能线程都还没创建起来, 这里就直接修改成了false + log.info("prepare to stop"); + demo.stopWithSleep(); + + thread.join(); + } +} \ No newline at end of file From 62e9dde94a48f42bc6537d001dd2c0d358a442c2 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Thu, 25 Apr 2019 13:41:17 +0800 Subject: [PATCH 020/476] -) delete thread module --- .../test/java/syntax/bytes/OperatorTest.java | 23 +++++++++++++++---- java-concurrency/build.gradle | 7 +++--- .../main/java/thread}/HowToCreateThread.java | 2 +- .../src/main/java/thread}/README.md | 2 +- .../ShowCreateThreadForSimpleMain.java | 2 +- .../java/thread}/ThreadInterruptedDemo.java | 2 +- .../src/main/java/thread}/ThreadJoinDemo.java | 2 +- .../java/thread}/ThreadStatusTransfer.java | 2 +- .../src/main/java/thread}/UseThreadPool.java | 2 +- .../src/main/java/thread}/order/README.md | 0 .../src/main/java/thread}/order/Task.java | 2 +- .../java/thread}/order/TaskWithVolatile.java | 2 +- .../main/java/thread}/pcstatus/Consumer.java | 2 +- .../src/main/java/thread}/pcstatus/Main.java | 2 +- .../main/java/thread}/pcstatus/Producer.java | 2 +- .../src/main/java/thread}/pcstatus/README.md | 0 .../src/main/java/thread}/pcstatus/Share.java | 2 +- .../thread}/ThreadStatusTransferTest.java | 6 ++--- .../java/thread}/order/OrderedThreadTest.java | 2 +- .../java/thread}/schdule/TimeTaskTest.java | 2 +- settings.gradle | 1 - 21 files changed, 41 insertions(+), 26 deletions(-) rename {java-thread/src/main/java/com/github/kuangcp => java-concurrency/src/main/java/thread}/HowToCreateThread.java (98%) rename {java-thread/src/main/java/com/github/kuangcp => java-concurrency/src/main/java/thread}/README.md (74%) rename {java-thread/src/main/java/com/github/kuangcp => java-concurrency/src/main/java/thread}/ShowCreateThreadForSimpleMain.java (96%) rename {java-thread/src/main/java/com/github/kuangcp => java-concurrency/src/main/java/thread}/ThreadInterruptedDemo.java (97%) rename {java-thread/src/main/java/com/github/kuangcp => java-concurrency/src/main/java/thread}/ThreadJoinDemo.java (97%) rename {java-thread/src/main/java/com/github/kuangcp => java-concurrency/src/main/java/thread}/ThreadStatusTransfer.java (98%) rename {java-thread/src/main/java/com/github/kuangcp => java-concurrency/src/main/java/thread}/UseThreadPool.java (97%) rename {java-thread/src/main/java/com/github/kuangcp => java-concurrency/src/main/java/thread}/order/README.md (100%) rename {java-thread/src/main/java/com/github/kuangcp => java-concurrency/src/main/java/thread}/order/Task.java (97%) rename {java-thread/src/main/java/com/github/kuangcp => java-concurrency/src/main/java/thread}/order/TaskWithVolatile.java (97%) rename {java-thread/src/main/java/com/github/kuangcp => java-concurrency/src/main/java/thread}/pcstatus/Consumer.java (92%) rename {java-thread/src/main/java/com/github/kuangcp => java-concurrency/src/main/java/thread}/pcstatus/Main.java (93%) rename {java-thread/src/main/java/com/github/kuangcp => java-concurrency/src/main/java/thread}/pcstatus/Producer.java (94%) rename {java-thread/src/main/java/com/github/kuangcp => java-concurrency/src/main/java/thread}/pcstatus/README.md (100%) rename {java-thread/src/main/java/com/github/kuangcp => java-concurrency/src/main/java/thread}/pcstatus/Share.java (97%) rename {java-thread/src/test/java/com/github/kuangcp => java-concurrency/src/test/java/thread}/ThreadStatusTransferTest.java (76%) rename {java-thread/src/test/java/com/github/kuangcp => java-concurrency/src/test/java/thread}/order/OrderedThreadTest.java (97%) rename {java-thread/src/test/java/com/github/kuangcp => java-concurrency/src/test/java/thread}/schdule/TimeTaskTest.java (95%) diff --git a/java-class/src/test/java/syntax/bytes/OperatorTest.java b/java-class/src/test/java/syntax/bytes/OperatorTest.java index 439e7c7d..adc86a9c 100644 --- a/java-class/src/test/java/syntax/bytes/OperatorTest.java +++ b/java-class/src/test/java/syntax/bytes/OperatorTest.java @@ -28,18 +28,33 @@ public void testAdd() { public void testToInt() { int a = 13232; // 直接赋值 无法编译通过 - byte b = (byte)a; + byte b = (byte) a; // int 赋值 byte 会截断后8位, 由于是有符号的, 结果是 10110000 // 原码则是 01010000 : 所以 b是-80 log.info("{}: {} -> {}", b, ShowBinary.toBinary(a), ShowBinary.toBinary(b)); } + // 和 int 一样的计算方式 @Test - public void testMod(){ + public void testMod() { byte a = -64; byte b = -4; - // TODO 取余操作 - log.info("{}", a%b); + + // 取余操作 a%b = a - (a/b)*b + log.info("a % b = {} \n a / b = {}\n(a / b) * b = {}\n a - (a / b) * b = {}", + a % b, a / b, (a / b) * b, a - (a / b) * b); + } + + @Test + public void testToString() { + byte[] bytes = "you".getBytes(); + + log.info("you={}", bytes); + // 默认采用 UTF8 编码 + String result = new String(bytes); + log.info("result={}", result); + + assertThat(result, equalTo("you")); } } diff --git a/java-concurrency/build.gradle b/java-concurrency/build.gradle index 5f4176ae..4bf22a9e 100644 --- a/java-concurrency/build.gradle +++ b/java-concurrency/build.gradle @@ -4,7 +4,8 @@ plugins { } dependencies { - implementation rootProject.libs['jedis'] - implementation rootProject.libs['groovy'] - implementation rootProject.libs['common-lang'] + implementation libs['jedis'] + implementation libs['groovy'] + implementation libs['common-lang'] + testCompile libs['testng'] } \ No newline at end of file diff --git a/java-thread/src/main/java/com/github/kuangcp/HowToCreateThread.java b/java-concurrency/src/main/java/thread/HowToCreateThread.java similarity index 98% rename from java-thread/src/main/java/com/github/kuangcp/HowToCreateThread.java rename to java-concurrency/src/main/java/thread/HowToCreateThread.java index a968cab5..e1ea8b79 100644 --- a/java-thread/src/main/java/com/github/kuangcp/HowToCreateThread.java +++ b/java-concurrency/src/main/java/thread/HowToCreateThread.java @@ -1,4 +1,4 @@ -package com.github.kuangcp; +package thread; /** * Created by https://github.com/kuangcp on 17-8-20 下午8:44 diff --git a/java-thread/src/main/java/com/github/kuangcp/README.md b/java-concurrency/src/main/java/thread/README.md similarity index 74% rename from java-thread/src/main/java/com/github/kuangcp/README.md rename to java-concurrency/src/main/java/thread/README.md index 3d0a4295..58d333b9 100644 --- a/java-thread/src/main/java/com/github/kuangcp/README.md +++ b/java-concurrency/src/main/java/thread/README.md @@ -1,2 +1,2 @@ # 线程的基础学习 -> [笔记详情](https://github.com/Kuangcp/Note/blob/master/Java/AdvancedLearning/Thread.md) \ No newline at end of file +> [笔记详情](https://github.com/Kuangcp/Note/blob/master/Java/AdvancedLearning/Thread.md) diff --git a/java-thread/src/main/java/com/github/kuangcp/ShowCreateThreadForSimpleMain.java b/java-concurrency/src/main/java/thread/ShowCreateThreadForSimpleMain.java similarity index 96% rename from java-thread/src/main/java/com/github/kuangcp/ShowCreateThreadForSimpleMain.java rename to java-concurrency/src/main/java/thread/ShowCreateThreadForSimpleMain.java index 5d086b0f..b80c0a99 100644 --- a/java-thread/src/main/java/com/github/kuangcp/ShowCreateThreadForSimpleMain.java +++ b/java-concurrency/src/main/java/thread/ShowCreateThreadForSimpleMain.java @@ -1,4 +1,4 @@ -package com.github.kuangcp; +package thread; import java.lang.management.ManagementFactory; import java.lang.management.ThreadInfo; diff --git a/java-thread/src/main/java/com/github/kuangcp/ThreadInterruptedDemo.java b/java-concurrency/src/main/java/thread/ThreadInterruptedDemo.java similarity index 97% rename from java-thread/src/main/java/com/github/kuangcp/ThreadInterruptedDemo.java rename to java-concurrency/src/main/java/thread/ThreadInterruptedDemo.java index fb1b3f02..c411ee4a 100644 --- a/java-thread/src/main/java/com/github/kuangcp/ThreadInterruptedDemo.java +++ b/java-concurrency/src/main/java/thread/ThreadInterruptedDemo.java @@ -1,4 +1,4 @@ -package com.github.kuangcp; +package thread; import java.util.concurrent.TimeUnit; import lombok.extern.slf4j.Slf4j; diff --git a/java-thread/src/main/java/com/github/kuangcp/ThreadJoinDemo.java b/java-concurrency/src/main/java/thread/ThreadJoinDemo.java similarity index 97% rename from java-thread/src/main/java/com/github/kuangcp/ThreadJoinDemo.java rename to java-concurrency/src/main/java/thread/ThreadJoinDemo.java index 11a678d7..4c86777d 100644 --- a/java-thread/src/main/java/com/github/kuangcp/ThreadJoinDemo.java +++ b/java-concurrency/src/main/java/thread/ThreadJoinDemo.java @@ -1,4 +1,4 @@ -package com.github.kuangcp; +package thread; import java.util.concurrent.TimeUnit; import lombok.extern.slf4j.Slf4j; diff --git a/java-thread/src/main/java/com/github/kuangcp/ThreadStatusTransfer.java b/java-concurrency/src/main/java/thread/ThreadStatusTransfer.java similarity index 98% rename from java-thread/src/main/java/com/github/kuangcp/ThreadStatusTransfer.java rename to java-concurrency/src/main/java/thread/ThreadStatusTransfer.java index bc6fd0d6..d0780d65 100644 --- a/java-thread/src/main/java/com/github/kuangcp/ThreadStatusTransfer.java +++ b/java-concurrency/src/main/java/thread/ThreadStatusTransfer.java @@ -1,4 +1,4 @@ -package com.github.kuangcp; +package thread; import lombok.extern.slf4j.Slf4j; diff --git a/java-thread/src/main/java/com/github/kuangcp/UseThreadPool.java b/java-concurrency/src/main/java/thread/UseThreadPool.java similarity index 97% rename from java-thread/src/main/java/com/github/kuangcp/UseThreadPool.java rename to java-concurrency/src/main/java/thread/UseThreadPool.java index 9c7097e9..03153486 100644 --- a/java-thread/src/main/java/com/github/kuangcp/UseThreadPool.java +++ b/java-concurrency/src/main/java/thread/UseThreadPool.java @@ -1,4 +1,4 @@ -package com.github.kuangcp; +package thread; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; diff --git a/java-thread/src/main/java/com/github/kuangcp/order/README.md b/java-concurrency/src/main/java/thread/order/README.md similarity index 100% rename from java-thread/src/main/java/com/github/kuangcp/order/README.md rename to java-concurrency/src/main/java/thread/order/README.md diff --git a/java-thread/src/main/java/com/github/kuangcp/order/Task.java b/java-concurrency/src/main/java/thread/order/Task.java similarity index 97% rename from java-thread/src/main/java/com/github/kuangcp/order/Task.java rename to java-concurrency/src/main/java/thread/order/Task.java index 04acb306..49158f16 100644 --- a/java-thread/src/main/java/com/github/kuangcp/order/Task.java +++ b/java-concurrency/src/main/java/thread/order/Task.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.order; +package thread.order; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; diff --git a/java-thread/src/main/java/com/github/kuangcp/order/TaskWithVolatile.java b/java-concurrency/src/main/java/thread/order/TaskWithVolatile.java similarity index 97% rename from java-thread/src/main/java/com/github/kuangcp/order/TaskWithVolatile.java rename to java-concurrency/src/main/java/thread/order/TaskWithVolatile.java index f8dcf0d6..0b9efc42 100644 --- a/java-thread/src/main/java/com/github/kuangcp/order/TaskWithVolatile.java +++ b/java-concurrency/src/main/java/thread/order/TaskWithVolatile.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.order; +package thread.order; import lombok.extern.slf4j.Slf4j; diff --git a/java-thread/src/main/java/com/github/kuangcp/pcstatus/Consumer.java b/java-concurrency/src/main/java/thread/pcstatus/Consumer.java similarity index 92% rename from java-thread/src/main/java/com/github/kuangcp/pcstatus/Consumer.java rename to java-concurrency/src/main/java/thread/pcstatus/Consumer.java index 7e680dfd..7cc0c07d 100644 --- a/java-thread/src/main/java/com/github/kuangcp/pcstatus/Consumer.java +++ b/java-concurrency/src/main/java/thread/pcstatus/Consumer.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.pcstatus; +package thread.pcstatus; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; diff --git a/java-thread/src/main/java/com/github/kuangcp/pcstatus/Main.java b/java-concurrency/src/main/java/thread/pcstatus/Main.java similarity index 93% rename from java-thread/src/main/java/com/github/kuangcp/pcstatus/Main.java rename to java-concurrency/src/main/java/thread/pcstatus/Main.java index 07b27a97..8d408d78 100644 --- a/java-thread/src/main/java/com/github/kuangcp/pcstatus/Main.java +++ b/java-concurrency/src/main/java/thread/pcstatus/Main.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.pcstatus; +package thread.pcstatus; /** * Created by https://github.com/kuangcp on 18-1-4 下午12:02 diff --git a/java-thread/src/main/java/com/github/kuangcp/pcstatus/Producer.java b/java-concurrency/src/main/java/thread/pcstatus/Producer.java similarity index 94% rename from java-thread/src/main/java/com/github/kuangcp/pcstatus/Producer.java rename to java-concurrency/src/main/java/thread/pcstatus/Producer.java index f83d28e2..02295402 100644 --- a/java-thread/src/main/java/com/github/kuangcp/pcstatus/Producer.java +++ b/java-concurrency/src/main/java/thread/pcstatus/Producer.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.pcstatus; +package thread.pcstatus; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; diff --git a/java-thread/src/main/java/com/github/kuangcp/pcstatus/README.md b/java-concurrency/src/main/java/thread/pcstatus/README.md similarity index 100% rename from java-thread/src/main/java/com/github/kuangcp/pcstatus/README.md rename to java-concurrency/src/main/java/thread/pcstatus/README.md diff --git a/java-thread/src/main/java/com/github/kuangcp/pcstatus/Share.java b/java-concurrency/src/main/java/thread/pcstatus/Share.java similarity index 97% rename from java-thread/src/main/java/com/github/kuangcp/pcstatus/Share.java rename to java-concurrency/src/main/java/thread/pcstatus/Share.java index a98d1c8c..2a645d5f 100644 --- a/java-thread/src/main/java/com/github/kuangcp/pcstatus/Share.java +++ b/java-concurrency/src/main/java/thread/pcstatus/Share.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.pcstatus; +package thread.pcstatus; import lombok.extern.slf4j.Slf4j; diff --git a/java-thread/src/test/java/com/github/kuangcp/ThreadStatusTransferTest.java b/java-concurrency/src/test/java/thread/ThreadStatusTransferTest.java similarity index 76% rename from java-thread/src/test/java/com/github/kuangcp/ThreadStatusTransferTest.java rename to java-concurrency/src/test/java/thread/ThreadStatusTransferTest.java index 0de36fc4..a7eb4f10 100644 --- a/java-thread/src/test/java/com/github/kuangcp/ThreadStatusTransferTest.java +++ b/java-concurrency/src/test/java/thread/ThreadStatusTransferTest.java @@ -1,9 +1,9 @@ -package com.github.kuangcp; +package thread; -import com.github.kuangcp.ThreadStatusTransfer.Notify; -import com.github.kuangcp.ThreadStatusTransfer.Wait; import java.util.concurrent.TimeUnit; import org.junit.Test; +import thread.ThreadStatusTransfer.Notify; +import thread.ThreadStatusTransfer.Wait; /** * @author kuangcp on 2019-04-22 9:40 AM diff --git a/java-thread/src/test/java/com/github/kuangcp/order/OrderedThreadTest.java b/java-concurrency/src/test/java/thread/order/OrderedThreadTest.java similarity index 97% rename from java-thread/src/test/java/com/github/kuangcp/order/OrderedThreadTest.java rename to java-concurrency/src/test/java/thread/order/OrderedThreadTest.java index d9d90d32..ef2d5bbf 100644 --- a/java-thread/src/test/java/com/github/kuangcp/order/OrderedThreadTest.java +++ b/java-concurrency/src/test/java/thread/order/OrderedThreadTest.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.order; +package thread.order; import java.util.concurrent.atomic.AtomicInteger; import org.junit.Test; diff --git a/java-thread/src/test/java/com/github/kuangcp/schdule/TimeTaskTest.java b/java-concurrency/src/test/java/thread/schdule/TimeTaskTest.java similarity index 95% rename from java-thread/src/test/java/com/github/kuangcp/schdule/TimeTaskTest.java rename to java-concurrency/src/test/java/thread/schdule/TimeTaskTest.java index 0fded3bd..7ec50a35 100644 --- a/java-thread/src/test/java/com/github/kuangcp/schdule/TimeTaskTest.java +++ b/java-concurrency/src/test/java/thread/schdule/TimeTaskTest.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.schdule; +package thread.schdule; import java.util.Timer; import java.util.TimerTask; diff --git a/settings.gradle b/settings.gradle index b78497b2..9157a6a1 100644 --- a/settings.gradle +++ b/settings.gradle @@ -6,7 +6,6 @@ include( , 'java-class' , 'java-gui' , 'java-network' - , 'java-thread' , 'java-algorithms' , 'java-question' , 'java-pattern' From c21f0d861c907d48cb810e577e49ef597733f701 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Thu, 25 Apr 2019 14:42:33 +0800 Subject: [PATCH 021/476] *) update readme --- README.md | 31 ++-- .../{FirstDemo.java => NeverStopThread.java} | 3 +- ...DemoTest.java => NeverStopThreadTest.java} | 9 +- .../com/github/kuangcp/notepad/ICommand.java | 10 ++ .../java/com/github/kuangcp/notepad/Note.java | 143 ++++++++++++++++++ java-io/build.gradle | 4 +- .../kuangcp/{inout => file}/CopyFile.java | 2 +- .../java/com/github/kuangcp/file/CutTxt.java | 127 ---------------- .../java/com/github/kuangcp/file/Lock.java | 84 ---------- .../java/com/github/kuangcp/file/Note.java | 132 ---------------- .../com/github/kuangcp/standard/Readme.md | 5 + java-io/src/test/java/buffer/BufferTest.java | 36 ++--- .../com/github/kuangcp/PropertiesTest.java | 4 +- .../kuangcp/{inout => file}/CopyFileTest.java | 2 +- 14 files changed, 206 insertions(+), 386 deletions(-) rename java-concurrency/src/main/java/com/github/kuangcp/volatiles/{FirstDemo.java => NeverStopThread.java} (98%) rename java-concurrency/src/test/java/com/github/kuangcp/volatiles/{FirstDemoTest.java => NeverStopThreadTest.java} (87%) create mode 100644 java-gui/src/main/java/com/github/kuangcp/notepad/ICommand.java create mode 100644 java-gui/src/main/java/com/github/kuangcp/notepad/Note.java rename java-io/src/main/java/com/github/kuangcp/{inout => file}/CopyFile.java (99%) delete mode 100644 java-io/src/main/java/com/github/kuangcp/file/CutTxt.java delete mode 100644 java-io/src/main/java/com/github/kuangcp/file/Lock.java delete mode 100644 java-io/src/main/java/com/github/kuangcp/file/Note.java create mode 100644 java-io/src/main/java/com/github/kuangcp/standard/Readme.md rename java-io/src/test/java/com/github/kuangcp/{inout => file}/CopyFileTest.java (98%) diff --git a/README.md b/README.md index 00a92dd7..c49edcfe 100644 --- a/README.md +++ b/README.md @@ -1,29 +1,32 @@ -# JavaBase +# Java 基础学习 [![Java Version](https://img.shields.io/badge/JDK-Java%208-red.svg)](https://www.java.com/zh_CN/download/) -[![Gradle 5.3](https://img.shields.io/badge/Gradle-5.2-green.svg)](https://docs.gradle.org/5.3/userguide/userguide.html) +[![Gradle 5.3](https://img.shields.io/badge/Gradle-5.3-green.svg)](https://docs.gradle.org/5.3/userguide/userguide.html) [![996.icu](https://img.shields.io/badge/link-996.icu-red.svg)](https://996.icu) [![codebeat badge](https://codebeat.co/badges/9145f9a8-a1aa-4c67-bb2b-f9dd12e924d4)](https://codebeat.co/projects/github-com-kuangcp-javabase-master) [![Maintainability](https://api.codeclimate.com/v1/badges/23134c0d2348845fecec/maintainability)](https://codeclimate.com/github/Kuangcp/JavaBase/maintainability) +************************ + | 基础 | 进阶 | 应用框架 | |:----|:----|:----| -| [Java8](/java-8) | [线程](/java-thread) | [Netty](/java-netty)| -| [集合](/java-collection)|[并发](/java-concurrency)| [Guava](/java-guava)| -| [算法](/java-algorithms)| [设计模式](/java-pattern)| -| [字节码](/java-class)| [网络](/java-network)| -| [泛型](/java-generic)| [测试](/java-test)| -| [IO](/java-io)| [Spring](/java-spring) | +| [Java8](/java-8) | [并发](/java-concurrency) | [Netty](/java-netty)| +| [集合](/java-collection) | [网络](/java-network) | [Guava](/java-guava)| +| [算法](/java-algorithms) | [设计模式](/java-pattern) | [Spring](/java-spring) +| [字节码](/java-class) | [测试](/java-test) | +| [泛型](/java-generic) | | +| [IO](/java-io) | | | [图形化](/java-gui) | [实际问题](/java-question) -************** - +************************ > 使用了Gradle作为构建工具, 将知识点分为了多个子项目, 然后将所有的依赖项放在了 `dependency.gradle`中 > 其他的子项目引用全局定义的依赖, 实现了依赖的统一管理 -# 同类仓库 -> [tutorials](https://github.com/eugenp/tutorials) -> [Java 学习笔记](https://github.com/brianway/java-learning) -> [demo](https://gitee.com/code4everything/demo) +************************ + +## 同类仓库 +> [tutorials](https://github.com/eugenp/tutorials) +> [Java 学习笔记](https://github.com/brianway/java-learning) +> [demo](https://gitee.com/code4everything/demo) diff --git a/java-concurrency/src/main/java/com/github/kuangcp/volatiles/FirstDemo.java b/java-concurrency/src/main/java/com/github/kuangcp/volatiles/NeverStopThread.java similarity index 98% rename from java-concurrency/src/main/java/com/github/kuangcp/volatiles/FirstDemo.java rename to java-concurrency/src/main/java/com/github/kuangcp/volatiles/NeverStopThread.java index 503f5ba1..a820d428 100644 --- a/java-concurrency/src/main/java/com/github/kuangcp/volatiles/FirstDemo.java +++ b/java-concurrency/src/main/java/com/github/kuangcp/volatiles/NeverStopThread.java @@ -7,7 +7,7 @@ * @author kuangcp on 2019-04-24 9:02 PM */ @Slf4j -class FirstDemo { +class NeverStopThread { private boolean stop = false; @@ -45,7 +45,6 @@ void stopWithSleep() { this.stopWithSleep = true; } - // 由于CPU调度的不可控性, 所以并不会在 stop 执行后就立马停掉 void normalStopWithVolatile() { while (!stopWithVolatile) { diff --git a/java-concurrency/src/test/java/com/github/kuangcp/volatiles/FirstDemoTest.java b/java-concurrency/src/test/java/com/github/kuangcp/volatiles/NeverStopThreadTest.java similarity index 87% rename from java-concurrency/src/test/java/com/github/kuangcp/volatiles/FirstDemoTest.java rename to java-concurrency/src/test/java/com/github/kuangcp/volatiles/NeverStopThreadTest.java index b9a487a4..8104531c 100644 --- a/java-concurrency/src/test/java/com/github/kuangcp/volatiles/FirstDemoTest.java +++ b/java-concurrency/src/test/java/com/github/kuangcp/volatiles/NeverStopThreadTest.java @@ -7,12 +7,11 @@ * @author kuangcp on 2019-04-24 9:55 PM */ @Slf4j -public class FirstDemoTest { - +public class NeverStopThreadTest { @Test public void testNeverStop() throws Exception { - FirstDemo demo = new FirstDemo(); + NeverStopThread demo = new NeverStopThread(); Thread thread = new Thread(demo::neverStop); thread.start(); @@ -26,7 +25,7 @@ public void testNeverStop() throws Exception { @Test public void testVolatile() throws InterruptedException { - FirstDemo demo = new FirstDemo(); + NeverStopThread demo = new NeverStopThread(); Thread thread = new Thread(demo::normalStopWithVolatile); thread.start(); @@ -40,7 +39,7 @@ public void testVolatile() throws InterruptedException { @Test public void testSleep() throws InterruptedException { - FirstDemo demo = new FirstDemo(); + NeverStopThread demo = new NeverStopThread(); Thread thread = new Thread(demo::normalStopWithSleep); thread.start(); diff --git a/java-gui/src/main/java/com/github/kuangcp/notepad/ICommand.java b/java-gui/src/main/java/com/github/kuangcp/notepad/ICommand.java new file mode 100644 index 00000000..b76b404f --- /dev/null +++ b/java-gui/src/main/java/com/github/kuangcp/notepad/ICommand.java @@ -0,0 +1,10 @@ +package com.github.kuangcp.notepad; + +/** + * @author kuangcp on 2019-04-25 1:55 PM + */ +public interface ICommand { + + String OPEN_FILE = "OPEN_FILE"; + String SAVE_FILE = "SAVE_FILE"; +} diff --git a/java-gui/src/main/java/com/github/kuangcp/notepad/Note.java b/java-gui/src/main/java/com/github/kuangcp/notepad/Note.java new file mode 100644 index 00000000..bd197058 --- /dev/null +++ b/java-gui/src/main/java/com/github/kuangcp/notepad/Note.java @@ -0,0 +1,143 @@ +package com.github.kuangcp.notepad; + +import com.github.kuangcp.io.ResourceTool; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.util.Objects; +import javax.swing.ImageIcon; +import javax.swing.JFileChooser; +import javax.swing.JFrame; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; +import javax.swing.JScrollPane; +import javax.swing.JTextArea; +import javax.swing.WindowConstants; +import lombok.extern.slf4j.Slf4j; + +/** + * Created by https://github.com/kuangcp on 17-8-22 下午2:39 + */ +@Slf4j +class Note extends JFrame implements ActionListener { + + private JTextArea textArea; + + public static void main(String[] args) { + new Note().init(); + } + + private void init() { + textArea = new JTextArea(); + JMenuBar menuBar = new JMenuBar(); + this.setJMenuBar(menuBar); + + JMenu fileMenu = new JMenu("文件"); + fileMenu.setMnemonic('F'); + + JMenu saveMenu = new JMenu("保存"); + + JMenuItem jmi1 = new JMenuItem("打开", new ImageIcon()); + JMenuItem jmi2 = new JMenuItem("保存"); + + //注册监听 + jmi1.addActionListener(this); + jmi1.setActionCommand(ICommand.OPEN_FILE); + jmi2.addActionListener(this); + jmi2.setActionCommand(ICommand.SAVE_FILE); + + //把jm1放入jmb + menuBar.add(fileMenu); + menuBar.add(saveMenu); + + //把item放入到Menu + fileMenu.add(jmi1); + saveMenu.add(jmi2); + + JScrollPane jsp = new JScrollPane(textArea); + + this.add(jsp); + this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); + this.setSize(400, 300); + this.setVisible(true); + } + + public void actionPerformed(ActionEvent event) { + switch (event.getActionCommand()) { + case ICommand.OPEN_FILE: + openFile(); + break; + case ICommand.SAVE_FILE: + saveFile(); + break; + default: + log.info("not supported command"); + break; + } + } + + private void openFile() { + //推荐JFileChooser 组件 + JFileChooser jc = new JFileChooser(); + //设置名字 + jc.setDialogTitle("请选择文件..."); + //默认方式 + jc.showOpenDialog(null); + jc.setVisible(true); + + //得知道用户选择的文件 绝对路径 + String filePath = jc.getSelectedFile().getAbsolutePath(); + BufferedReader br = null; + try { + br = new BufferedReader(new FileReader(filePath)); + + //从文件读取信息显示到jta + String s; + StringBuilder result = new StringBuilder(); + while ((s = br.readLine()) != null) { + result.append(s).append("\n"); + } + + //输出到 textArea + textArea.setText(result.toString()); + } catch (Exception e) { + log.error(e.getMessage(), e); + } finally { + try { + ResourceTool.close(br); + } catch (IOException e) { + log.error(e.getMessage(), e); + } + } + } + + private void saveFile() { + JFileChooser chooser = new JFileChooser(); + chooser.setDialogTitle("另存为..."); + + //按默认方式显示 + chooser.showSaveDialog(null); + chooser.setVisible(true); + + //得到用户希望把文件保存到何处 + File file = chooser.getSelectedFile(); + if (Objects.isNull(file)) { + log.warn("please select file: chooser={}", chooser); + return; + } + + //准备写入到指定目录下 + String path = file.getAbsolutePath(); + try (BufferedWriter bw = new BufferedWriter(new FileWriter(path))) { + bw.write(this.textArea.getText()); + } catch (Exception e) { + log.error(e.getMessage(), e); + } + } +} diff --git a/java-io/build.gradle b/java-io/build.gradle index 578eb9df..65fd0f7b 100644 --- a/java-io/build.gradle +++ b/java-io/build.gradle @@ -1,3 +1,3 @@ -dependencies{ - compile rootProject.libs['common-lang'] +dependencies { + compile libs['common-lang'] } diff --git a/java-io/src/main/java/com/github/kuangcp/inout/CopyFile.java b/java-io/src/main/java/com/github/kuangcp/file/CopyFile.java similarity index 99% rename from java-io/src/main/java/com/github/kuangcp/inout/CopyFile.java rename to java-io/src/main/java/com/github/kuangcp/file/CopyFile.java index 7089f38e..a8113fcd 100644 --- a/java-io/src/main/java/com/github/kuangcp/inout/CopyFile.java +++ b/java-io/src/main/java/com/github/kuangcp/file/CopyFile.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.inout; +package com.github.kuangcp.file; import com.github.kuangcp.io.ResourceTool; import java.io.BufferedReader; diff --git a/java-io/src/main/java/com/github/kuangcp/file/CutTxt.java b/java-io/src/main/java/com/github/kuangcp/file/CutTxt.java deleted file mode 100644 index beaf3970..00000000 --- a/java-io/src/main/java/com/github/kuangcp/file/CutTxt.java +++ /dev/null @@ -1,127 +0,0 @@ -package com.github.kuangcp.file; - -import java.io.*; -import java.nio.charset.Charset; -import java.util.ArrayList; -import java.util.List; - -/** - * 搞定,成功在于找到特点,然后拆分,保存 - * Created by Myth on 2017/1/10 0010 - 22:25 - */ -public class CutTxt { - - //拆分文件根目录 - static String PATH = "E:\\GCDD\\"; - //源文件目录 - static String realFile="E:\\GCD.txt"; - static List rows = new ArrayList(); - static List titles = new ArrayList(); - public static void main(String[] a) { - ReadFile(); - for(int i=0;i chapters = new ArrayList(); - String content = null; - - //读取直到文件最末尾 - while ((content = br.readLine()) != null) { - count++; - //截取失败就返回原字符串 所以长度是1 -// String [] s = content.split("javascript"); -// System.out.println(count+" : "+s.length); - String test[] = content.split("javascript"); - if(test.length==1){ - chapters.add(content); -// System.out.println(count); - }else{ - //截取到了章节位置 - if(count>2) - //SaveFile(chapters); - chapters.clear(); -// System.out.println(count+"截取成功"); - chapters.add(count+""); - rows.add(count+""); - titles.add("【"+test[0]+" 】 "+test[1]); - //chapters.add(count+" "+runable[0]+" "+runable[1]); - } - -// System.out.println(count + "." + content); -// new String(content.getBytes("GBK"),"UTF-8") - } -// System.out.println(count+"循环退出,到达文件尾"); - //SaveFile(chapters); - } catch (IOException e) { - e.printStackTrace(); - } finally {//从里到外的封装关系 来关闭流 一旦顺序错了就有莫名奇妙的错误 - try { - input.close(); - ids.close(); - br.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - } - //将数组转存成文件 - public static void SaveFile(List datas){ - BufferedWriter bw = null; - OutputStream out = null; - OutputStreamWriter os = null; -// for(String e:dataStack){ -// System.out.println(e); -// } - if(datas.size()>1) { - try { - String filename = PATH + datas.get(0) + ".txt"; - filename.split(""); - System.out.println("文件名:"+filename); - if(filename.length()<40) { - out = new FileOutputStream(filename.trim()); - } - os = new OutputStreamWriter(out); - bw = new BufferedWriter(os); - - for (int i = 0; i < datas.size(); i++) { - bw.write(datas.get(i)+"\n"); - } - } catch (Exception e) { - e.printStackTrace(); - System.out.println("IO有异常"); - } finally { - try { - if (bw != null) bw.close(); - if (os != null) os.close(); - if (out != null) out.close(); - } catch (Exception e2) { - e2.printStackTrace(); - System.out.println("资源关闭异常"); - } - } - } - } -} diff --git a/java-io/src/main/java/com/github/kuangcp/file/Lock.java b/java-io/src/main/java/com/github/kuangcp/file/Lock.java deleted file mode 100644 index b30de4e7..00000000 --- a/java-io/src/main/java/com/github/kuangcp/file/Lock.java +++ /dev/null @@ -1,84 +0,0 @@ -package com.github.kuangcp.file; - -import java.util.Scanner; - -/* - * 密码锁 combination lock: - * 密码(三个数字的序列)是隐藏的,通过密码可以解锁,通过组合可以改变密码 - * 但只有当前知道密码的人才能改变密码,设计一个类,具有公有方法open,changeCombo - * 和保存密码的私有数据字段。密码应该在构造函数中 - */ -class LockUse{ - - String file;//密码保护的内容 - boolean pass = false;//记录是否正确输入密码 - private String code;//密码 - - public void Pass(String s){//判断密码是否正确 - if (s.equals(code)){ - this.pass = true; - System.out.println("密码正确!"); - } - else {this.pass = false;System.out.println("密码错误!");}//this.pass = false; - //这句话就是不能少的,当你更改密码后是不能缺少的重置状态语句 - } - public void openfile(){//打开保护的文件 - if (this.pass){System.out.println("内容就是:"+this.file);} - else System.out.println("@@@请先验证密码!!!"); - } - public void changeCombo(String str){//更改密码 - if (this.pass){ - this.code = str; - } - else {System.out.println("_________请先验证密码:"); - Scanner s = new Scanner(System.in);String strs; - while (!this.pass){System.out.println("请输入密码:"); - strs = s.nextLine(); - this.Pass(strs); - } - } - - } - public LockUse(String file){//构造器初始化文件,密码 - this.file = file; - this.code = "123"; - } -} - -public class Lock {//用于实现的类 - - public static void main(String[] args) { - //while (menu()){} - //不能用函数多次调用,因为每次调用都是一个新的栈,就算登录了系统但是没有被保存登录状态 - menu(); - System.out.println("您已经退出系统!"); - } - public static void menu(){ - LockUse Q = new LockUse("你居然知道了密码");// 创建对象总是忘了加 new关键字!!!!!!!!!!!!! - String strs; - Scanner s = new Scanner(System.in); - - while (true){ - System.out.println("\n##请输入要做的操作:\n##1.验证密码\n##2.打开文件\n##3.更改密码"); - strs = s.nextLine(); - - switch (strs){ - case "1":{ - System.out.println("请输入密码:"); - strs = s.nextLine(); - Q.Pass(strs);}break; - case "2": - Q.openfile();break; - case "3":{ - System.out.println("请输入您要更改的密码:"); - String p = s.nextLine(); - Q.changeCombo(p); - }break; - default : System.out.println("请输入0-2的选择!");break; - } - - } - } - -} - diff --git a/java-io/src/main/java/com/github/kuangcp/file/Note.java b/java-io/src/main/java/com/github/kuangcp/file/Note.java deleted file mode 100644 index 678027b9..00000000 --- a/java-io/src/main/java/com/github/kuangcp/file/Note.java +++ /dev/null @@ -1,132 +0,0 @@ -package com.github.kuangcp.file; - -import javax.swing.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.io.BufferedReader; -import java.io.BufferedWriter; -import java.io.FileReader; -import java.io.FileWriter; - -/** - * Created by https://github.com/kuangcp on 17-8-22 下午2:39 - */ -public class Note extends JFrame implements ActionListener { - - //定义所需组件 - private JTextArea jta; - - public static void main(String[] args) { - Note n = new Note(); - } - - private Note() { - jta = new JTextArea(); - JMenuBar jmb = new JMenuBar(); - JMenu jm1 = new JMenu("打开"); - JMenu jm2 = new JMenu("保存"); - - jm1.setMnemonic('F'); - JMenuItem jmi1 = new JMenuItem("打开", new ImageIcon()); - JMenuItem jmi2 = new JMenuItem("保存"); - - //注册监听 - jmi1.addActionListener(this); - jmi1.setActionCommand("打开"); - jmi2.addActionListener(this); - jmi2.setActionCommand("保存"); - //加入 - this.setJMenuBar(jmb); - //把jm1放入jmb - jmb.add(jm1); - jmb.add(jm2); - //把item放入到Menu - jm1.add(jmi1); - jm2.add(jmi2); -// jm1.add(jmi2); - - JScrollPane jsp = new JScrollPane(jta); - - this.add(jsp); - this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); - this.setSize(400, 300); - this.setVisible(true); - } - - public void actionPerformed(ActionEvent N) { - if (N.getActionCommand().equals("打开")) { - openFile(); - } else if (N.getActionCommand().equals("保存")) { - saveFile(); - } - } - - private void openFile() { - System.out.println("打开"); - //推荐JFileChooser 组件 - JFileChooser jc = new JFileChooser(); - //设置名字 - jc.setDialogTitle("请选择文件..."); - //默认方式 - jc.showOpenDialog(null); - jc.setVisible(true); - - //得知道用户选择的文件 绝对路径 - String filePath = jc.getSelectedFile().getAbsolutePath(); - BufferedReader br = null; - try { - br = new BufferedReader(new FileReader(filePath)); - - //从文件读取信息显示到jta - String s = ""; - StringBuilder result = new StringBuilder(); - while ((s = br.readLine()) != null) { - - result.append(s).append("\r\n"); - } - //输出到jta - jta.setText(result.toString()); - } catch (Exception e) { - e.printStackTrace(); - } finally { - try { - if (br != null) { - br.close(); - } - } catch (Exception e2) { - e2.printStackTrace(); - } - } - } - - private void saveFile() { - System.out.println("保存"); - //出现保存对话框 - JFileChooser jc = new JFileChooser(); - jc.setDialogTitle("另存为..."); - //按默认方式显示 - jc.showSaveDialog(null); - jc.setVisible(true); - - //得到用户希望把文件保存到何处 - String file = jc.getSelectedFile().getAbsolutePath(); - BufferedWriter bw = null; - //准备写入到指定目录下 - try { - bw = new BufferedWriter(new FileWriter(file)); - - bw.write(this.jta.getText()); - - } catch (Exception e) { - e.printStackTrace(); - } finally { - try { - if (bw != null) { - bw.close(); - } - } catch (Exception e2) { - } - } - - } -} diff --git a/java-io/src/main/java/com/github/kuangcp/standard/Readme.md b/java-io/src/main/java/com/github/kuangcp/standard/Readme.md new file mode 100644 index 00000000..0755868f --- /dev/null +++ b/java-io/src/main/java/com/github/kuangcp/standard/Readme.md @@ -0,0 +1,5 @@ +# 标准输入输出 + +http://www.runoob.com/java/java-files-io.html + +http://ifeve.com/java-io/ diff --git a/java-io/src/test/java/buffer/BufferTest.java b/java-io/src/test/java/buffer/BufferTest.java index 85882858..02468992 100644 --- a/java-io/src/test/java/buffer/BufferTest.java +++ b/java-io/src/test/java/buffer/BufferTest.java @@ -1,30 +1,32 @@ package buffer; +import lombok.extern.slf4j.Slf4j; import org.junit.Test; import java.nio.IntBuffer; /** * Created by https://github.com/kuangcp - * + * TODO StringBuffer * @author kuangcp - * @date 18-3-19 上午11:42 */ +@Slf4j public class BufferTest { - /** - * flip 方法直译就是翻转的意思, 也就是说在读写之间切换, 做好准备工作 - */ - @Test - public void testFlip(){ - IntBuffer buffer = IntBuffer.allocate(1024); - buffer.put(1); - // 需要使用flip方法来进行模式的切换, 如果没有做读写操作就直接调用就会抛出 BufferOverflowException 异常 - // 也就是说, 在写和读操作后, 需要调用该方法才能进行下一步的写和读 - buffer.flip(); - buffer.put(2); - buffer.flip(); - int result = buffer.get(); - System.out.println(result); - } + /** + * flip 方法直译就是翻转的意思, 也就是说在读写之间切换, 做好准备工作 + */ + @Test + public void testFlip() { + IntBuffer buffer = IntBuffer.allocate(1024); + buffer.put(1); + // 需要使用flip方法来进行模式的切换, 如果没有做读写操作就直接调用就会抛出 BufferOverflowException 异常 + // 也就是说, 在写和读操作后, 需要调用该方法才能进行下一步的写和读 + buffer.flip(); + buffer.put(2); + buffer.flip(); + int result = buffer.get(); + + log.info("result={}", result); + } } diff --git a/java-io/src/test/java/com/github/kuangcp/PropertiesTest.java b/java-io/src/test/java/com/github/kuangcp/PropertiesTest.java index 5585208d..c7739bba 100644 --- a/java-io/src/test/java/com/github/kuangcp/PropertiesTest.java +++ b/java-io/src/test/java/com/github/kuangcp/PropertiesTest.java @@ -5,11 +5,13 @@ import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.Properties; +import lombok.extern.slf4j.Slf4j; import org.junit.Test; /** * @author kuangcp on 18-8-21-下午4:51 */ +@Slf4j public class PropertiesTest { @Test @@ -22,7 +24,7 @@ public void testRead() throws IOException { String a = properties.getProperty("A"); String b = new String(properties.getProperty("B").getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8); - System.out.println(a + b); + log.info("a={} b={}", a, b); } } diff --git a/java-io/src/test/java/com/github/kuangcp/inout/CopyFileTest.java b/java-io/src/test/java/com/github/kuangcp/file/CopyFileTest.java similarity index 98% rename from java-io/src/test/java/com/github/kuangcp/inout/CopyFileTest.java rename to java-io/src/test/java/com/github/kuangcp/file/CopyFileTest.java index 2dde8b41..b1092528 100644 --- a/java-io/src/test/java/com/github/kuangcp/inout/CopyFileTest.java +++ b/java-io/src/test/java/com/github/kuangcp/file/CopyFileTest.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.inout; +package com.github.kuangcp.file; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; From 70fbee24570f218e59e27afaf82112e0483f7745 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Mon, 29 Apr 2019 14:45:42 +0800 Subject: [PATCH 022/476] *) rename --- .../github/kuangcp/sort/MainSortHelper.java} | 3 +- .../kuangcp/sort/{Box.java => Radix.java} | 4 +-- .../com/github/kuangcp/sort/SortTest.java | 31 +++++++++---------- .../volatiles/NeverStopThreadTest.java | 1 - 4 files changed, 18 insertions(+), 21 deletions(-) rename java-algorithms/src/{test/java/com/github/kuangcp/sort/SortHelper.java => main/java/com/github/kuangcp/sort/MainSortHelper.java} (96%) rename java-algorithms/src/main/java/com/github/kuangcp/sort/{Box.java => Radix.java} (80%) diff --git a/java-algorithms/src/test/java/com/github/kuangcp/sort/SortHelper.java b/java-algorithms/src/main/java/com/github/kuangcp/sort/MainSortHelper.java similarity index 96% rename from java-algorithms/src/test/java/com/github/kuangcp/sort/SortHelper.java rename to java-algorithms/src/main/java/com/github/kuangcp/sort/MainSortHelper.java index dbc97ebf..2c6b871a 100644 --- a/java-algorithms/src/test/java/com/github/kuangcp/sort/SortHelper.java +++ b/java-algorithms/src/main/java/com/github/kuangcp/sort/MainSortHelper.java @@ -5,7 +5,6 @@ import java.util.Map; import java.util.concurrent.ThreadLocalRandom; import lombok.extern.slf4j.Slf4j; -import sun.reflect.generics.scope.Scope; /** * Created by https://github.com/kuangcp @@ -14,7 +13,7 @@ * @author kuangcp */ @Slf4j -class SortHelper { +class MainSortHelper { // 数量级 static int AMOUNT = 1000; diff --git a/java-algorithms/src/main/java/com/github/kuangcp/sort/Box.java b/java-algorithms/src/main/java/com/github/kuangcp/sort/Radix.java similarity index 80% rename from java-algorithms/src/main/java/com/github/kuangcp/sort/Box.java rename to java-algorithms/src/main/java/com/github/kuangcp/sort/Radix.java index 72a4f516..dd65d4b1 100644 --- a/java-algorithms/src/main/java/com/github/kuangcp/sort/Box.java +++ b/java-algorithms/src/main/java/com/github/kuangcp/sort/Radix.java @@ -1,11 +1,11 @@ package com.github.kuangcp.sort; /** - * 箱排序, + * 基数排序 箱排序 * * @author Myth */ -public enum Box implements SortAlgorithm { +public enum Radix implements SortAlgorithm { INSTANCE; diff --git a/java-algorithms/src/test/java/com/github/kuangcp/sort/SortTest.java b/java-algorithms/src/test/java/com/github/kuangcp/sort/SortTest.java index 2a1a0adc..5a339ebe 100644 --- a/java-algorithms/src/test/java/com/github/kuangcp/sort/SortTest.java +++ b/java-algorithms/src/test/java/com/github/kuangcp/sort/SortTest.java @@ -1,7 +1,6 @@ package com.github.kuangcp.sort; import com.github.kuangcp.time.GetRunTime; -import java.util.Arrays; import lombok.extern.slf4j.Slf4j; import org.junit.Test; @@ -15,41 +14,41 @@ public class SortTest { @Test public void testSortCorrect() { - SortHelper.AMOUNT = 17; - SortHelper.SCOPE = 999; - SortHelper.show = true; + MainSortHelper.AMOUNT = 17; + MainSortHelper.SCOPE = 999; + MainSortHelper.show = true; - SortHelper.init(); + MainSortHelper.init(); - SortHelper.algorithms.forEach((k, v) -> { + MainSortHelper.algorithms.forEach((k, v) -> { log.info("sort: name={}", k.getName()); - int[] data = SortHelper.data.get(v); + int[] data = MainSortHelper.data.get(v); - SortHelper.showData(data); + MainSortHelper.showData(data); k.sort(data); - SortHelper.showData(data); + MainSortHelper.showData(data); - SortHelper.validate(data); + MainSortHelper.validate(data); System.out.println(); }); } @Test public void testSortPerformance() { - SortHelper.AMOUNT = 8000; - SortHelper.SCOPE = 1000000; + MainSortHelper.AMOUNT = 8000; + MainSortHelper.SCOPE = 1000000; - SortHelper.init(); + MainSortHelper.init(); - SortHelper.algorithms.forEach((k, v) -> { - int[] data = SortHelper.data.get(v); + MainSortHelper.algorithms.forEach((k, v) -> { + int[] data = MainSortHelper.data.get(v); GetRunTime runTime = new GetRunTime().startCount(); k.sort(data); runTime.endCountOneLine(k.getName()); - SortHelper.validate(data); + MainSortHelper.validate(data); System.out.println(); }); } diff --git a/java-concurrency/src/test/java/com/github/kuangcp/volatiles/NeverStopThreadTest.java b/java-concurrency/src/test/java/com/github/kuangcp/volatiles/NeverStopThreadTest.java index 8104531c..cb74b1f0 100644 --- a/java-concurrency/src/test/java/com/github/kuangcp/volatiles/NeverStopThreadTest.java +++ b/java-concurrency/src/test/java/com/github/kuangcp/volatiles/NeverStopThreadTest.java @@ -43,7 +43,6 @@ public void testSleep() throws InterruptedException { Thread thread = new Thread(demo::normalStopWithSleep); thread.start(); - // 如果不加输出, 可能线程都还没创建起来, 这里就直接修改成了false log.info("prepare to stop"); demo.stopWithSleep(); From dc923ed8ff91a434671e78bc70648b3d21456f1b Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Tue, 30 Apr 2019 00:41:22 +0800 Subject: [PATCH 023/476] *) restruct for it --- .../{OutMiGong.java => OutOfTheMaze.java} | 129 ++++---- .../com/github/kuangcp/read/ClassScanner.java | 309 +++++++++--------- .../{Button.java => ButtonListener.java} | 2 +- .../jigsaw/{Part.java => ImageBlock.java} | 15 +- .../github/kuangcp/jigsaw/ImageBlockMgr.java | 78 +++++ .../com/github/kuangcp/jigsaw/MainFrame.java | 121 +------ .../com/github/kuangcp/jigsaw/MainPanel.java | 63 ++++ .../java/com/github/kuangcp/jigsaw/Move.java | 160 ++++----- .../com/github/kuangcp/jigsaw/Panelp.java | 96 ------ java-gui/src/main/resources/logback.xml | 82 +++++ .../kuangcp/bio/onechatone/ChatProtocol.java | 4 +- 11 files changed, 549 insertions(+), 510 deletions(-) rename java-algorithms/src/main/java/com/github/kuangcp/strcuture/stackapp/{OutMiGong.java => OutOfTheMaze.java} (50%) rename java-gui/src/main/java/com/github/kuangcp/jigsaw/{Button.java => ButtonListener.java} (88%) rename java-gui/src/main/java/com/github/kuangcp/jigsaw/{Part.java => ImageBlock.java} (72%) create mode 100644 java-gui/src/main/java/com/github/kuangcp/jigsaw/ImageBlockMgr.java create mode 100644 java-gui/src/main/java/com/github/kuangcp/jigsaw/MainPanel.java delete mode 100644 java-gui/src/main/java/com/github/kuangcp/jigsaw/Panelp.java create mode 100644 java-gui/src/main/resources/logback.xml diff --git a/java-algorithms/src/main/java/com/github/kuangcp/strcuture/stackapp/OutMiGong.java b/java-algorithms/src/main/java/com/github/kuangcp/strcuture/stackapp/OutOfTheMaze.java similarity index 50% rename from java-algorithms/src/main/java/com/github/kuangcp/strcuture/stackapp/OutMiGong.java rename to java-algorithms/src/main/java/com/github/kuangcp/strcuture/stackapp/OutOfTheMaze.java index 8714826b..57a80a17 100644 --- a/java-algorithms/src/main/java/com/github/kuangcp/strcuture/stackapp/OutMiGong.java +++ b/java-algorithms/src/main/java/com/github/kuangcp/strcuture/stackapp/OutOfTheMaze.java @@ -1,5 +1,7 @@ package com.github.kuangcp.strcuture.stackapp; +import lombok.extern.slf4j.Slf4j; + /** * Created by https://github.com/kuangcp on 17-8-22 下午3:11 * 迷宫,找出路径和最短路路径 @@ -7,9 +9,11 @@ * 在接口中定义一个常量,再继承这个接口,就能在类中到处用这个常量了 * path的路径没有变过,哪了出错了?、、、 */ -public class OutMiGong { +@Slf4j +public class OutOfTheMaze { + + private static int top = -1, count = 0, minSize = 100; - private static int top = -1, count = 0, minlen = 100; //定义一个普通整型的地图,两个栈 //int [][] map = new int [M+2][N+2]; 不需要指明大小 private static int[][] map = { @@ -23,8 +27,9 @@ public class OutMiGong { {1, 1, 1, 1, 1, 1, 1, 1} }; - private static Point[] Stack = new Point[100];//只是说放的是point类型的引用,而不是放对象 - private static Point[] Path = new Point[100]; + //只是说放的是point类型的引用,而不是放对象 + private static Point[] stack = new Point[100]; + private static Point[] path = new Point[100]; static class Point { @@ -35,8 +40,8 @@ static class Point { public static void main(String[] args) { for (int k = 0; k < 100; k++) { - Stack[k] = new Point(); - Path[k] = new Point(); + stack[k] = new Point(); + path[k] = new Point(); } int found, choose, i, j; boolean success = false; @@ -48,103 +53,107 @@ public static void main(String[] args) { print[z][x] = (char) map[z][x]; } } - System.out.println(Stack[6].row);//为什么会说空指向呢? +// log.info("{}", stack[6].row); + top++; - Stack[top].row = 1; - Stack[top].col = 1; //起始位置是(1,1) - Stack[top].choose = 0; + stack[top].row = 1; + stack[top].col = 1; //起始位置是(1,1) + stack[top].choose = 0; map[1][1] = -1; while (top > -1) { //栈不为空就循环 - i = Stack[top].row; - j = Stack[top].col; - choose = Stack[top].choose; + i = stack[top].row; + j = stack[top].col; + choose = stack[top].choose; if (i == maxRow && j == maxCol) { //走到了终点 success = true; - System.out.printf(" >>> %d: ", count++); + log.info("the {} way:", ++count); for (int k = 0; k <= top; k++) { - System.out.printf("(%d,%d) ", Stack[k].row, Stack[k].col); - if ((k + 1) % 8 == 0) { - System.out.println(); //每8个换行 - } + System.out.printf("(%d,%d) ", stack[k].row, stack[k].col); } System.out.println("\n"); - if (top + 1 < minlen) { //比较 找到最短路径放入path中 - System.arraycopy(Stack, 0, Path, 0, top + 1); - minlen = top + 1; + if (top + 1 < minSize) { //比较 找到最短路径放入path中 + System.arraycopy(stack, 0, path, 0, top + 1); + minSize = top + 1; } - map[Stack[top].row][Stack[top].col] = 0; // 把破坏的那个终点恢复 + map[stack[top].row][stack[top].col] = 0; // 把破坏的那个终点恢复 top--; - i = Stack[top].row; - j = Stack[top].col; - choose = Stack[top].choose; + i = stack[top].row; + j = stack[top].col; + choose = stack[top].choose; } + found = 0; while (choose < 4 && found == 0) { //开始选择路径 switch (choose++) { case 0: - i = Stack[top].row - 1; - j = Stack[top].col; + i = stack[top].row - 1; + j = stack[top].col; break; // 上 case 1: - i = Stack[top].row; - j = Stack[top].col + 1; + i = stack[top].row; + j = stack[top].col + 1; break; // 右 case 2: - i = Stack[top].row + 1; - j = Stack[top].col; + i = stack[top].row + 1; + j = stack[top].col; break; // 下 case 3: - i = Stack[top].row; - j = Stack[top].col - 1; + i = stack[top].row; + j = stack[top].col - 1; break; // 左 } if (map[i][j] == 0) { found = 1; } } + //System.out.println("top="+top); if (found == 1) { - Stack[top].choose = choose; + stack[top].choose = choose; top++; - Stack[top].row = i; - Stack[top].col = j; - Stack[top].choose = 0; + stack[top].row = i; + stack[top].col = j; + stack[top].choose = 0; map[i][j] = -1; //破坏该位置 } else { //没找到 - map[Stack[top].row][Stack[top].col] = 0; + map[stack[top].row][stack[top].col] = 0; //恢复当前位置,在while和方向choose变量的作用下就能一步步按原路返回 top--; } } if (success) { - System.out.printf("最短路径的长度 %d\n\n", minlen); - System.out.print("最短的路径:"); - for (int k = 0; k < minlen; k++) { - System.out.printf("(%d,%d) ", Path[k].row, Path[k].col); - print[Path[k].row][Path[k].col] = '.'; - if ((k + 1) % 8 == 0) { - System.out.println(); - } - } - System.out.print("\n迷宫地图:\n"); - for (int z = 0; z < maxRow + 2; z++) { - for (int x = 0; x < maxCol + 2; x++) { - System.out.printf(" %c", map[z][x]); - } + showResult(maxRow, maxCol, print); + } else { + log.warn("这个迷宫无解"); + } + } + + private static void showResult(int maxRow, int maxCol, char[][] print) { + System.out.printf("最短路径的长度 %d\n\n", minSize); + System.out.print("最短的路径:"); + for (int k = 0; k < minSize; k++) { + System.out.printf("(%d,%d) ", path[k].row, path[k].col); + print[path[k].row][path[k].col] = '.'; + if ((k + 1) % 8 == 0) { System.out.println(); } + } + System.out.print("\n迷宫地图:\n"); + for (int z = 0; z < maxRow + 2; z++) { + for (int x = 0; x < maxCol + 2; x++) { + System.out.printf(" %c", map[z][x]); + } System.out.println(); - System.out.print("\n最短路径如图:\n"); - for (int z = 0; z < maxRow + 2; z++) { - for (int x = 0; x < maxCol + 2; x++) { - System.out.printf(" %c", print[z][x]); - } - System.out.println(); + } + System.out.println(); + System.out.print("\n最短路径如图:\n"); + for (int z = 0; z < maxRow + 2; z++) { + for (int x = 0; x < maxCol + 2; x++) { + System.out.printf(" %c", print[z][x]); } - } else { - System.out.print("这个迷宫不能到达出口"); + System.out.println(); } } } diff --git a/java-class/src/main/java/com/github/kuangcp/read/ClassScanner.java b/java-class/src/main/java/com/github/kuangcp/read/ClassScanner.java index 38db52f3..683e3c35 100644 --- a/java-class/src/main/java/com/github/kuangcp/read/ClassScanner.java +++ b/java-class/src/main/java/com/github/kuangcp/read/ClassScanner.java @@ -1,189 +1,184 @@ package com.github.kuangcp.read; import java.io.File; -import java.io.FileFilter; import java.io.IOException; import java.net.JarURLConnection; import java.net.URL; import java.net.URLDecoder; -import java.util.*; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.util.regex.Pattern; +import lombok.NoArgsConstructor; /** * Created by https://github.com/kuangcp * 读取类 是一个工具类 * * @author kuangcp - * @date 18-5-21 下午2:14 */ +@NoArgsConstructor public class ClassScanner { - private boolean checkInOrEx = true; - private List classFilters = null; - private boolean excludeInner = true; - public ClassScanner() { + private boolean checkInOrEx; + private List classFilters; + private boolean excludeInner; + + public ClassScanner(Boolean excludeInner, Boolean checkInOrEx, List classFilters) { + this.excludeInner = excludeInner; + this.checkInOrEx = checkInOrEx; + this.classFilters = classFilters; + } + + public List getClassFilters() { + return this.classFilters; + } + + /** + * 按正则匹配, 递归获取包下所有类 + * + * @param basePackage 包根路径 + * @param recursive 是否递归 + * @return 类对象集合 + */ + public Set> getPackageAllClasses(String basePackage, boolean recursive) { + Set> classes = new LinkedHashSet<>(); + String packageName = basePackage; + if (basePackage.endsWith(".")) { + packageName = basePackage.substring(0, basePackage.length() - 1); } - - public ClassScanner(Boolean excludeInner, Boolean checkInOrEx, List classFilters) { - this.excludeInner = excludeInner; - this.checkInOrEx = checkInOrEx; - this.classFilters = classFilters; - } - - public List getClassFilters() { - return this.classFilters; + String package2Path = packageName.replace('.', '/'); + try { + Enumeration dirs = Thread.currentThread().getContextClassLoader() + .getResources(package2Path); + while (dirs.hasMoreElements()) { + URL url = dirs.nextElement(); + String protocol = url.getProtocol(); + if ("file".equals(protocol)) { + String filePath = URLDecoder.decode(url.getFile(), "UTF-8"); + this.doScanPackageClassesByFile(classes, packageName, filePath, recursive); + } else if ("jar".equals(protocol)) { + this.doScanPackageClassesByJar(packageName, url, recursive, classes); + } + } + return classes; + } catch (IOException var10) { + var10.printStackTrace(); + throw new InternalError("扫描包失败"); } - - - /** - * 按正则匹配, 递归获取包下所有类 - * @param basePackage 包根路径 - * @param recursive 是否递归 - * @return 类对象集合 - */ - public Set> getPackageAllClasses(String basePackage, boolean recursive) { - Set> classes = new LinkedHashSet<>(); - String packageName = basePackage; - if (basePackage.endsWith(".")) { - packageName = basePackage.substring(0, basePackage.length()-1); + } + + /** + * 从文件中读取类 + */ + private void doScanPackageClassesByFile(Set> classes, String packageName, + String packagePath, final boolean recursive) { + File dir = new File(packagePath); + if (dir.exists() && dir.isDirectory()) { + File[] files = dir.listFiles(file -> { + if (file.isDirectory()) { + return recursive; + } else { + String filename = file.getName(); + return (!ClassScanner.this.excludeInner || filename.indexOf(36) == -1) + && ClassScanner.this.filterClassName(filename); } - String package2Path = packageName.replace('.', '/'); - try { - Enumeration dirs = Thread.currentThread().getContextClassLoader().getResources(package2Path); - while (dirs.hasMoreElements()) { - URL url = dirs.nextElement(); - String protocol = url.getProtocol(); - if ("file".equals(protocol)) { - String filePath = URLDecoder.decode(url.getFile(), "UTF-8"); - this.doScanPackageClassesByFile(classes, packageName, filePath, recursive); - } else if ("jar".equals(protocol)) { - this.doScanPackageClassesByJar(packageName, url, recursive, classes); - } + }); + if (files != null) { + for (File file : files) { + if (file.isDirectory()) { + this.doScanPackageClassesByFile(classes, packageName + "." + file.getName(), + file.getAbsolutePath(), recursive); + } else { + String className = file.getName().substring(0, file.getName().length() - 6); + + try { + classes.add(Thread.currentThread().getContextClassLoader() + .loadClass(packageName + '.' + className)); + } catch (ClassNotFoundException var14) { + var14.printStackTrace(); + throw new InternalError("过滤失败"); } - return classes; - } catch (IOException var10) { - var10.printStackTrace(); - throw new InternalError("扫描包失败"); + } } - } - /** - * 从文件中读取类 - * @param classes - * @param packageName - * @param packagePath - * @param recursive - */ - private void doScanPackageClassesByFile(Set> classes, String packageName, String packagePath, final boolean recursive) { - File dir = new File(packagePath); - if (dir.exists() && dir.isDirectory()) { - File[] dirfiles = dir.listFiles(new FileFilter() { - public boolean accept(File file) { - if (file.isDirectory()) { - return recursive; - } else { - String filename = file.getName(); - return ClassScanner.this.excludeInner && filename.indexOf(36) != -1 ? false : ClassScanner.this.filterClassName(filename); - } - } - }); - if (dirfiles != null) { - File[] var8 = dirfiles; - int var9 = dirfiles.length; - - for (int var10 = 0; var10 < var9; ++var10) { - File file = var8[var10]; - if (file.isDirectory()) { - this.doScanPackageClassesByFile(classes, packageName + "." + file.getName(), file.getAbsolutePath(), recursive); - } else { - String className = file.getName().substring(0, file.getName().length() - 6); - - try { - classes.add(Thread.currentThread().getContextClassLoader().loadClass(packageName + '.' + className)); - } catch (ClassNotFoundException var14) { - var14.printStackTrace(); - throw new InternalError("过滤失败"); - } - } - } - - } - } + } } - - /** - * 从Jar中读取类 - * @param basePackage - * @param url - * @param recursive - * @param classes - */ - private void doScanPackageClassesByJar(String basePackage, URL url, boolean recursive, Set> classes) { - String package2Path = basePackage.replace('.', '/'); - - try { - JarFile jar = ((JarURLConnection) url.openConnection()).getJarFile(); - Enumeration entries = jar.entries(); - - while (true) { - String name; - do { - JarEntry entry; - do { - do { - do { - if (!entries.hasMoreElements()) { - return; - } - - entry = (JarEntry) entries.nextElement(); - name = entry.getName(); - } while (!name.startsWith(package2Path)); - } while (entry.isDirectory()); - } while (!recursive && name.lastIndexOf(47) != package2Path.length()); - } while (this.excludeInner && name.indexOf(36) != -1); - - String classSimpleName = name.substring(name.lastIndexOf(47) + 1); - if (this.filterClassName(classSimpleName)) { - String className = name.replace('/', '.'); - className = className.substring(0, className.length() - 6); - - try { - classes.add(Thread.currentThread().getContextClassLoader().loadClass(className)); - } catch (ClassNotFoundException var14) { - var14.printStackTrace(); - throw new InternalError("过滤失败"); - } + } + + /** + * 从Jar中读取类 + */ + private void doScanPackageClassesByJar(String basePackage, URL url, boolean recursive, + Set> classes) { + String package2Path = basePackage.replace('.', '/'); + + try { + JarFile jar = ((JarURLConnection) url.openConnection()).getJarFile(); + Enumeration entries = jar.entries(); + + while (true) { + String name; + // TODO optimized !!! + do { + JarEntry entry; + do { + do { + do { + if (!entries.hasMoreElements()) { + return; } - } - } catch (IOException var15) { - var15.printStackTrace(); - throw new InternalError("扫描jar失败"); + + entry = (JarEntry) entries.nextElement(); + name = entry.getName(); + } while (!name.startsWith(package2Path)); + } while (entry.isDirectory()); + } while (!recursive && name.lastIndexOf(47) != package2Path.length()); + } while (this.excludeInner && name.indexOf(36) != -1); + + String classSimpleName = name.substring(name.lastIndexOf(47) + 1); + if (this.filterClassName(classSimpleName)) { + String className = name.replace('/', '.'); + className = className.substring(0, className.length() - 6); + + try { + classes.add(Thread.currentThread().getContextClassLoader().loadClass(className)); + } catch (ClassNotFoundException var14) { + var14.printStackTrace(); + throw new InternalError("过滤失败"); + } } + } + } catch (IOException e) { + e.printStackTrace(); + throw new InternalError("扫描jar失败"); } - - private boolean filterClassName(String className) { - if (!className.endsWith(".class")) { - return false; - } else if (null != this.classFilters && !this.classFilters.isEmpty()) { - String tmpName = className.substring(0, className.length() - 6); - boolean flag = false; - Iterator var4 = this.classFilters.iterator(); - - while (var4.hasNext()) { - String str = (String) var4.next(); - String tmpreg = "^" + str.replace("*", ".*") + "$"; - Pattern p = Pattern.compile(tmpreg); - if (p.matcher(tmpName).find()) { - flag = true; - break; - } - } - return this.checkInOrEx && flag || !this.checkInOrEx && !flag; - } else { - return true; + } + + private boolean filterClassName(String className) { + if (!className.endsWith(".class")) { + return false; + } else if (null != this.classFilters && !this.classFilters.isEmpty()) { + String tmpName = className.substring(0, className.length() - 6); + boolean flag = false; + Iterator var4 = this.classFilters.iterator(); + + while (var4.hasNext()) { + String str = (String) var4.next(); + String tmpreg = "^" + str.replace("*", ".*") + "$"; + Pattern p = Pattern.compile(tmpreg); + if (p.matcher(tmpName).find()) { + flag = true; + break; } + } + return this.checkInOrEx && flag || !this.checkInOrEx && !flag; + } else { + return true; } + } } \ No newline at end of file diff --git a/java-gui/src/main/java/com/github/kuangcp/jigsaw/Button.java b/java-gui/src/main/java/com/github/kuangcp/jigsaw/ButtonListener.java similarity index 88% rename from java-gui/src/main/java/com/github/kuangcp/jigsaw/Button.java rename to java-gui/src/main/java/com/github/kuangcp/jigsaw/ButtonListener.java index d1e33621..ce565ce8 100644 --- a/java-gui/src/main/java/com/github/kuangcp/jigsaw/Button.java +++ b/java-gui/src/main/java/com/github/kuangcp/jigsaw/ButtonListener.java @@ -10,7 +10,7 @@ * @author kuangcp on 18-9-16-上午11:15 */ @Slf4j -public class Button implements ActionListener { +public class ButtonListener implements ActionListener { public void actionPerformed(ActionEvent e) { log.debug("开始监听按钮的点击"); diff --git a/java-gui/src/main/java/com/github/kuangcp/jigsaw/Part.java b/java-gui/src/main/java/com/github/kuangcp/jigsaw/ImageBlock.java similarity index 72% rename from java-gui/src/main/java/com/github/kuangcp/jigsaw/Part.java rename to java-gui/src/main/java/com/github/kuangcp/jigsaw/ImageBlock.java index dd160656..fcafe588 100644 --- a/java-gui/src/main/java/com/github/kuangcp/jigsaw/Part.java +++ b/java-gui/src/main/java/com/github/kuangcp/jigsaw/ImageBlock.java @@ -4,7 +4,6 @@ import java.io.IOException; import java.io.InputStream; import javax.imageio.ImageIO; -import javax.swing.JLabel; import lombok.extern.slf4j.Slf4j; /** @@ -14,24 +13,24 @@ * @author kuangcp on 18-9-16-上午11:14 */ @Slf4j -class Part { +class ImageBlock { + - static int MAX = 3; //拼图的行列规格 int x; int y; int position; - JLabel l; - Image im; + Image image; - Part(int x, int y, int position) { + ImageBlock(int x, int y, int position) { this.x = x; this.y = y; - this.position = y + MAX * x; + this.position = y + ImageBlockMgr.MAX * x; try { InputStream inputStream = getClass().getClassLoader() .getResourceAsStream("jigsaw/" + position + ".jpg"); - im = ImageIO.read(inputStream); + assert inputStream != null; + image = ImageIO.read(inputStream); } catch (IOException e) { log.error(e.getMessage(), e); } diff --git a/java-gui/src/main/java/com/github/kuangcp/jigsaw/ImageBlockMgr.java b/java-gui/src/main/java/com/github/kuangcp/jigsaw/ImageBlockMgr.java new file mode 100644 index 00000000..4fbaa727 --- /dev/null +++ b/java-gui/src/main/java/com/github/kuangcp/jigsaw/ImageBlockMgr.java @@ -0,0 +1,78 @@ +package com.github.kuangcp.jigsaw; + +import java.awt.Image; +import java.util.Objects; +import java.util.Optional; +import java.util.Vector; +import lombok.extern.slf4j.Slf4j; + +/** + * @author kuangcp on 2019-04-29 11:58 PM + */ +@Slf4j +class ImageBlockMgr { + + static int MAX = 3; //拼图的行列大小 + static int IMAGE_SIZE = 200;// 200 x 200 + + static Move move = new Move(); + static Vector images = new Vector<>(ImageBlockMgr.MAX * ImageBlockMgr.MAX); + + static { + for (int i = 0; i < ImageBlockMgr.MAX * ImageBlockMgr.MAX; i++) { + ImageBlock p = new ImageBlock(i / ImageBlockMgr.MAX, i % ImageBlockMgr.MAX, i); + images.add(p); + } + } + + static void show(int index) { + Optional imageBlockOpt = get(index); + imageBlockOpt.ifPresent(imageBlock -> + log.info("image: x = " + imageBlock.x + "y = " + imageBlock.y)); + } + + static boolean isSameImage(int index, Image image) { + return Objects.equals(getImage(index), image); + } + + static ImageBlock getImageBlock(int index) { + Optional imageBlock = get(index); + assert imageBlock.isPresent(); + return imageBlock.get(); + } + + private static Optional get(int index) { + return Optional.ofNullable(images.get(index)); + } + + static Image getImage(int index) { + ImageBlock imageBlock = images.get(index); + if (Objects.nonNull(imageBlock)) { + return imageBlock.image; + } + + log.error("invalid: index={}", index); + return null; + } + + static int getStartX(int index) { + ImageBlock imageBlock = images.get(index); + if (Objects.nonNull(imageBlock)) { + return imageBlock.x * IMAGE_SIZE; + } + + log.error("invalid: index={}", index); + return 0; + } + + static int getStartY(int index) { + ImageBlock imageBlock = images.get(index); + if (Objects.nonNull(imageBlock)) { + return imageBlock.y * IMAGE_SIZE; + } + + log.error("invalid: index={}", index); + return 0; + } + +} diff --git a/java-gui/src/main/java/com/github/kuangcp/jigsaw/MainFrame.java b/java-gui/src/main/java/com/github/kuangcp/jigsaw/MainFrame.java index 057624ba..5f7175ec 100644 --- a/java-gui/src/main/java/com/github/kuangcp/jigsaw/MainFrame.java +++ b/java-gui/src/main/java/com/github/kuangcp/jigsaw/MainFrame.java @@ -1,7 +1,7 @@ package com.github.kuangcp.jigsaw; import java.awt.BorderLayout; -import java.util.Vector; +import java.awt.HeadlessException; import javax.swing.JButton; import javax.swing.JFrame; @@ -12,125 +12,32 @@ */ public class MainFrame extends JFrame { - static Vector partVector = new Vector<>(); - public static void main(String[] args) { new MainFrame(); + ImageBlockMgr.move.disrupt(); } - private MainFrame() { - for (int i = 0; i < Part.MAX * Part.MAX; i++) { - Part p = new Part(i / Part.MAX, i % Part.MAX, i); - partVector.add(p); - } + private MainFrame() throws HeadlessException { + ButtonListener button = new ButtonListener(); + JButton startBtn = new JButton("开始游戏"); + startBtn.addActionListener(button);//注册监听 + startBtn.setActionCommand("开始");//指定特定的命令 - com.github.kuangcp.jigsaw.Button button = new Button(); - // Vector tu; 集合更好些 只是不太会用 - JButton jb1 = new JButton("开始游戏"); - jb1.addActionListener(button);//注册监听 - jb1.setActionCommand("开始");//指定特定的命令 + MainPanel btnPanel = new MainPanel(); + btnPanel.add(startBtn, BorderLayout.SOUTH); - Panelp jp2 = new Panelp(); - jp2.add(jb1, BorderLayout.SOUTH); + MainPanel mainPanel = new MainPanel(); + mainPanel.setSize(800, 600); + this.addKeyListener(mainPanel); - Panelp jp1 = new Panelp(); - jp1.setSize(800, 600); - //设置监听 不过为什么是要写当前对象,而不是写jp画板呢 - //事件源是JFrame 事件监听者是jp1 - this.addKeyListener(jp1); + this.add(mainPanel, BorderLayout.CENTER); +// this.add(btnPanel,BorderLayout.SOUTH); - this.add(jp1, BorderLayout.CENTER); -// this.add(jp2,BorderLayout.SOUTH); //加上按钮的监听后,键盘监听就失效了。??? -// jp.add(jb1,BorderLayout.SOUTH); this.setSize(597, 615); -// this.setSize(750, 730); this.setLocation(600, 0); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); this.setVisible(true); - -// disrupt(); - } - - /** - * 打乱顺序 - */ - private void disrupt() { - for (int i = 0; i < 80; i++) { - Move k = new Move(); - int dir; - dir = (int) (Math.random() * 5); - switch (dir) { - case 1: - k.moveA(); - break; - case 2: - k.moveD(); - break; - case 3: - k.moveS(); - break; - case 4: - k.moveW(); - break; - default: - break; - } - } } -} - -/*失败的尝试 -int x0,y0,position; -public void moveW(){ - x0 = -1;y0 = -1;position = 0; - for (int i=0;i + + + + + + + %d{HH:mm:ss.SSS} %highlight(%-5level) [%thread] %logger{30}:%yellow(%-3L) %msg%n + + + DEBUG + + + + + + ${log.base}debug.log + true + + ${log.base}debug.%d{yyyy-MM-dd}.log + + + %d{MM-dd HH:mm:ss.SSS} |-%-5level |%thread| %logger{36}:%L - %msg%n + + + DEBUG + + + + + + ${log.base}warn.log + true + + ${log.base}warn.%d{yyyy-MM-dd}.log + + + %d{MM-dd HH:mm:ss.SSS} |-%-5level %logger{36}:%L - %msg%n + + + WARN + + + + + + ${log.base}info.log + true + + ${log.base}info.%d{yyyy-MM-dd}.log + + + %d{MM-dd HH:mm:ss.SSS} |-%-5level %logger{36}:%L - %msg%n + + + INFO + + + + + + ${log.base}error.log + true + + ${log.base}error.%d{yyyy-MM-dd}.log + + + %d{MM-dd HH:mm:ss.SSS} | %thread |-%-5level %logger{36}:%L - %msg%n + + + ERROR + + + + + + + + + + + \ No newline at end of file diff --git a/java-network/src/main/java/com/github/kuangcp/bio/onechatone/ChatProtocol.java b/java-network/src/main/java/com/github/kuangcp/bio/onechatone/ChatProtocol.java index b9ea4230..908bec76 100644 --- a/java-network/src/main/java/com/github/kuangcp/bio/onechatone/ChatProtocol.java +++ b/java-network/src/main/java/com/github/kuangcp/bio/onechatone/ChatProtocol.java @@ -2,8 +2,8 @@ /** * Created by Myth on 2017/4/2 - * 信息的特定内容前后的的特殊字符,称为协议字符 - * TODO 但是这种做法很容易出现问题, 当数据内容出现协议字符时 + * 特殊符号分隔法: 信息的特定内容前后的的特殊字符,称为协议字符 + * 但是这种做法很容易出现问题, 当数据内容出现协议字符时 */ public interface ChatProtocol { From e6e8429a86e675c91ebdec193002c2dac56f2e54 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Tue, 30 Apr 2019 08:51:45 +0800 Subject: [PATCH 024/476] *) optimized code style --- .../java/com/github/kuangcp/sort/Quick.java | 166 ++++++++++-------- .../strcuture/stackapp/OutOfTheMaze.java | 2 +- .../redis/migration/RedisPoolProperty.java | 1 - .../Calculate_btnBegin_actionAdapter.java | 2 +- .../Calculate_btnEqual_actionAdapter.java | 2 +- .../Calculate_btnIncrease_actionAdapter.java | 2 +- .../Calculate_btnMinus_actionAdapter.java | 2 +- .../Calculate_btnPoint_actionAdapter.java | 2 +- .../Calculate_btnZero_actionAdapter.java | 2 +- .../github/kuangcp/caculator/Calculator.java | 23 ++- .../SimplexMethod/SimplexMethod.java | 73 ++++---- .../SimplexMethodQuarter/SimplexMethod.java | 66 +++---- .../SimplexMethodQuarter/MethodTest.java | 2 +- 13 files changed, 177 insertions(+), 168 deletions(-) diff --git a/java-algorithms/src/main/java/com/github/kuangcp/sort/Quick.java b/java-algorithms/src/main/java/com/github/kuangcp/sort/Quick.java index d5103651..3f2a0b44 100644 --- a/java-algorithms/src/main/java/com/github/kuangcp/sort/Quick.java +++ b/java-algorithms/src/main/java/com/github/kuangcp/sort/Quick.java @@ -13,117 +13,129 @@ public enum Quick implements SortAlgorithm { @Override public void sort(int[] data) { - sortData(data, 0, data.length - 1); + QuickSort sort = new FirstImpl(); + sort.sortData(data, 0, data.length - 1); + } + + + interface QuickSort { + + void sortData(int[] data, int low, int high); } /** * 高效的写法 */ - public void sortData(int[] data, int low, int high) { - if (low >= high) { - return; - } - - int lowIndex = low; - int highIndex = high; + static class FirstImpl implements QuickSort { - int value = data[low]; - while (lowIndex < highIndex) { - // 找出右边小于低位所在的标识值 - while (lowIndex < highIndex && data[highIndex] >= value) { - highIndex -= 1; + @Override + public void sortData(int[] data, int low, int high) { + if (low >= high) { + return; } - data[lowIndex] = data[highIndex]; - // 找出左边大于标识值 - while (lowIndex < highIndex && data[lowIndex] <= value) { - lowIndex += 1; + int lowIndex = low; + int highIndex = high; + + int value = data[low]; + while (lowIndex < highIndex) { + // 找出右边小于低位所在的标识值 + while (lowIndex < highIndex && data[highIndex] >= value) { + highIndex -= 1; + } + data[lowIndex] = data[highIndex]; + + // 找出左边大于标识值 + while (lowIndex < highIndex && data[lowIndex] <= value) { + lowIndex += 1; + } + data[highIndex] = data[lowIndex]; } - data[highIndex] = data[lowIndex]; - } - data[lowIndex] = value; + data[lowIndex] = value; - sortData(data, low, lowIndex - 1); - sortData(data, highIndex + 1, high); + sortData(data, low, lowIndex - 1); + sortData(data, highIndex + 1, high); + } } - public void sortData2(int[] data, int low, int high) { - int lowIndex = low; - int highIndex = high; - int index = data[low]; - - while (lowIndex < highIndex) { - while (lowIndex < highIndex && data[highIndex] >= index) { - highIndex--; - } - if (lowIndex < highIndex) { - int temp = data[highIndex]; - data[highIndex] = data[lowIndex]; - data[lowIndex] = temp; - lowIndex++; + /** + * 个人手写 + */ + static class SecondImpl implements QuickSort { + + @Override + public void sortData(int[] data, int low, int high) { + int lowIndex = low; + int highIndex = high; + int index = data[low]; + + while (lowIndex < highIndex) { + while (lowIndex < highIndex && data[highIndex] >= index) { + highIndex--; + } + if (lowIndex < highIndex) { + int temp = data[highIndex]; + data[highIndex] = data[lowIndex]; + data[lowIndex] = temp; + lowIndex++; + } + + while (lowIndex < highIndex && data[lowIndex] <= index) { + lowIndex++; + } + if (lowIndex < highIndex) { + int temp = data[highIndex]; + data[highIndex] = data[lowIndex]; + data[lowIndex] = temp; + highIndex--; + } } - while (lowIndex < highIndex && data[lowIndex] <= index) { - lowIndex++; + if (lowIndex > low) { + sortData(data, low, lowIndex - 1); } - if (lowIndex < highIndex) { - int temp = data[highIndex]; - data[highIndex] = data[lowIndex]; - data[lowIndex] = temp; - highIndex--; + if (highIndex < high) { + sortData(data, lowIndex + 1, high); } } - - if (lowIndex > low) { - sortData2(data, low, lowIndex - 1); - } - if (highIndex < high) { - sortData2(data, lowIndex + 1, high); - } } - public > T[] quickSort(T[] targetArr, - int start, int end) { - int i = start + 1, j = end; - T key = targetArr[start]; - // SortUtilsUtil=newSortUtil(); + /** + * 对象数组排序 + */ + public > T[] quickSort(T[] data, int start, int end) { + int low = start + 1, high = end; + T key = data[start]; if (start >= end) { - return (targetArr); + return (data); } - /* - * 从i++和j--两个方向搜索不满足条件的值并交换 - * - * 条件为:i++方向小于key,j--方向大于key - */ + while (true) { - while (targetArr[j].compareTo(key) > 0) { - j--; + while (data[high].compareTo(key) > 0) { + high--; } - while (targetArr[i].compareTo(key) < 0 && i < j) { - i++; + while (data[low].compareTo(key) < 0 && low < high) { + low++; } - if (i >= j) { + if (low >= high) { break; } - if (targetArr[i] == key) { - j--; + if (data[low] == key) { + high--; } else { - i++; + low++; } } - /* 关键数据放到‘中间’ */ - // sUtil.swap(targetArr,start,j); - - if (start < i - 1) { - this.quickSort(targetArr, start, i - 1); + if (start < low - 1) { + this.quickSort(data, start, low - 1); } - if (j + 1 < end) { - this.quickSort(targetArr, j + 1, end); + if (high + 1 < end) { + this.quickSort(data, high + 1, end); } - return targetArr; + return data; } } \ No newline at end of file diff --git a/java-algorithms/src/main/java/com/github/kuangcp/strcuture/stackapp/OutOfTheMaze.java b/java-algorithms/src/main/java/com/github/kuangcp/strcuture/stackapp/OutOfTheMaze.java index 57a80a17..3e0391c7 100644 --- a/java-algorithms/src/main/java/com/github/kuangcp/strcuture/stackapp/OutOfTheMaze.java +++ b/java-algorithms/src/main/java/com/github/kuangcp/strcuture/stackapp/OutOfTheMaze.java @@ -39,7 +39,7 @@ static class Point { } public static void main(String[] args) { - for (int k = 0; k < 100; k++) { + for (int k = 0; k < minSize; k++) { stack[k] = new Point(); path[k] = new Point(); } diff --git a/java-concurrency/src/main/java/redis/migration/RedisPoolProperty.java b/java-concurrency/src/main/java/redis/migration/RedisPoolProperty.java index 4a625d4c..79e4438e 100644 --- a/java-concurrency/src/main/java/redis/migration/RedisPoolProperty.java +++ b/java-concurrency/src/main/java/redis/migration/RedisPoolProperty.java @@ -29,7 +29,6 @@ class RedisPoolProperty { //设定默认值为空字符串不能为null private String password = ""; - // host and port is effective boolean isAbleToInit() { return StringUtils.isNoneBlank(host) && port > 0 && port < 65535 && timeout > 0; diff --git a/java-gui/src/main/java/com/github/kuangcp/caculator/Calculate_btnBegin_actionAdapter.java b/java-gui/src/main/java/com/github/kuangcp/caculator/Calculate_btnBegin_actionAdapter.java index 744e24c6..2e792468 100644 --- a/java-gui/src/main/java/com/github/kuangcp/caculator/Calculate_btnBegin_actionAdapter.java +++ b/java-gui/src/main/java/com/github/kuangcp/caculator/Calculate_btnBegin_actionAdapter.java @@ -17,6 +17,6 @@ class Calculate_btnBegin_actionAdapter implements ActionListener { } public void actionPerformed(ActionEvent e) { - adapter.btnBegin_actionPerformed(); + adapter.btnBeginActionPerformed(); } } diff --git a/java-gui/src/main/java/com/github/kuangcp/caculator/Calculate_btnEqual_actionAdapter.java b/java-gui/src/main/java/com/github/kuangcp/caculator/Calculate_btnEqual_actionAdapter.java index 90db7d69..deb865f5 100644 --- a/java-gui/src/main/java/com/github/kuangcp/caculator/Calculate_btnEqual_actionAdapter.java +++ b/java-gui/src/main/java/com/github/kuangcp/caculator/Calculate_btnEqual_actionAdapter.java @@ -17,6 +17,6 @@ class Calculate_btnEqual_actionAdapter implements ActionListener { } public void actionPerformed(ActionEvent e) { - adapter.btnEqual_actionPerformed(); + adapter.btnEqualActionPerformed(); } } diff --git a/java-gui/src/main/java/com/github/kuangcp/caculator/Calculate_btnIncrease_actionAdapter.java b/java-gui/src/main/java/com/github/kuangcp/caculator/Calculate_btnIncrease_actionAdapter.java index 3e91e36a..2fd99fb5 100644 --- a/java-gui/src/main/java/com/github/kuangcp/caculator/Calculate_btnIncrease_actionAdapter.java +++ b/java-gui/src/main/java/com/github/kuangcp/caculator/Calculate_btnIncrease_actionAdapter.java @@ -17,6 +17,6 @@ class Calculate_btnIncrease_actionAdapter implements ActionListener { } public void actionPerformed(ActionEvent e) { - adapter.btnIncrease_actionPerformed(e); + adapter.btnIncreaseActionPerformed(e); } } diff --git a/java-gui/src/main/java/com/github/kuangcp/caculator/Calculate_btnMinus_actionAdapter.java b/java-gui/src/main/java/com/github/kuangcp/caculator/Calculate_btnMinus_actionAdapter.java index a0c13b49..f35648a7 100644 --- a/java-gui/src/main/java/com/github/kuangcp/caculator/Calculate_btnMinus_actionAdapter.java +++ b/java-gui/src/main/java/com/github/kuangcp/caculator/Calculate_btnMinus_actionAdapter.java @@ -17,6 +17,6 @@ class Calculate_btnMinus_actionAdapter implements ActionListener { } public void actionPerformed(ActionEvent e) { - adapter.btnMinus_actionPerformed(); + adapter.btnMinusActionPerformed(); } } diff --git a/java-gui/src/main/java/com/github/kuangcp/caculator/Calculate_btnPoint_actionAdapter.java b/java-gui/src/main/java/com/github/kuangcp/caculator/Calculate_btnPoint_actionAdapter.java index f3ac1a32..dd16adc3 100644 --- a/java-gui/src/main/java/com/github/kuangcp/caculator/Calculate_btnPoint_actionAdapter.java +++ b/java-gui/src/main/java/com/github/kuangcp/caculator/Calculate_btnPoint_actionAdapter.java @@ -17,6 +17,6 @@ class Calculate_btnPoint_actionAdapter implements ActionListener { } public void actionPerformed(ActionEvent e) { - adapter.btnPoint_actionPerformed(e); + adapter.btnPointActionPerformed(e); } } diff --git a/java-gui/src/main/java/com/github/kuangcp/caculator/Calculate_btnZero_actionAdapter.java b/java-gui/src/main/java/com/github/kuangcp/caculator/Calculate_btnZero_actionAdapter.java index dc46ed22..24aee553 100644 --- a/java-gui/src/main/java/com/github/kuangcp/caculator/Calculate_btnZero_actionAdapter.java +++ b/java-gui/src/main/java/com/github/kuangcp/caculator/Calculate_btnZero_actionAdapter.java @@ -17,6 +17,6 @@ class Calculate_btnZero_actionAdapter implements ActionListener { } public void actionPerformed(ActionEvent e) { - adapter.btnZero_actionPerformed(e); + adapter.btnZeroActionPerformed(e); } } diff --git a/java-gui/src/main/java/com/github/kuangcp/caculator/Calculator.java b/java-gui/src/main/java/com/github/kuangcp/caculator/Calculator.java index 24130d04..da7f316c 100644 --- a/java-gui/src/main/java/com/github/kuangcp/caculator/Calculator.java +++ b/java-gui/src/main/java/com/github/kuangcp/caculator/Calculator.java @@ -5,7 +5,6 @@ import java.awt.Font; import java.awt.Rectangle; import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; import java.util.Objects; import javax.swing.JButton; import javax.swing.JFrame; @@ -182,7 +181,7 @@ private void bindButton(JPanel panel, JButton... buttons) { } } - void btnZero_actionPerformed(ActionEvent e) { + void btnZeroActionPerformed(ActionEvent e) { if (flag) { //如果刚刚按下了运算符 txtResult.setText(""); if (flag1) {//判断之前是否输入了点运算符 @@ -208,14 +207,14 @@ void btnZero_actionPerformed(ActionEvent e) { flag3 = false; } - void btnIncrease_actionPerformed(ActionEvent e) { + void btnIncreaseActionPerformed(ActionEvent e) { if (flag3) { txtResult.setText(txtResult.getText()); op = e.getActionCommand(); //得到刚刚按下的运算符 front = txtResult.getText(); //记录加减乘除运算符之前输入的内容 } else if (flag2) { // ActionEvent ee = new ActionEvent("qq", 1, "pp"); - btnEqual_actionPerformed(); + btnEqualActionPerformed(); op = e.getActionCommand(); //得到刚刚按下的运算符 front = re; flag2 = false; @@ -227,7 +226,7 @@ void btnIncrease_actionPerformed(ActionEvent e) { flag = true; //记录已经按下了加减乘除运算符的其中一个 } - void btnEqual_actionPerformed() { + void btnEqualActionPerformed() { if (!flag3) { //未曾按下等于运算符 behind = txtResult.getText(); } else { @@ -259,7 +258,7 @@ void btnEqual_actionPerformed() { } } - void btnPoint_actionPerformed(ActionEvent e) { + void btnPointActionPerformed(ActionEvent e) { int num = txtResult.getText().indexOf("."); if (num < 0 && !flag) { txtResult.setText(txtResult.getText() + e.getActionCommand()); @@ -269,7 +268,7 @@ void btnPoint_actionPerformed(ActionEvent e) { } } - void btnBegin_actionPerformed() {//清零运算符事件处理 + void btnBeginActionPerformed() {//清零运算符事件处理 flag = false; flag1 = false; flag2 = false; @@ -280,7 +279,7 @@ void btnBegin_actionPerformed() {//清零运算符事件处理 txtResult.setText("0"); } - void btnMinus_actionPerformed() {//取反运算符事件处理 + void btnMinusActionPerformed() {//取反运算符事件处理 if (txtResult.getText().equals("0")) {//如果文本框内容为0 txtResult.setText(txtResult.getText()); } else if (txtResult.getText().contains("-")) {//若文本框中含有负号 @@ -305,10 +304,10 @@ void btnCancel_actionPerformed() {//退格事件处理方法 } public static void main(String[] args) { - Calculator fc = new Calculator(); - fc.setSize(400, 310); - fc.setLocation(200, 150); - fc.setVisible(true); + Calculator calculator = new Calculator(); + calculator.setSize(400, 310); + calculator.setLocation(200, 150); + calculator.setVisible(true); } } diff --git a/java-question/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethod/SimplexMethod.java b/java-question/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethod/SimplexMethod.java index 776deeb6..1f60e670 100644 --- a/java-question/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethod/SimplexMethod.java +++ b/java-question/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethod/SimplexMethod.java @@ -15,7 +15,7 @@ public class SimplexMethod { //读取配置文件 private static ReadProperties config; //最大参数个数 - private static int MAXPARAMS; + private static int MAX_PARAMS; //最大行列式数 private static int EQUALITY; //约束式子系数集合 @@ -34,7 +34,7 @@ public class SimplexMethod { static { config = new ReadProperties("src/main/resources/math/SimplexMethod.properties"); - MAXPARAMS = config.getInt("MaxParams"); + MAX_PARAMS = config.getInt("MaxParams"); EQUALITY = config.getInt("Equality"); } @@ -48,8 +48,8 @@ public static void main(String[] s) { */ public void init() { //添加求解式的数据 - String maxs = config.getString("Max"); - String[] temp = maxs.split(","); + String max = config.getString("Max"); + String[] temp = max.split(","); for (String data : temp) { //System.out.println(data); Max.add(Double.parseDouble(data)); @@ -59,8 +59,8 @@ public void init() { String buffer = config.getString("E" + i); String[] tempList = buffer.split(","); Equality e = new Equality(); - for (int j = 0; j < tempList.length; j++) { - e.getParams().add(Double.parseDouble(tempList[j])); + for (String s : tempList) { + e.getParams().add(Double.parseDouble(s)); } e.setResult(config.getDouble("B" + i)); e.setIndex(config.getInt("I" + i)); @@ -76,27 +76,26 @@ public void init() { Tables.add(table); } //查看数据是否正确装填 - disp("单纯形表的中心数据 : ", Tables); -// disp("求解的方程式",Max); -// disp("约束条件的方程式",Rows); + display("单纯形表的中心数据 : ", Tables); +// display("求解的方程式",Max); +// display("约束条件的方程式",Rows); //展示方程式 System.out.println("原题样式 : "); showRows(); - } //进行表格的运算 - public void run() { + private void run() { init(); - Boolean flag = true; + boolean flag; //运算出初始的表格 //计算最底下一行 - CaculateLastRow(); + CalculateLastRow(); //计算右边列 - CaculateRightCol(); + CalculateRightCol(); //判断是否达到退出条件 flag = exitTime(Zs); - disp("运行状态", Tables); + display("运行状态", Tables); //迭代的计算直到满足条件 while (!flag) { @@ -138,64 +137,64 @@ public void run() { Zs.clear(); Os.clear(); //计算最后一行 - CaculateLastRow(); + CalculateLastRow(); //判断是否达到退出条件 flag = exitTime(Zs); if (!flag) { - CaculateRightCol(); + CalculateRightCol(); } - disp("运行状态", Tables, true); - disp("底栏", Zs, false); + display("运行状态", Tables, true); + display("底栏", Zs, false); } //运算完成 - Double result = 0.0; - Double[] results = new Double[MAXPARAMS]; + double result = 0.0; + Double[] results = new Double[MAX_PARAMS]; for (int i = 0; i < EQUALITY; i++) { Table t = Tables.get(i); result += t.getCb() * t.getB(); results[t.getXb() - 1] = t.getB(); } System.out.println("最优目标值是 : " + result); - String resultstr = "X=("; + StringBuilder resultStr = new StringBuilder("X=("); for (Double d : results) { if (d != null) { - resultstr += d + ","; + resultStr.append(d).append(","); } else { - resultstr += "0,"; + resultStr.append("0,"); } } - resultstr = resultstr.substring(0, resultstr.length() - 1); - resultstr += ")"; - System.out.println(resultstr); + resultStr = new StringBuilder(resultStr.substring(0, resultStr.length() - 1)); + resultStr.append(")"); + System.out.println(resultStr); } /** * 计算最后一行和最右边的一列 */ - public void CaculateLastRow() { - for (int i = 0; i < MAXPARAMS; i++) {//循环变量个数 + public void CalculateLastRow() { + for (int i = 0; i < MAX_PARAMS; i++) {//循环变量个数 Double temp = Max.get(i); for (int j = 0; j < EQUALITY; j++) {//循环层数 temp -= Tables.get(j).getRows().get(i) * Tables.get(j).getCb(); } Zs.add(temp); } - disp("最后一行", Zs, false); + display("最后一行", Zs, false); resultCol = MaxList(Zs, true); } - public void CaculateRightCol() { + public void CalculateRightCol() { //计算右边栏 for (int i = 0; i < EQUALITY; i++) { Double temp = Tables.get(i).getB() / Tables.get(i).getRows().get(resultCol); Tables.get(i).setO(temp); Os.add(temp); } - //disp("右栏",Os,false); + //display("右栏",Os,false); resultRow = MaxList(Os, false); } @@ -221,7 +220,7 @@ public boolean exitTime(List list) { * @return 极值下标 */ public Integer MaxList(List list, boolean max) { - Integer index = 0; + int index = 0; Double temp = list.get(index); for (int i = 1; i < list.size(); i++) { if (max && temp < list.get(i)) { @@ -239,11 +238,11 @@ public Integer MaxList(List list, boolean max) { /** * 方便展示原始数据 */ - public void disp(String title, List list) { - disp(title, list, true); + public void display(String title, List list) { + display(title, list, true); } - public void disp(String title, List list, boolean flag) { + public void display(String title, List list, boolean flag) { System.out.println(title); for (int i = 0; i < list.size(); i++) { if (flag) { @@ -262,7 +261,7 @@ public void disp(String title, List list, boolean flag) { */ public void showRows() { StringBuilder MaxRows = new StringBuilder("Max(z)="); - for (int i = 0; i < MAXPARAMS; i++) { + for (int i = 0; i < MAX_PARAMS; i++) { if (Max.get(i) != 0) { MaxRows.append(Max.get(i)).append(" X").append(i + 1).append(" + "); } diff --git a/java-question/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethodQuarter/SimplexMethod.java b/java-question/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethodQuarter/SimplexMethod.java index 23c08d19..47ea2b61 100644 --- a/java-question/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethodQuarter/SimplexMethod.java +++ b/java-question/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethodQuarter/SimplexMethod.java @@ -48,8 +48,8 @@ public static void main(String[] s) { */ private void init() { //添加求解式的数据 - String maxs = config.getString("Max"); - String[] temp = maxs.split(","); + String max = config.getString("Max"); + String[] temp = max.split(","); for (String data : temp) { // System.out.println(data); Max.add(Fraction.valueOf(data)); @@ -78,9 +78,9 @@ private void init() { Tables.add(table); } //查看数据是否正确装填 -// disp("单纯形表的中心数据 : ",Tables); - disp("求解的方程式系数 :", false, Max, false); -// disp("约束条件的方程式",Rows); +// display("单纯形表的中心数据 : ",Tables); + display("求解的方程式系数 :", false, Max, false); +// display("约束条件的方程式",Rows); //展示方程式 System.out.println("原题样式 : "); showRows(); @@ -93,13 +93,13 @@ private void init() { public void run() throws Exception { init(); //运算出初始的表格 - disp("中间计算结果", Tables); + display("中间计算结果", Tables); //计算最底下一行 - CaculateLastRow(); + CalculateLastRow(); //计算右边列 - CaculateRightCol(); + CalculateRightCol(); //判断是否达到退出条件 - CONTINUE = isCONTINUES(Zs); + CONTINUE = isNeedContinue(Zs); //迭代的计算直到满足条件 while (CONTINUE) { @@ -142,13 +142,13 @@ public void run() throws Exception { Os.clear(); // log("下右两个计算集合的大小"+Zs.size()+":"+Os.size()); - disp("中间计算结果", true, Tables, true); + display("中间计算结果", true, Tables, true); //计算最后一行 - CaculateLastRow(); + CalculateLastRow(); //判断是否达到退出条件 - CONTINUE = isCONTINUES(Zs); + CONTINUE = isNeedContinue(Zs); if (CONTINUE) { - CaculateRightCol(); + CalculateRightCol(); } System.out.println("******************************"); } @@ -166,7 +166,7 @@ public void run() throws Exception { * @throws Exception 异常 */ private void finallyResult() throws Exception { - Integer RightMin = MaxList(Os, false, true, false); + Integer RightMin = maxList(Os, false, true, false); boolean SUCCESS = true; if (RightMin == -1) { System.out.println("右列没有一个正数,最后一行也没有正数,原方程没有最优解"); @@ -182,17 +182,17 @@ private void finallyResult() throws Exception { results[t.getXb() - 1] = t.getBl(); } System.out.println("最优目标值是 : " + result); - StringBuilder resultstr = new StringBuilder("X=("); + StringBuilder resultStr = new StringBuilder("X=("); for (Fraction d : results) { if (d != null) { - resultstr.append(d).append(","); + resultStr.append(d).append(","); } else { - resultstr.append("0,"); + resultStr.append("0,"); } } - resultstr = new StringBuilder(resultstr.substring(0, resultstr.length() - 1)); - resultstr.append(")"); - System.out.println(resultstr); + resultStr = new StringBuilder(resultStr.substring(0, resultStr.length() - 1)); + resultStr.append(")"); + System.out.println(resultStr); } for (String key : Xbs.keySet()) { @@ -203,10 +203,10 @@ private void finallyResult() throws Exception { /** * 计算最后一行和最右边的一列 */ - private void CaculateLastRow() throws Exception { + private void CalculateLastRow() throws Exception { for (int i = 0; i < MAX_PARAMS; i++) {//循环变量个数 Fraction temp = Max.get(i); -// disp("目标方程系数组",Max,false); +// display("目标方程系数组",Max,false); // System.out.println("Max中取到的temp"+temp); for (int j = 0; j < EQUALITY; j++) {//循环层数 // System.out.println("乘积 "+Tables.get(j).getCb()+"*"+Tables.get(j).getRows().get(i)+" = "+Tables.get(j).getCb().multiply(Tables.get(j).getRows().get(i))); @@ -216,13 +216,13 @@ private void CaculateLastRow() throws Exception { Zs.add(temp); // System.out.println("jieguo "+temp); } - disp("最后一行", false, Zs, false); - resultCol = MaxList(Zs, true, true, true); + display("最后一行", false, Zs, false); + resultCol = maxList(Zs, true, true, true); } //计算右边栏 - private void CaculateRightCol() throws Exception { + private void CalculateRightCol() throws Exception { // log("计算所得行最大Index"+resultCol); for (int i = 0; i < EQUALITY; i++) { @@ -232,8 +232,8 @@ private void CaculateRightCol() throws Exception { Tables.get(i).setO(temp); Os.add(temp); } - disp("右栏", false, Os, false); - resultRow = MaxList(Os, false, true, false); + display("右栏", false, Os, false); + resultRow = maxList(Os, false, true, false); if (resultRow == -1) { CONTINUE = false; } @@ -249,7 +249,7 @@ private void CaculateRightCol() throws Exception { * @param list 分数数组 * @return false就不再继续 */ - private boolean isCONTINUES(List list) { + private boolean isNeedContinue(List list) { boolean flag = false; for (Fraction b : list) { if (b.isPositive()) { @@ -257,7 +257,7 @@ private boolean isCONTINUES(List list) { break; } } -// disp("检测",Tables); +// display("检测",Tables); StringBuilder temp = new StringBuilder(); for (int i = 0; i < EQUALITY; i++) { temp.append(Tables.get(i).getXb()); @@ -280,7 +280,7 @@ private boolean isCONTINUES(List list) { * @param permitMinus 是否允许负数进行笔记比较 * @return 最大 */ - Integer MaxList(List list, boolean isMax, boolean haveInfinity, boolean permitMinus) { + Integer maxList(List list, boolean isMax, boolean haveInfinity, boolean permitMinus) { Integer index = null; //有非数的集合 if (haveInfinity) { @@ -339,8 +339,8 @@ Integer MaxList(List list, boolean isMax, boolean haveInfinity, boolea * * @param list 数据数组 */ - private void disp(String title, List list) { - disp(title, true, list, true); + private void display(String title, List list) { + display(title, true, list, true); } /** @@ -351,7 +351,7 @@ private void disp(String title, List list) { * @param list 数据 * @param turn 内容是否换行 */ - private void disp(String title, boolean titleTurn, List list, boolean turn) { + private void display(String title, boolean titleTurn, List list, boolean turn) { if (titleTurn) { System.out.println(title); } else { diff --git a/java-question/src/test/java/com/github/kuangcp/simpleMethod/SimplexMethodQuarter/MethodTest.java b/java-question/src/test/java/com/github/kuangcp/simpleMethod/SimplexMethodQuarter/MethodTest.java index d73d1233..ebce7927 100644 --- a/java-question/src/test/java/com/github/kuangcp/simpleMethod/SimplexMethodQuarter/MethodTest.java +++ b/java-question/src/test/java/com/github/kuangcp/simpleMethod/SimplexMethodQuarter/MethodTest.java @@ -20,7 +20,7 @@ public void testMax() { list.add(new Fraction(-15, 1)); list.add(new Fraction(5, 2)); SimplexMethod sm = new SimplexMethod(); - Integer index = sm.MaxList(list, false, true, false); + Integer index = sm.maxList(list, false, true, false); if (index != -1) { System.out.println(index + " => " + list.get(index)); } else { From 97fb238a9a953526fecfcc96dc982153b7884516 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Thu, 2 May 2019 01:13:41 +0800 Subject: [PATCH 025/476] +) simply implement radix sort --- .../java/com/github/kuangcp/sort/Radix.java | 54 ++++++++++++++++--- .../{MainSortHelper.java => SortHelper.java} | 8 +-- .../com/github/kuangcp/sort/SortTest.java | 39 +++++++------- 3 files changed, 72 insertions(+), 29 deletions(-) rename java-algorithms/src/main/java/com/github/kuangcp/sort/{MainSortHelper.java => SortHelper.java} (90%) diff --git a/java-algorithms/src/main/java/com/github/kuangcp/sort/Radix.java b/java-algorithms/src/main/java/com/github/kuangcp/sort/Radix.java index dd65d4b1..398434f3 100644 --- a/java-algorithms/src/main/java/com/github/kuangcp/sort/Radix.java +++ b/java-algorithms/src/main/java/com/github/kuangcp/sort/Radix.java @@ -1,5 +1,14 @@ package com.github.kuangcp.sort; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; + /** * 基数排序 箱排序 * @@ -9,15 +18,46 @@ public enum Radix implements SortAlgorithm { INSTANCE; - public void sort(int[] arr) { - //盒子个数 - for (int i = 0; i < 1000; i++) { - for (int anArr : arr) { - if (anArr == i) { - // TODO 放入链队列中 + //需要确定盒子的轮数 也就是数值的最大位数 + static int maxLen = 5; + + public void sort(int[] data) { + List temp = Arrays.stream(data).boxed().collect(Collectors.toList()); + // 0 -> []. 1 -> [], ... 9 -> [] + Map> box = new HashMap<>(); + + for (int i = 0; i < maxLen; i++) { + box.clear(); + for (int value : temp) { + int weight = (int) ((value / Math.pow(10, i)) % 10); + addValue(box, weight, value); + } + + temp = new ArrayList<>(data.length); + for (int j = 0; j < 10; j++) { + List list = box.get(j); + if (Objects.nonNull(list)) { + temp.addAll(list); } } } + + setArray(temp, data); + } + + private static void addValue(Map> box, int weight, int value) { + List result = box.get(weight); + if (Objects.isNull(result)) { + result = new LinkedList<>(); + box.put(weight, result); + } + + result.add(value); } -} + private static void setArray(List result, int[] data) { + for (int i = 0; i < data.length; i++) { + data[i] = result.get(i); + } + } +} diff --git a/java-algorithms/src/main/java/com/github/kuangcp/sort/MainSortHelper.java b/java-algorithms/src/main/java/com/github/kuangcp/sort/SortHelper.java similarity index 90% rename from java-algorithms/src/main/java/com/github/kuangcp/sort/MainSortHelper.java rename to java-algorithms/src/main/java/com/github/kuangcp/sort/SortHelper.java index 2c6b871a..370a7521 100644 --- a/java-algorithms/src/main/java/com/github/kuangcp/sort/MainSortHelper.java +++ b/java-algorithms/src/main/java/com/github/kuangcp/sort/SortHelper.java @@ -13,7 +13,7 @@ * @author kuangcp */ @Slf4j -class MainSortHelper { +class SortHelper { // 数量级 static int AMOUNT = 1000; @@ -27,8 +27,10 @@ class MainSortHelper { static ArrayList data = new ArrayList<>(); static void init() { + Radix.maxLen = String.valueOf(SCOPE).length(); + int count = -1; -// algorithms.put(Box.INSTANCE, ++count); + algorithms.put(Radix.INSTANCE, ++count); algorithms.put(Bubble.INSTANCE, ++count); algorithms.put(Insert.INSTANCE, ++count); algorithms.put(Select.INSTANCE, ++count); @@ -54,7 +56,7 @@ static void showData(int[] data) { for (int i = 0; i < data.length; i++) { int length = String.valueOf(SCOPE).length(); - System.out.printf("%"+length+"d ", data[i]); + System.out.printf("%" + length + "d ", data[i]); if ((i + 1) % 19 == 0) { System.out.println(); } diff --git a/java-algorithms/src/test/java/com/github/kuangcp/sort/SortTest.java b/java-algorithms/src/test/java/com/github/kuangcp/sort/SortTest.java index 5a339ebe..543e9d87 100644 --- a/java-algorithms/src/test/java/com/github/kuangcp/sort/SortTest.java +++ b/java-algorithms/src/test/java/com/github/kuangcp/sort/SortTest.java @@ -12,43 +12,44 @@ @Slf4j public class SortTest { + // test sort algorithms was correct @Test public void testSortCorrect() { - MainSortHelper.AMOUNT = 17; - MainSortHelper.SCOPE = 999; - MainSortHelper.show = true; + SortHelper.AMOUNT = 17; + SortHelper.SCOPE = 999; + SortHelper.show = true; - MainSortHelper.init(); + SortHelper.init(); - MainSortHelper.algorithms.forEach((k, v) -> { - log.info("sort: name={}", k.getName()); + SortHelper.algorithms.forEach((sort, sortNo) -> { + log.info("sort: name={}", sort.getName()); - int[] data = MainSortHelper.data.get(v); + int[] data = SortHelper.data.get(sortNo); - MainSortHelper.showData(data); - k.sort(data); - MainSortHelper.showData(data); + SortHelper.showData(data); + sort.sort(data); + SortHelper.showData(data); - MainSortHelper.validate(data); + SortHelper.validate(data); System.out.println(); }); } @Test public void testSortPerformance() { - MainSortHelper.AMOUNT = 8000; - MainSortHelper.SCOPE = 1000000; + SortHelper.AMOUNT = 2000; + SortHelper.SCOPE = 10; - MainSortHelper.init(); + SortHelper.init(); - MainSortHelper.algorithms.forEach((k, v) -> { - int[] data = MainSortHelper.data.get(v); + SortHelper.algorithms.forEach((sort, sortNo) -> { + int[] data = SortHelper.data.get(sortNo); GetRunTime runTime = new GetRunTime().startCount(); - k.sort(data); - runTime.endCountOneLine(k.getName()); + sort.sort(data); + runTime.endCountOneLine(sort.getName()); - MainSortHelper.validate(data); + SortHelper.validate(data); System.out.println(); }); } From b6a8172db8c7ac42663598fcd7bf4b5baba2c902 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Thu, 2 May 2019 10:51:57 +0800 Subject: [PATCH 026/476] *) change sort --- .../java/com/github/kuangcp/sort/Bubble.java | 18 ++++---- .../java/com/github/kuangcp/sort/Insert.java | 20 +++++---- .../java/com/github/kuangcp/sort/Quick.java | 8 +++- .../java/com/github/kuangcp/sort/Radix.java | 9 ++-- .../java/com/github/kuangcp/sort/Select.java | 19 +++++---- .../java/com/github/kuangcp/sort/Shell.java | 9 ++-- .../github/kuangcp/sort/SortAlgorithm.java | 2 +- .../com/github/kuangcp/sort/SortHelper.java | 42 +++++++++---------- .../com/github/kuangcp/sort/SortTest.java | 22 ++++------ 9 files changed, 82 insertions(+), 67 deletions(-) diff --git a/java-algorithms/src/main/java/com/github/kuangcp/sort/Bubble.java b/java-algorithms/src/main/java/com/github/kuangcp/sort/Bubble.java index ccc53c67..6710be56 100644 --- a/java-algorithms/src/main/java/com/github/kuangcp/sort/Bubble.java +++ b/java-algorithms/src/main/java/com/github/kuangcp/sort/Bubble.java @@ -1,5 +1,7 @@ package com.github.kuangcp.sort; +import java.util.Arrays; + /** * 冒泡排序,从小到大 * 最坏的情况:O(n^2) @@ -10,18 +12,20 @@ public enum Bubble implements SortAlgorithm { INSTANCE; - public void sort(int[] arr) { + public int[] sort(int[] data) { + int[] result = Arrays.copyOf(data, data.length); - for (int i = 1; i < arr.length; i++) { + for (int i = 1; i < result.length; i++) { //用来冒泡的语句,0到已排序的部分 - for (int j = 0; j < arr.length - i; j++) { + for (int j = 0; j < result.length - i; j++) { //大就交换,把最大的沉入最后 - if (arr[j] > arr[j + 1]) { - int temp = arr[j + 1]; - arr[j + 1] = arr[j]; - arr[j] = temp; + if (result[j] > result[j + 1]) { + int temp = result[j + 1]; + result[j + 1] = result[j]; + result[j] = temp; } } } + return result; } } diff --git a/java-algorithms/src/main/java/com/github/kuangcp/sort/Insert.java b/java-algorithms/src/main/java/com/github/kuangcp/sort/Insert.java index 75db2980..0172628e 100644 --- a/java-algorithms/src/main/java/com/github/kuangcp/sort/Insert.java +++ b/java-algorithms/src/main/java/com/github/kuangcp/sort/Insert.java @@ -1,5 +1,7 @@ package com.github.kuangcp.sort; +import java.util.Arrays; + /** * 插入法排序,由小到大 * 最坏的情况就是数列是有序的大到小,那么需要比较和移动 n(n+1)/2 次 时间复杂度是O(n^2) @@ -12,19 +14,21 @@ public enum Insert implements SortAlgorithm { INSTANCE; - public void sort(int[] arr) { - int i, j; - int data; - for (i = 1; i < arr.length; i++) { - data = arr[i]; + public int[] sort(int[] data) { + int[] result = Arrays.copyOf(data, data.length); + int i, j, tmp; + + for (i = 1; i < result.length; i++) { + tmp = result[i]; j = i - 1; //比较,如果是比前面还要小,就将数字往后移动一位。将小的那一位插入到合适位置 - while (j >= 0 && data < arr[j]) { - arr[j + 1] = arr[j]; + while (j >= 0 && tmp < result[j]) { + result[j + 1] = result[j]; j--; } - arr[j + 1] = data; + result[j + 1] = tmp; } + return result; } } diff --git a/java-algorithms/src/main/java/com/github/kuangcp/sort/Quick.java b/java-algorithms/src/main/java/com/github/kuangcp/sort/Quick.java index 3f2a0b44..8d2c0d8b 100644 --- a/java-algorithms/src/main/java/com/github/kuangcp/sort/Quick.java +++ b/java-algorithms/src/main/java/com/github/kuangcp/sort/Quick.java @@ -1,5 +1,7 @@ package com.github.kuangcp.sort; +import java.util.Arrays; + /** * 快速排序 * 最坏的情况:当数列是一样的数据 O(n^2) @@ -12,9 +14,11 @@ public enum Quick implements SortAlgorithm { INSTANCE; @Override - public void sort(int[] data) { + public int[] sort(int[] data) { + int[] result = Arrays.copyOf(data, data.length); QuickSort sort = new FirstImpl(); - sort.sortData(data, 0, data.length - 1); + sort.sortData(result, 0, result.length - 1); + return result; } diff --git a/java-algorithms/src/main/java/com/github/kuangcp/sort/Radix.java b/java-algorithms/src/main/java/com/github/kuangcp/sort/Radix.java index 398434f3..3ed184e0 100644 --- a/java-algorithms/src/main/java/com/github/kuangcp/sort/Radix.java +++ b/java-algorithms/src/main/java/com/github/kuangcp/sort/Radix.java @@ -10,7 +10,7 @@ import java.util.stream.Collectors; /** - * 基数排序 箱排序 + * 基数排序 * * @author Myth */ @@ -21,8 +21,10 @@ public enum Radix implements SortAlgorithm { //需要确定盒子的轮数 也就是数值的最大位数 static int maxLen = 5; - public void sort(int[] data) { + public int[] sort(int[] data) { + int[] result = Arrays.copyOf(data, data.length); List temp = Arrays.stream(data).boxed().collect(Collectors.toList()); + // 0 -> []. 1 -> [], ... 9 -> [] Map> box = new HashMap<>(); @@ -42,7 +44,8 @@ public void sort(int[] data) { } } - setArray(temp, data); + setArray(temp, result); + return result; } private static void addValue(Map> box, int weight, int value) { diff --git a/java-algorithms/src/main/java/com/github/kuangcp/sort/Select.java b/java-algorithms/src/main/java/com/github/kuangcp/sort/Select.java index d4e4cdbc..24d17e2b 100644 --- a/java-algorithms/src/main/java/com/github/kuangcp/sort/Select.java +++ b/java-algorithms/src/main/java/com/github/kuangcp/sort/Select.java @@ -1,5 +1,7 @@ package com.github.kuangcp.sort; +import java.util.Arrays; + /** * 选择排序,小到大 * 原理是:第一个依次与后面所有元素进行比较,遇到比自己小的就交换直到最后,第二轮就拿第二个元素去依次比较 @@ -9,17 +11,20 @@ public enum Select implements SortAlgorithm { INSTANCE; - public void sort(int[] arr) { - for (int i = 0; i < arr.length - 1; i++) { + public int[] sort(int[] data) { + int[] result = Arrays.copyOf(data, data.length); + + for (int i = 0; i < result.length - 1; i++) { //这个循环就是把较小数堆叠在数组头,依次与后面比较再交换 - for (int j = i + 1; j < arr.length; j++) { - if (arr[i] > arr[j]) { - int temp = arr[i]; - arr[i] = arr[j]; - arr[j] = temp; + for (int j = i + 1; j < result.length; j++) { + if (result[i] > result[j]) { + int temp = result[i]; + result[i] = result[j]; + result[j] = temp; } } } + return result; } } diff --git a/java-algorithms/src/main/java/com/github/kuangcp/sort/Shell.java b/java-algorithms/src/main/java/com/github/kuangcp/sort/Shell.java index 880225e6..186005bc 100644 --- a/java-algorithms/src/main/java/com/github/kuangcp/sort/Shell.java +++ b/java-algorithms/src/main/java/com/github/kuangcp/sort/Shell.java @@ -1,6 +1,7 @@ package com.github.kuangcp.sort; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; /** @@ -18,9 +19,10 @@ public enum Shell implements SortAlgorithm { INSTANCE; - public void sort(int[] arr) { - //如果改变了引用的指向,是对调用者无效的,要改变该引用的内存上的数据 -// Test.display(arr); + + // FIXME + public int[] sort(int[] data) { + int[] arr = Arrays.copyOf(data, data.length); List> dataList = new ArrayList<>(); List arrs = new ArrayList<>(); @@ -65,5 +67,6 @@ public void sort(int[] arr) { dataList = temp; last = randomNum; } + return arr; } } diff --git a/java-algorithms/src/main/java/com/github/kuangcp/sort/SortAlgorithm.java b/java-algorithms/src/main/java/com/github/kuangcp/sort/SortAlgorithm.java index 2e501bbe..f8599153 100644 --- a/java-algorithms/src/main/java/com/github/kuangcp/sort/SortAlgorithm.java +++ b/java-algorithms/src/main/java/com/github/kuangcp/sort/SortAlgorithm.java @@ -5,7 +5,7 @@ */ public interface SortAlgorithm { - void sort(int[] data); + int[] sort(int[] data); default String getName() { return this.getClass().getSimpleName(); diff --git a/java-algorithms/src/main/java/com/github/kuangcp/sort/SortHelper.java b/java-algorithms/src/main/java/com/github/kuangcp/sort/SortHelper.java index 370a7521..cc01b10c 100644 --- a/java-algorithms/src/main/java/com/github/kuangcp/sort/SortHelper.java +++ b/java-algorithms/src/main/java/com/github/kuangcp/sort/SortHelper.java @@ -1,8 +1,7 @@ package com.github.kuangcp.sort; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Map; +import java.util.Arrays; +import java.util.List; import java.util.concurrent.ThreadLocalRandom; import lombok.extern.slf4j.Slf4j; @@ -22,40 +21,37 @@ class SortHelper { static int SCOPE = 999999; static boolean show = false; - static Map algorithms = new HashMap<>(); + static List algorithms = Arrays.asList( + Radix.INSTANCE + , Bubble.INSTANCE + , Insert.INSTANCE + , Select.INSTANCE + , Quick.INSTANCE +// , Shell.INSTANCE + ); - static ArrayList data = new ArrayList<>(); + static int[] data; static void init() { - Radix.maxLen = String.valueOf(SCOPE).length(); + Radix.maxLen = getScopeLength(); - int count = -1; - algorithms.put(Radix.INSTANCE, ++count); - algorithms.put(Bubble.INSTANCE, ++count); - algorithms.put(Insert.INSTANCE, ++count); - algorithms.put(Select.INSTANCE, ++count); - algorithms.put(Shell.INSTANCE, ++count); - algorithms.put(Quick.INSTANCE, ++count); - - for (int i = 0; i < algorithms.size(); i++) { - int[] temp = new int[AMOUNT]; - data.add(temp); - } + data = new int[AMOUNT]; for (int i = 0; i < AMOUNT; i++) { - int temp = ThreadLocalRandom.current().nextInt(SCOPE); - for (int j = 0; j < algorithms.size(); j++) { - data.get(j)[i] = temp; - } + data[i] = ThreadLocalRandom.current().nextInt(SCOPE); } } + private static int getScopeLength() { + return String.valueOf(SCOPE).length(); + } + static void showData(int[] data) { if (!show) { return; } for (int i = 0; i < data.length; i++) { - int length = String.valueOf(SCOPE).length(); + int length = getScopeLength(); System.out.printf("%" + length + "d ", data[i]); if ((i + 1) % 19 == 0) { System.out.println(); diff --git a/java-algorithms/src/test/java/com/github/kuangcp/sort/SortTest.java b/java-algorithms/src/test/java/com/github/kuangcp/sort/SortTest.java index 543e9d87..9d669313 100644 --- a/java-algorithms/src/test/java/com/github/kuangcp/sort/SortTest.java +++ b/java-algorithms/src/test/java/com/github/kuangcp/sort/SortTest.java @@ -15,22 +15,21 @@ public class SortTest { // test sort algorithms was correct @Test public void testSortCorrect() { - SortHelper.AMOUNT = 17; + SortHelper.AMOUNT = 170; SortHelper.SCOPE = 999; SortHelper.show = true; SortHelper.init(); - SortHelper.algorithms.forEach((sort, sortNo) -> { + SortHelper.algorithms.forEach(sort -> { log.info("sort: name={}", sort.getName()); + int[] data = SortHelper.data; - int[] data = SortHelper.data.get(sortNo); - - SortHelper.showData(data); - sort.sort(data); SortHelper.showData(data); + int[] result = sort.sort(data); + SortHelper.showData(result); - SortHelper.validate(data); + SortHelper.validate(result); System.out.println(); }); } @@ -42,15 +41,12 @@ public void testSortPerformance() { SortHelper.init(); - SortHelper.algorithms.forEach((sort, sortNo) -> { - int[] data = SortHelper.data.get(sortNo); - + SortHelper.algorithms.forEach(sort -> { GetRunTime runTime = new GetRunTime().startCount(); - sort.sort(data); + int[] result = sort.sort(SortHelper.data); runTime.endCountOneLine(sort.getName()); - SortHelper.validate(data); - System.out.println(); + SortHelper.validate(result); }); } } From 1a5104197e84d03d9300e31c1867fd087aad0593 Mon Sep 17 00:00:00 2001 From: kcp Date: Mon, 6 May 2019 12:49:51 +0800 Subject: [PATCH 027/476] *) learn oom killer --- java-class/src/main/java/jvm/oom/Readme.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/java-class/src/main/java/jvm/oom/Readme.md b/java-class/src/main/java/jvm/oom/Readme.md index a021b9e9..79b8f057 100644 --- a/java-class/src/main/java/jvm/oom/Readme.md +++ b/java-class/src/main/java/jvm/oom/Readme.md @@ -18,4 +18,7 @@ javac A.java java A -疑问: 好几种情况并没有出现OOM而是直接被Killed了, 要思考Docker内存限制的实现方式了 +疑问: 好几种情况并没有出现OOM而是直接被Killed了 + - 这是因为 Linux 的 OOM Killer [Linux OOM killer](https://segmentfault.com/a/1190000008268803) + - 可执行 dmesg 看到类似于 `Out of memory: Kill process 10375 (java) score 59 or sacrifice child` + From 79dc07517710ca9aee053d676d2dded4f390e031 Mon Sep 17 00:00:00 2001 From: kcp Date: Tue, 7 May 2019 20:08:24 +0800 Subject: [PATCH 028/476] -) delete useless file --- java-class/src/main/java/jvm/oom/Readme.md | 1 + .../main/java/thread/HowToCreateThread.java | 11 +-- .../thread/ShowCreateThreadForSimpleMain.java | 2 +- java-thread/build.gradle | 3 - java-thread/src/main/resources/logback.xml | 82 ------------------- 5 files changed, 8 insertions(+), 91 deletions(-) delete mode 100644 java-thread/build.gradle delete mode 100644 java-thread/src/main/resources/logback.xml diff --git a/java-class/src/main/java/jvm/oom/Readme.md b/java-class/src/main/java/jvm/oom/Readme.md index 79b8f057..465767c2 100644 --- a/java-class/src/main/java/jvm/oom/Readme.md +++ b/java-class/src/main/java/jvm/oom/Readme.md @@ -22,3 +22,4 @@ java A - 这是因为 Linux 的 OOM Killer [Linux OOM killer](https://segmentfault.com/a/1190000008268803) - 可执行 dmesg 看到类似于 `Out of memory: Kill process 10375 (java) score 59 or sacrifice child` +- [ ] 关闭 oom-killer diff --git a/java-concurrency/src/main/java/thread/HowToCreateThread.java b/java-concurrency/src/main/java/thread/HowToCreateThread.java index e1ea8b79..1645f1d7 100644 --- a/java-concurrency/src/main/java/thread/HowToCreateThread.java +++ b/java-concurrency/src/main/java/thread/HowToCreateThread.java @@ -4,11 +4,10 @@ * Created by https://github.com/kuangcp on 17-8-20 下午8:44 * 创建线程的多种方式 */ -public class HowToCreateThread { +class HowToCreateThread { /** - * 继承方式来实现线程 - * 继承Thread 重写run方法 + * 继承方式来实现线程 继承Thread 重写run方法 */ static class ExampleOne extends Thread { @@ -27,8 +26,7 @@ public void shutdown() { } /** - * 实现接口方式来实现线程 - * Runnable不是线程,是线程要运行的代码的宿主。 + * 实现接口方式来实现线程 Runnable不是线程,是线程要运行的代码的宿主。 */ static class ExampleTwo implements Runnable { @@ -68,6 +66,9 @@ public void run() { System.out.println("直接实例化一个匿名内部方法在方法体里"); } }).start(); + + // 方法引用 + new Thread(this::test).start(); } } } diff --git a/java-concurrency/src/main/java/thread/ShowCreateThreadForSimpleMain.java b/java-concurrency/src/main/java/thread/ShowCreateThreadForSimpleMain.java index b80c0a99..09c59547 100644 --- a/java-concurrency/src/main/java/thread/ShowCreateThreadForSimpleMain.java +++ b/java-concurrency/src/main/java/thread/ShowCreateThreadForSimpleMain.java @@ -25,4 +25,4 @@ public static void main(String[] args) { //name=Finalizer 调用对象 finalize() //name=Reference Handler 清除 Reference //name=main -} +} \ No newline at end of file diff --git a/java-thread/build.gradle b/java-thread/build.gradle deleted file mode 100644 index 71fdb0f5..00000000 --- a/java-thread/build.gradle +++ /dev/null @@ -1,3 +0,0 @@ -dependencies { - testCompile rootProject.libs['testng'] -} \ No newline at end of file diff --git a/java-thread/src/main/resources/logback.xml b/java-thread/src/main/resources/logback.xml deleted file mode 100644 index 5e375431..00000000 --- a/java-thread/src/main/resources/logback.xml +++ /dev/null @@ -1,82 +0,0 @@ - - - - - - - - %d{HH:mm:ss.SSS} %highlight(%-5level) [%thread] %logger{30}:%yellow(%-3L) %msg%n - - - DEBUG - - - - - - ${log.base}debug.log - true - - ${log.base}debug.%d{yyyy-MM-dd}.log - - - %d{MM-dd HH:mm:ss.SSS} |-%-5level |%thread| %logger{36}:%L - %msg%n - - - DEBUG - - - - - - ${log.base}warn.log - true - - ${log.base}warn.%d{yyyy-MM-dd}.log - - - %d{MM-dd HH:mm:ss.SSS} |-%-5level %logger{36}:%L - %msg%n - - - WARN - - - - - - ${log.base}info.log - true - - ${log.base}info.%d{yyyy-MM-dd}.log - - - %d{MM-dd HH:mm:ss.SSS} |-%-5level %logger{36}:%L - %msg%n - - - INFO - - - - - - ${log.base}error.log - true - - ${log.base}error.%d{yyyy-MM-dd}.log - - - %d{MM-dd HH:mm:ss.SSS} | %thread |-%-5level %logger{36}:%L - %msg%n - - - ERROR - - - - - - - - - - - \ No newline at end of file From b5d90e6bc57c643fa4620354eab07448fe424f9b Mon Sep 17 00:00:00 2001 From: kcp Date: Wed, 8 May 2019 20:58:03 +0800 Subject: [PATCH 029/476] +) sync for class --- .../com/github/kuangcp/FinalPoolLearn.java | 28 ------------------- .../com/github/kuangcp/ValueCacheDemo.java | 27 ++++++++++++++++++ .../kuangcp/singleton/StaticInitWithSync.java | 21 -------------- .../singleton/StaticLazyWithSyncBlock.java | 23 +++++++++++++++ .../singleton/StaticLazyWithSyncMethod.java | 21 ++++++++++++++ 5 files changed, 71 insertions(+), 49 deletions(-) delete mode 100644 java-class/src/main/java/com/github/kuangcp/FinalPoolLearn.java create mode 100644 java-class/src/main/java/com/github/kuangcp/ValueCacheDemo.java delete mode 100644 java-pattern/src/main/java/com/github/kuangcp/singleton/StaticInitWithSync.java create mode 100644 java-pattern/src/main/java/com/github/kuangcp/singleton/StaticLazyWithSyncBlock.java create mode 100644 java-pattern/src/main/java/com/github/kuangcp/singleton/StaticLazyWithSyncMethod.java diff --git a/java-class/src/main/java/com/github/kuangcp/FinalPoolLearn.java b/java-class/src/main/java/com/github/kuangcp/FinalPoolLearn.java deleted file mode 100644 index b27f3adc..00000000 --- a/java-class/src/main/java/com/github/kuangcp/FinalPoolLearn.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.github.kuangcp; - -/** - * Created by https://github.com/kuangcp on 17-8-21 下午2:41 - * List常见用法 - */ -public class FinalPoolLearn { - - public static void main(String[]s ){ - testInteger(); - } - - //java中的基本类型的包装类、其中 Byte、Boolean、Short、Character、Integer、Long 实现了常量池技术 - // (除了Boolean,都只对小于128的值才支持) - private static void testInteger(){ -// 对于-128~127的Integer对象才会到IntegerCache里获取缓存,使用常量池技术 - Integer i1 = 100; - Integer i2 = 100; - // 上面两行代码,使用自动装箱特性,编译成 - // Integer i1 = Integer.valueOf(100); - // Integer i2 = Integer.valueOf(100); - System.out.println(i1 == i2); - - Integer i3 = 128; - Integer i4 = 128; - System.out.println(i3 == i4); - } -} diff --git a/java-class/src/main/java/com/github/kuangcp/ValueCacheDemo.java b/java-class/src/main/java/com/github/kuangcp/ValueCacheDemo.java new file mode 100644 index 00000000..24943b6f --- /dev/null +++ b/java-class/src/main/java/com/github/kuangcp/ValueCacheDemo.java @@ -0,0 +1,27 @@ +package com.github.kuangcp; + +/** + * Created by https://github.com/kuangcp on 17-8-21 下午2:41 + */ +public class ValueCacheDemo { + + public static void main(String[] s) { + testInteger(); + } + + //java中的基本类型的包装类、其中 Byte、Boolean、Short、Character、Integer、Long 实现了常量池技术 + // (除了Boolean,都只对小于128的值才支持) + private static void testInteger() { +// 对于-128~127的Integer对象才会到IntegerCache里获取缓存,使用常量池技术 + Integer i1 = 100; + Integer i2 = 100; + // 上面两行代码,使用自动装箱特性,编译成 + // Integer i1 = Integer.valueOf(100); + // Integer i2 = Integer.valueOf(100); + System.out.println(i1 == i2); + + Integer i3 = 128; + Integer i4 = 128; + System.out.println(i3 == i4); + } +} diff --git a/java-pattern/src/main/java/com/github/kuangcp/singleton/StaticInitWithSync.java b/java-pattern/src/main/java/com/github/kuangcp/singleton/StaticInitWithSync.java deleted file mode 100644 index 602b3575..00000000 --- a/java-pattern/src/main/java/com/github/kuangcp/singleton/StaticInitWithSync.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.github.kuangcp.singleton; - -/** - * 懒汉式 (线程安全,同步方法) [不推荐用] 有额外的锁, 同步消耗 - * - * @author kuangcp on 2019-04-11 12:40 PM - */ -public class StaticInitWithSync { - - private static StaticInitWithSync singleton; - - private StaticInitWithSync() { - } - - public synchronized static StaticInitWithSync getInstance() { - if (singleton == null) { - singleton = new StaticInitWithSync(); - } - return singleton; - } -} diff --git a/java-pattern/src/main/java/com/github/kuangcp/singleton/StaticLazyWithSyncBlock.java b/java-pattern/src/main/java/com/github/kuangcp/singleton/StaticLazyWithSyncBlock.java new file mode 100644 index 00000000..7353e2bc --- /dev/null +++ b/java-pattern/src/main/java/com/github/kuangcp/singleton/StaticLazyWithSyncBlock.java @@ -0,0 +1,23 @@ +package com.github.kuangcp.singleton; + +/** + * TODO 验证 + * @author https://github.com/kuangcp + * @date 2019-05-08 14:26 + */ +public class StaticLazyWithSyncBlock { + + private static StaticLazyWithSyncBlock singleton; + + private StaticLazyWithSyncBlock() { + } + + public static StaticLazyWithSyncBlock getInstance() { + synchronized (StaticLazyWithSyncBlock.class) { + if (singleton == null) { + singleton = new StaticLazyWithSyncBlock(); + } + } + return singleton; + } +} diff --git a/java-pattern/src/main/java/com/github/kuangcp/singleton/StaticLazyWithSyncMethod.java b/java-pattern/src/main/java/com/github/kuangcp/singleton/StaticLazyWithSyncMethod.java new file mode 100644 index 00000000..18f7095d --- /dev/null +++ b/java-pattern/src/main/java/com/github/kuangcp/singleton/StaticLazyWithSyncMethod.java @@ -0,0 +1,21 @@ +package com.github.kuangcp.singleton; + +/** + * 懒汉式 (线程安全,同步方法) [不推荐用] 有额外的锁, 同步消耗 + * + * @author kuangcp on 2019-04-11 12:40 PM + */ +public class StaticLazyWithSyncMethod { + + private static StaticLazyWithSyncMethod singleton; + + private StaticLazyWithSyncMethod() { + } + + public synchronized static StaticLazyWithSyncMethod getInstance() { + if (singleton == null) { + singleton = new StaticLazyWithSyncMethod(); + } + return singleton; + } +} From f18b6b56e1d6bfd5ac1c9e4f429ef6dbbe24a08a Mon Sep 17 00:00:00 2001 From: kcp Date: Fri, 10 May 2019 18:11:56 +0800 Subject: [PATCH 030/476] +) learn instanceof --- README.md | 2 +- .../src/test/java/syntax/InstanceofTest.java | 41 +++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 java-class/src/test/java/syntax/InstanceofTest.java diff --git a/README.md b/README.md index c49edcfe..7cc581a4 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,6 @@ ************************ ## 同类仓库 -> [tutorials](https://github.com/eugenp/tutorials) +> [tutorials](https://github.com/eugenp/tutorials) `baeldung` > [Java 学习笔记](https://github.com/brianway/java-learning) > [demo](https://gitee.com/code4everything/demo) diff --git a/java-class/src/test/java/syntax/InstanceofTest.java b/java-class/src/test/java/syntax/InstanceofTest.java new file mode 100644 index 00000000..34974eea --- /dev/null +++ b/java-class/src/test/java/syntax/InstanceofTest.java @@ -0,0 +1,41 @@ +package syntax; + +import static org.hamcrest.Matchers.equalTo; +import static org.junit.Assert.assertThat; + +import java.util.Date; +import org.junit.Test; + +/** + * @author https://github.com/Kuangcp + * @date 2019-05-10 08:18 + */ +public class InstanceofTest { + + @Test + public void test() { + assertThat("" instanceof Object, equalTo(true)); + assertThat(new String() instanceof String, equalTo(true)); + assertThat(new Object() instanceof String, equalTo(false)); + // 如果左操作数 为 null 直接返回 false + assertThat(null instanceof String, equalTo(false)); + assertThat((String) null instanceof String, equalTo(false)); + assertThat(new GenericClass().isDateInstance(""), equalTo(false)); + + // instanceof 只能用于对象 不能用于基本类型 +// assertThat('A' instanceof Character, equalTo(true)); + + // 编译失败, 因为 instanceof 左右需要有继承或实现关系才能编译通过 +// assertThat(new Date() instanceof String, equalTo(false)); + + } + + class GenericClass { + + boolean isDateInstance(T t) { + return t instanceof Date; + } + } + + +} From f537c777606d57b4168f5a36f9f7c899931a8a3c Mon Sep 17 00:00:00 2001 From: kcp Date: Mon, 13 May 2019 18:48:27 +0800 Subject: [PATCH 031/476] +) learn toMap collectors --- .../github/kuangcp/stream/map/ToMapTest.java | 50 +++++++++++++++++++ .../src/test/java/log/CorrectLogTest.java | 1 + 2 files changed, 51 insertions(+) create mode 100644 java-8/src/test/java/com/github/kuangcp/stream/map/ToMapTest.java diff --git a/java-8/src/test/java/com/github/kuangcp/stream/map/ToMapTest.java b/java-8/src/test/java/com/github/kuangcp/stream/map/ToMapTest.java new file mode 100644 index 00000000..00607add --- /dev/null +++ b/java-8/src/test/java/com/github/kuangcp/stream/map/ToMapTest.java @@ -0,0 +1,50 @@ +package com.github.kuangcp.stream.map; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import lombok.Builder; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; + +/** + * @author https://github.com/kuangcp + * @date 2019-05-13 10:34 + */ +@Slf4j +public class ToMapTest { + + @Data + @Builder + static class Phone { + + String name; + int price; + } + + @SuppressWarnings("ResultOfMethodCallIgnored") + @Test(expected = IllegalStateException.class) + public void testDuplicated() { + List phones = getPhones(); + + phones.stream().collect(Collectors.toMap(Phone::getName, Phone::getPrice)); + } + + @Test + public void testDuplicatedWithHandled() { + List phones = getPhones(); + + // 发生重复后, 后来的值覆盖前面的值, 如果 (v1, v2) -> v1 则是忽略后来的值 + Map result = phones.stream() + .collect(Collectors.toMap(Phone::getName, Phone::getPrice, (v1, v2) -> v2)); + result.forEach((k, v) -> log.info("{} {}", k, v)); + } + + private List getPhones() { + return Arrays.asList(Phone.builder().name("a").price(1).build(), + Phone.builder().name("b").price(2).build(), + Phone.builder().name("a").price(3).build()); + } +} diff --git a/java-class/src/test/java/log/CorrectLogTest.java b/java-class/src/test/java/log/CorrectLogTest.java index 3f6f6fa0..dff264f3 100644 --- a/java-class/src/test/java/log/CorrectLogTest.java +++ b/java-class/src/test/java/log/CorrectLogTest.java @@ -32,6 +32,7 @@ public void testCorrectLog() { // Exception ClassName and message log.debug("C"); log.error("" + e); //C + log.debug("D"); log.error(e.toString()); //D From de830058bff40e28bcce147fa5e67eb3947273c3 Mon Sep 17 00:00:00 2001 From: kcp Date: Wed, 15 May 2019 09:34:30 +0800 Subject: [PATCH 032/476] +) equals example --- .../instantiation/ComplexConstructor.java | 39 ++++++ .../instantiation/StaticFieldInit.java | 24 ++++ .../instantiation/ComplexConstructorTest.java | 25 ++++ .../instantiation/StaticFieldInitTest.java | 20 +++ .../base/method/EqualsAndHashCodeTest.java | 118 ++++++++++++++++++ .../{ => base}/method/ParamTransferTest.java | 2 +- .../syntax/method/EqualsAndHashCodeTest.java | 48 ------- 7 files changed, 227 insertions(+), 49 deletions(-) create mode 100644 java-class/src/main/java/com/github/kuangcp/instantiation/ComplexConstructor.java create mode 100644 java-class/src/main/java/com/github/kuangcp/instantiation/StaticFieldInit.java create mode 100644 java-class/src/test/java/com/github/kuangcp/instantiation/ComplexConstructorTest.java create mode 100644 java-class/src/test/java/com/github/kuangcp/instantiation/StaticFieldInitTest.java create mode 100644 java-class/src/test/java/syntax/base/method/EqualsAndHashCodeTest.java rename java-class/src/test/java/syntax/{ => base}/method/ParamTransferTest.java (95%) delete mode 100644 java-class/src/test/java/syntax/method/EqualsAndHashCodeTest.java diff --git a/java-class/src/main/java/com/github/kuangcp/instantiation/ComplexConstructor.java b/java-class/src/main/java/com/github/kuangcp/instantiation/ComplexConstructor.java new file mode 100644 index 00000000..2241f11b --- /dev/null +++ b/java-class/src/main/java/com/github/kuangcp/instantiation/ComplexConstructor.java @@ -0,0 +1,39 @@ +package com.github.kuangcp.instantiation; + +import lombok.extern.slf4j.Slf4j; + +/** + * @author https://github.com/kuangcp + * @date 2019-05-15 09:20 + */ +@Slf4j +public class ComplexConstructor { + + static abstract class AbstractServer { + + int actualPort; + static final int DEFAULT_PORT = 8848; + + AbstractServer() { + actualPort = getPort(); + + log.info("port={}", actualPort); + } + + abstract int getPort(); + } + + public static class Server extends AbstractServer { + + private int port = 8080; + + public Server(int port) { + this.port = port; + } + + @Override + int getPort() { + return Math.random() > 0.5 ? port : DEFAULT_PORT; + } + } +} diff --git a/java-class/src/main/java/com/github/kuangcp/instantiation/StaticFieldInit.java b/java-class/src/main/java/com/github/kuangcp/instantiation/StaticFieldInit.java new file mode 100644 index 00000000..be313353 --- /dev/null +++ b/java-class/src/main/java/com/github/kuangcp/instantiation/StaticFieldInit.java @@ -0,0 +1,24 @@ +package com.github.kuangcp.instantiation; + +/** + * 看起来 count 似乎是先使用再声明的 + * 静态变量是类加载时期就分配到了数据区, 在内存中只有一个, 不会分配多次, 其后所有的操作都是值改变, 地址不会变 ?? + * 类初始化的时候, 先去查找类中的所有静态声明, 然后分配空间, 这时候只有空间, 还没有赋值, 然后依据代码的顺序执行, 进行赋值 + * IDEA 里面避免这个问题是比较容易的, 会有明显的警告 + * + * @author https://github.com/kuangcp + * @date 2019-05-15 09:03 + */ +public class StaticFieldInit { + + static int num = 1; // 这个值被覆盖 + + static { + num = 2; + count = 2; // 这个值被覆盖, 看起来似乎是先使用再声明 + // 由于按顺序执行, 所以 count = 2 count = 1, 最终为1 + } + + static int count = 1; + +} diff --git a/java-class/src/test/java/com/github/kuangcp/instantiation/ComplexConstructorTest.java b/java-class/src/test/java/com/github/kuangcp/instantiation/ComplexConstructorTest.java new file mode 100644 index 00000000..5a8eaccd --- /dev/null +++ b/java-class/src/test/java/com/github/kuangcp/instantiation/ComplexConstructorTest.java @@ -0,0 +1,25 @@ +package com.github.kuangcp.instantiation; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.IsEqual.equalTo; + +import com.github.kuangcp.instantiation.ComplexConstructor.Server; +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; + +/** + * @author https://github.com/kuangcp + * @date 2019-05-15 09:21 + */ +@Slf4j +public class ComplexConstructorTest { + + @Test + public void test() { + Server server = new Server(3306); + + // TODO 解释 + assertThat(server.actualPort == 0 || server.actualPort == Server.DEFAULT_PORT, + equalTo(true)); + } +} diff --git a/java-class/src/test/java/com/github/kuangcp/instantiation/StaticFieldInitTest.java b/java-class/src/test/java/com/github/kuangcp/instantiation/StaticFieldInitTest.java new file mode 100644 index 00000000..ebfb52fd --- /dev/null +++ b/java-class/src/test/java/com/github/kuangcp/instantiation/StaticFieldInitTest.java @@ -0,0 +1,20 @@ +package com.github.kuangcp.instantiation; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.IsEqual.equalTo; + +import org.junit.Test; + +/** + * @author https://github.com/kuangcp + * @date 2019-05-15 09:11 + */ + +public class StaticFieldInitTest { + + @Test + public void test(){ + assertThat(StaticFieldInit.num , equalTo(2)); + assertThat(StaticFieldInit.count , equalTo(1)); + } +} diff --git a/java-class/src/test/java/syntax/base/method/EqualsAndHashCodeTest.java b/java-class/src/test/java/syntax/base/method/EqualsAndHashCodeTest.java new file mode 100644 index 00000000..eb4b9242 --- /dev/null +++ b/java-class/src/test/java/syntax/base/method/EqualsAndHashCodeTest.java @@ -0,0 +1,118 @@ +package syntax.base.method; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.MatcherAssert.assertThat; + +import java.util.Objects; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; + +/** + * @author kuangcp on 3/5/19-11:17 AM + * 值类 必须重写 equals 使得比较时比较的是所有属性的值而不是地址 + * 其他普通类 不重写 + */ +@Slf4j +public class EqualsAndHashCodeTest { + + abstract class Flyable { + + } + + // 当一个类继承抽象类时, Lombok 的 EqualsAndHashCode 注解会有警告 + // 提醒你要使用Object的equals还是lombok重写的equals + @Data + @EqualsAndHashCode(callSuper = false) + @AllArgsConstructor + private class Target extends Flyable { + + private String name; + private int age; + } + + // 若 hashCode 相等,不一定 equals; 若equals,hashCode一定相等 + @Test + public void testEquals() { + Target target = new Target("you", 3); + Target you = new Target("you", 3); + + log.info("hasCode: target={} you={}", target.hashCode(), you.hashCode()); + log.info("equals={}", target.equals(you)); + + assertThat(target.hashCode(), equalTo(you.hashCode())); + } + +/////////////////////////////////////////// + + class Electronics { + + String name; + + Electronics() { + } + + Electronics(String name) { + this.name = name; + } + + @Override + public boolean equals(Object obj) { + // 如果改成 用 类去判断 就能避免问题 +// if(Objects.nonNull(obj) && obj.getClass() == this.getClass()){ + if (Objects.nonNull(obj) && obj instanceof Electronics) { + Electronics phone = (Electronics) obj; + return phone.name.equals(name); + } + return false; + } + + @Override + public int hashCode() { + return super.hashCode(); + } + } + + class Phone extends Electronics { + + private int number; + + public Phone(int number, String name) { + this.number = number; + this.name = name; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof Phone) { + Phone phone = (Phone) obj; + return super.equals(obj) && phone.number == number; + } + return false; + } + + @Override + public int hashCode() { + return super.hashCode(); + } + } + + @Test + public void testEqualPhone() { + Phone a = new Phone(12, "HONOR"); + Phone b = new Phone(1777, "HONOR"); + + Electronics electronics = new Electronics("HONOR"); + + // 这里是因为 父类 使用的 instanceof, 子类自然能通过判断 + assertThat(electronics.equals(b), equalTo(true)); + assertThat(electronics.equals(a), equalTo(true)); + + assertThat(a.equals(b), equalTo(false)); + + } +} + + diff --git a/java-class/src/test/java/syntax/method/ParamTransferTest.java b/java-class/src/test/java/syntax/base/method/ParamTransferTest.java similarity index 95% rename from java-class/src/test/java/syntax/method/ParamTransferTest.java rename to java-class/src/test/java/syntax/base/method/ParamTransferTest.java index 01d1c409..985e057f 100644 --- a/java-class/src/test/java/syntax/method/ParamTransferTest.java +++ b/java-class/src/test/java/syntax/base/method/ParamTransferTest.java @@ -1,4 +1,4 @@ -package syntax.method; +package syntax.base.method; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; diff --git a/java-class/src/test/java/syntax/method/EqualsAndHashCodeTest.java b/java-class/src/test/java/syntax/method/EqualsAndHashCodeTest.java deleted file mode 100644 index 4319668e..00000000 --- a/java-class/src/test/java/syntax/method/EqualsAndHashCodeTest.java +++ /dev/null @@ -1,48 +0,0 @@ -package syntax.method; - -import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.MatcherAssert.assertThat; - -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.extern.slf4j.Slf4j; -import org.junit.Test; - -/** - * @author kuangcp on 3/5/19-11:17 AM - * 值类 必须重写 equals 使得比较时比较的是所有的值而不是地址 - * 其他普通类 不重写 - * 或者不暴露 equals 方法就重写并抛出异常 - */ -@Slf4j -public class EqualsAndHashCodeTest { - - abstract class Flyable { - - } - - // 当一个类继承抽象类时, Lombok 的 EqualsAndHashCode 注解会有警告, 提醒你要使用Object的equals还是lombok重写的equals - @Data - @EqualsAndHashCode(callSuper = false) - @AllArgsConstructor - class Target extends Flyable { - - private String name; - private int age; - - } - - @Test - public void testEquals() { - Target target = new Target("you", 3); - Target you = new Target("you", 3); - - log.info("hasCode: target={} you={}", target.hashCode(), you.hashCode()); - log.info("equals={}", target.equals(you)); - - assertThat(target.hashCode(), equalTo(you.hashCode())); - } -} - - From 6695096f86ad0546b1f47b8339b6f0f001352ea7 Mon Sep 17 00:00:00 2001 From: kcp Date: Fri, 17 May 2019 08:54:47 +0800 Subject: [PATCH 033/476] *) update for String Constant Pool With GC --- .../java/jvm/oom/RunTimeConstantPoolOOM.java | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/java-class/src/main/java/jvm/oom/RunTimeConstantPoolOOM.java b/java-class/src/main/java/jvm/oom/RunTimeConstantPoolOOM.java index fdf46e47..24cfe01c 100644 --- a/java-class/src/main/java/jvm/oom/RunTimeConstantPoolOOM.java +++ b/java-class/src/main/java/jvm/oom/RunTimeConstantPoolOOM.java @@ -3,25 +3,26 @@ import java.util.ArrayList; import java.util.List; import java.util.Optional; +import java.util.concurrent.TimeUnit; import java.util.stream.IntStream; /** * Java7以下 运行时字符串常量池是放在了永久代中, 就会报错 OOM: Perm space * -XX:PermSize=10M -XX:MaxPermSize=10M * - * Java7以上包括7, 运行时字符串常量池放在了堆中(Eden), 所以需要通过 -Xmx128M 来设限 + * Java7以上包括7, 运行时字符串常量池放在了堆中(Eden), 所以需要通过设置堆大小来限制 -Xms5m -Xmx5m * 最终报错: OOM heap space 或者 GC overhead limit exceeded * - * 同样的在Docker容器中运行, 同样的被Killed - * * https://www.cnblogs.com/paddix/p/5309550.html * + * 字符串的常量池不会被GC, 即使没有任何对象去引用 + * * @author kuangcp on 4/4/19-12:20 AM */ public class RunTimeConstantPoolOOM { + private static List data = new ArrayList<>(); public static void main(String[] args) throws InterruptedException { - List data = new ArrayList<>(); int i = 0; Optional result = IntStream.rangeClosed(1, 1000).mapToObj(String::valueOf) .reduce(String::concat); @@ -29,11 +30,11 @@ public static void main(String[] args) throws InterruptedException { while (true) { data.add((result.get() + i++).intern()); - // 如果加上睡眠, 就会发现 Eden 一直涨, 然后被回收掉, 并且部分转移到了 Old - // 但是转移的内存大小大约为Eden的1/4? 慢慢的 最终 OOM - // 如果不加, 瞬间 Old Gen 爆满然后 OOM heap space -// TimeUnit.MILLISECONDS.sleep(1); + // 如果不加, 瞬间 Old Gen 爆满然后 OOM : heap space + // 如果加上, 就会发现 Eden 一直涨, 然后被回收掉, 并且部分转移到了 Old + // 还能通过 visualvm 查看 内存的慢慢涨上去, 直到满了后 OOM, 内存被一下子释放掉 + TimeUnit.MILLISECONDS.sleep(20); } } } From d8d23eb593705baa5c5cca95c0f24cfcbf8fef52 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Mon, 20 May 2019 00:34:58 +0800 Subject: [PATCH 034/476] +) add kafka demo --- dependency.gradle | 19 +++++- .../java/com/github/kuangcp/sort/Tim.java | 2 +- .../test/java/syntax/bytes/OperatorTest.java | 2 +- java-kafka/build.gradle | 3 + .../java/com/github/kuangcp/hi/Constants.java | 13 ++++ .../com/github/kuangcp/hi/ConsumerDemo.java | 49 +++++++++++++++ .../com/github/kuangcp/hi/ProducerDemo.java | 56 ++++++++++++++++++ java-question/build.gradle | 3 +- java-spring/build.gradle | 59 ++++--------------- settings.gradle | 1 + 10 files changed, 153 insertions(+), 54 deletions(-) create mode 100644 java-kafka/build.gradle create mode 100644 java-kafka/src/main/java/com/github/kuangcp/hi/Constants.java create mode 100644 java-kafka/src/main/java/com/github/kuangcp/hi/ConsumerDemo.java create mode 100644 java-kafka/src/main/java/com/github/kuangcp/hi/ProducerDemo.java diff --git a/dependency.gradle b/dependency.gradle index 1c391014..12e41284 100644 --- a/dependency.gradle +++ b/dependency.gradle @@ -14,6 +14,9 @@ ext { , netty : '4.1.35.Final' , guava : '27.1-jre' , jmh : '1.2.1' + , kafka : '2.2.0' + , spring : '5.1.5.RELEASE' + , aspectj : '1.9.0' ] libs = [ // mine tool @@ -50,6 +53,7 @@ ext { // db , "jedis" : "redis.clients:jedis:3.0.1" + , "MySQL" : "mysql:mysql-connector-java:8.0.15" // JSON dependency , "gson" : "com.google.code.gson:gson:2.8.5" @@ -58,5 +62,18 @@ ext { , "jackson_databind" : "com.fasterxml.jackson.core:jackson-databind:$ver.jackson" , "jackson_annotations" : "com.fasterxml.jackson.core:jackson-annotations:$ver.jackson" , "jackson_smile" : "com.fasterxml.jackson.dataformat:jackson-dataformat-smile:$ver.jackson" + + , "aspectjrt" : "org.aspectj:aspectjrt:$ver.aspectj" + , "aspectjweaver" : "org.aspectj:aspectjweaver:$ver.aspectj" + + // Spring + , "spring-core" : "org.springframework:spring-core:$ver.spring" + , "spring-ctx" : "org.springframework:spring-context:$ver.spring" + , "spring-beans" : "org.springframework:spring-beans:$ver.spring" + , "spring-aop" : "org.springframework:spring-aop:$ver.spring" + , "spring-orm" : "org.springframework:spring-orm:$ver.spring" + + // kafka + , "kafka-clients" : "org.apache.kafka:kafka-clients:$ver.kafka" ] -} +} \ No newline at end of file diff --git a/java-algorithms/src/main/java/com/github/kuangcp/sort/Tim.java b/java-algorithms/src/main/java/com/github/kuangcp/sort/Tim.java index ff901f66..bd43398d 100644 --- a/java-algorithms/src/main/java/com/github/kuangcp/sort/Tim.java +++ b/java-algorithms/src/main/java/com/github/kuangcp/sort/Tim.java @@ -8,6 +8,6 @@ * * @author kuangcp on 18-8-27-上午12:19 */ -public class Tim { +public enum Tim { } diff --git a/java-class/src/test/java/syntax/bytes/OperatorTest.java b/java-class/src/test/java/syntax/bytes/OperatorTest.java index adc86a9c..e39245b7 100644 --- a/java-class/src/test/java/syntax/bytes/OperatorTest.java +++ b/java-class/src/test/java/syntax/bytes/OperatorTest.java @@ -41,7 +41,7 @@ public void testMod() { byte a = -64; byte b = -4; - // 取余操作 a%b = a - (a/b)*b + // 取余操作 a % b => a - (a / b) * b log.info("a % b = {} \n a / b = {}\n(a / b) * b = {}\n a - (a / b) * b = {}", a % b, a / b, (a / b) * b, a - (a / b) * b); } diff --git a/java-kafka/build.gradle b/java-kafka/build.gradle new file mode 100644 index 00000000..fe452827 --- /dev/null +++ b/java-kafka/build.gradle @@ -0,0 +1,3 @@ +dependencies { + implementation libs['kafka-clients'] +} \ No newline at end of file diff --git a/java-kafka/src/main/java/com/github/kuangcp/hi/Constants.java b/java-kafka/src/main/java/com/github/kuangcp/hi/Constants.java new file mode 100644 index 00000000..22c556c8 --- /dev/null +++ b/java-kafka/src/main/java/com/github/kuangcp/hi/Constants.java @@ -0,0 +1,13 @@ +package com.github.kuangcp.hi; + +/** + * @author kuangcp + * + * Date: 2019-05-20 00:16 + */ +public interface Constants { + + String KAFKA_SERVER = "127.0.0.1:9092"; + + String TOPIC = "Hi"; +} diff --git a/java-kafka/src/main/java/com/github/kuangcp/hi/ConsumerDemo.java b/java-kafka/src/main/java/com/github/kuangcp/hi/ConsumerDemo.java new file mode 100644 index 00000000..0a76b185 --- /dev/null +++ b/java-kafka/src/main/java/com/github/kuangcp/hi/ConsumerDemo.java @@ -0,0 +1,49 @@ +package com.github.kuangcp.hi; + +import static com.github.kuangcp.hi.Constants.KAFKA_SERVER; +import static com.github.kuangcp.hi.Constants.TOPIC; + +import java.time.Duration; +import java.util.Collections; +import java.util.Properties; +import lombok.extern.slf4j.Slf4j; +import org.apache.kafka.clients.consumer.ConsumerRecord; +import org.apache.kafka.clients.consumer.ConsumerRecords; +import org.apache.kafka.clients.consumer.KafkaConsumer; + +/** + * @author kuangcp + * + * Date: 2019-05-20 00:11 + */ +@Slf4j +public class ConsumerDemo { + + public static void main(String[] args) { + Properties properties = getConf(); + + KafkaConsumer kafkaConsumer = new KafkaConsumer<>(properties); + kafkaConsumer.subscribe(Collections.singletonList(TOPIC)); + while (true) { + ConsumerRecords records = kafkaConsumer.poll(Duration.ofMillis(100)); + for (ConsumerRecord record : records) { + log.info("offset = {}, value = %{}", record.offset(), record.value()); + } + } + + } + + private static Properties getConf() { + Properties properties = new Properties(); + properties.put("bootstrap.servers", KAFKA_SERVER); + properties.put("group.id", "group-1"); + properties.put("enable.auto.commit", "true"); + properties.put("auto.commit.interval.ms", "1000"); + properties.put("auto.offset.reset", "earliest"); + properties.put("session.timeout.ms", "30000"); + properties.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer"); + properties + .put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer"); + return properties; + } +} \ No newline at end of file diff --git a/java-kafka/src/main/java/com/github/kuangcp/hi/ProducerDemo.java b/java-kafka/src/main/java/com/github/kuangcp/hi/ProducerDemo.java new file mode 100644 index 00000000..678403ec --- /dev/null +++ b/java-kafka/src/main/java/com/github/kuangcp/hi/ProducerDemo.java @@ -0,0 +1,56 @@ +package com.github.kuangcp.hi; + +import static com.github.kuangcp.hi.Constants.KAFKA_SERVER; +import static com.github.kuangcp.hi.Constants.TOPIC; + +import com.github.kuangcp.io.ResourceTool; +import java.io.IOException; +import java.util.Properties; +import lombok.extern.slf4j.Slf4j; +import org.apache.kafka.clients.producer.KafkaProducer; +import org.apache.kafka.clients.producer.Producer; +import org.apache.kafka.clients.producer.ProducerRecord; + +/** + * @author kuangcp + * + * Date: 2019-05-20 00:04 + */ +@Slf4j +public class ProducerDemo { + + public static void main(String[] args) { + Properties properties = getConf(); + + Producer producer = null; + try { + producer = new KafkaProducer<>(properties); + for (int i = 0; i < 100; i++) { + String msg = "Message " + i; + producer.send(new ProducerRecord<>(TOPIC, msg)); + log.info("Sent: {}", msg); + } + } catch (Exception e) { + log.error(e.getMessage(), e); + } finally { + try { + ResourceTool.close(producer); + } catch (IOException e) { + log.error(e.getMessage(), e); + } + } + } + + private static Properties getConf() { + Properties properties = new Properties(); + properties.put("bootstrap.servers", KAFKA_SERVER); + properties.put("acks", "all"); + properties.put("retries", 0); + properties.put("batch.size", 16384); + properties.put("linger.ms", 1); + properties.put("buffer.memory", 33554432); + properties.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer"); + properties.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer"); + return properties; + } +} diff --git a/java-question/build.gradle b/java-question/build.gradle index 5022d225..283b8876 100644 --- a/java-question/build.gradle +++ b/java-question/build.gradle @@ -1,4 +1,3 @@ - dependencies { - implementation rootProject.libs['testng'] + implementation libs['testng'] } \ No newline at end of file diff --git a/java-spring/build.gradle b/java-spring/build.gradle index 74fe2f6f..35b0e488 100644 --- a/java-spring/build.gradle +++ b/java-spring/build.gradle @@ -1,56 +1,17 @@ -plugins { - id 'java' -} - -group 'com.github.kuangcp' - -sourceCompatibility = 1.8 -targetCompatibility = 1.8 - -buildDir = 'out/build' - -repositories { - mavenLocal() - - def aliYun = "http://maven.aliyun.com/nexus/content/groups/public/" - def abroad = "http://central.maven.org/maven2/" - maven { - url = aliYun - artifactUrls abroad - } - maven { - url = "https://gitee.com/gin9/MavenRepos/raw/master" - } - jcenter() - mavenCentral() -} - -rootProject.ext { - ver = [ - spring: '5.1.5.RELEASE', - aspectj: '1.9.0' - ] -} - dependencies { - testImplementation 'junit:junit:4.12' - - implementation 'ch.qos.logback:logback-classic:1.2.3' + implementation libs['MySQL'] + implementation libs['cglib'] - implementation 'mysql:mysql-connector-java:8.0.15' + implementation libs['aspectjrt'] + implementation libs['aspectjweaver'] - implementation "cglib:cglib:3.2.9" + implementation libs['spring-core'] + implementation libs['spring-ctx'] + implementation libs['spring-beans'] + implementation libs['spring-aop'] + implementation libs['spring-orm'] - implementation "org.aspectj:aspectjrt:$rootProject.ver.aspectj" - implementation "org.aspectj:aspectjweaver:$rootProject.ver.aspectj" - - implementation "org.springframework:spring-core:$rootProject.ver.spring" - implementation "org.springframework:spring-context:$rootProject.ver.spring" - implementation "org.springframework:spring-beans:$rootProject.ver.spring" - implementation "org.springframework:spring-aop:$rootProject.ver.spring" - - implementation "org.springframework:spring-orm:$rootProject.ver.spring" implementation 'org.hibernate:hibernate-core:5.3.7.Final' - + implementation 'commons-dbcp:commons-dbcp:1.4' } diff --git a/settings.gradle b/settings.gradle index 9157a6a1..701f58a0 100644 --- a/settings.gradle +++ b/settings.gradle @@ -15,4 +15,5 @@ include( , 'java-guava' , 'java-netty' , 'java-spring' + , 'java-kafka' ) From bcda79b47ab27508c2f07b1a68003bb56d4c152d Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Mon, 20 May 2019 00:39:06 +0800 Subject: [PATCH 035/476] *) update readme --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 7cc581a4..49dfaba7 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ | [Java8](/java-8) | [并发](/java-concurrency) | [Netty](/java-netty)| | [集合](/java-collection) | [网络](/java-network) | [Guava](/java-guava)| | [算法](/java-algorithms) | [设计模式](/java-pattern) | [Spring](/java-spring) -| [字节码](/java-class) | [测试](/java-test) | +| [字节码](/java-class) | [测试](/java-test) | [Kafka](/java-kafka)| | [泛型](/java-generic) | | | [IO](/java-io) | | | @@ -30,3 +30,4 @@ > [tutorials](https://github.com/eugenp/tutorials) `baeldung` > [Java 学习笔记](https://github.com/brianway/java-learning) > [demo](https://gitee.com/code4everything/demo) + From 0561e986f74411daa339f582dd14a2cb5466fc05 Mon Sep 17 00:00:00 2001 From: kcp Date: Mon, 20 May 2019 15:42:42 +0800 Subject: [PATCH 036/476] +) add hadoop --- dependency.gradle | 6 ++ .../java/jvm/oom/RunTimeConstantPoolOOM.java | 5 +- .../src/test/java/log/CorrectLogTest.java | 4 +- java-hadoop/build.gradle | 5 ++ .../github/kuangcp/hdfs/hi/GeneralIODemo.java | 89 +++++++++++++++++++ .../kuangcp/hdfs/hi/GeneralIODemoTest.java | 33 +++++++ settings.gradle | 1 + 7 files changed, 140 insertions(+), 3 deletions(-) create mode 100644 java-hadoop/build.gradle create mode 100644 java-hadoop/src/main/java/com/github/kuangcp/hdfs/hi/GeneralIODemo.java create mode 100644 java-hadoop/src/test/java/com/github/kuangcp/hdfs/hi/GeneralIODemoTest.java diff --git a/dependency.gradle b/dependency.gradle index 12e41284..bba99b16 100644 --- a/dependency.gradle +++ b/dependency.gradle @@ -17,6 +17,7 @@ ext { , kafka : '2.2.0' , spring : '5.1.5.RELEASE' , aspectj : '1.9.0' + , hadoop : '2.7.2' ] libs = [ // mine tool @@ -75,5 +76,10 @@ ext { // kafka , "kafka-clients" : "org.apache.kafka:kafka-clients:$ver.kafka" + + // hadoop + , "hadoop-common" : "org.apache.hadoop:hadoop-common:$ver.hadoop" + , "hadoop-client" : "org.apache.hadoop:hadoop-client:$ver.hadoop" + , "hadoop-hdfs" : "org.apache.hadoop:hadoop-hdfs:$ver.hadoop" ] } \ No newline at end of file diff --git a/java-class/src/main/java/jvm/oom/RunTimeConstantPoolOOM.java b/java-class/src/main/java/jvm/oom/RunTimeConstantPoolOOM.java index 24cfe01c..5a4344ba 100644 --- a/java-class/src/main/java/jvm/oom/RunTimeConstantPoolOOM.java +++ b/java-class/src/main/java/jvm/oom/RunTimeConstantPoolOOM.java @@ -24,12 +24,15 @@ public class RunTimeConstantPoolOOM { public static void main(String[] args) throws InterruptedException { int i = 0; + // 先生成一个长字符串 Optional result = IntStream.rangeClosed(1, 1000).mapToObj(String::valueOf) .reduce(String::concat); assert result.isPresent(); while (true) { - data.add((result.get() + i++).intern()); + // 只要出现了显式的 + 字符串拼接, 就会创建新的字符串, intern() 方法调用与否结果都是一样的 +// data.add((result.get() + i++).intern()); + data.add((result.get() + i++)); // 如果不加, 瞬间 Old Gen 爆满然后 OOM : heap space // 如果加上, 就会发现 Eden 一直涨, 然后被回收掉, 并且部分转移到了 Old diff --git a/java-class/src/test/java/log/CorrectLogTest.java b/java-class/src/test/java/log/CorrectLogTest.java index dff264f3..966a7237 100644 --- a/java-class/src/test/java/log/CorrectLogTest.java +++ b/java-class/src/test/java/log/CorrectLogTest.java @@ -26,8 +26,8 @@ public void testCorrectLog() { System.out.println(num); } catch (Exception e) { // compile error -// log.error(e); //A -// log.error(e, e); //B + // log.error(e); //A + // log.error(e, e); //B // Exception ClassName and message log.debug("C"); diff --git a/java-hadoop/build.gradle b/java-hadoop/build.gradle new file mode 100644 index 00000000..c6aed60e --- /dev/null +++ b/java-hadoop/build.gradle @@ -0,0 +1,5 @@ +dependencies { + implementation libs["hadoop-common"] + implementation libs["hadoop-client"] + implementation libs["hadoop-hdfs"] +} \ No newline at end of file diff --git a/java-hadoop/src/main/java/com/github/kuangcp/hdfs/hi/GeneralIODemo.java b/java-hadoop/src/main/java/com/github/kuangcp/hdfs/hi/GeneralIODemo.java new file mode 100644 index 00000000..35dbbbbe --- /dev/null +++ b/java-hadoop/src/main/java/com/github/kuangcp/hdfs/hi/GeneralIODemo.java @@ -0,0 +1,89 @@ +package com.github.kuangcp.hdfs.hi; + +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.net.URI; +import java.nio.charset.StandardCharsets; +import java.util.concurrent.TimeUnit; +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.fs.FSDataOutputStream; +import org.apache.hadoop.fs.FileStatus; +import org.apache.hadoop.fs.FileSystem; +import org.apache.hadoop.fs.Path; + +/** + * @author https://github.com/kuangcp + * @date 2019-05-20 11:15 + */ +public class GeneralIODemo { + + private static Configuration config; + + static { + System.setProperty("hadoop.home.dir", "/"); + config = new Configuration(); +// config.set("fs.defaultFS", "hdfs://127.0.0.1:8020"); + config.set("fs.defaultFS", "hdfs://172.16.16.80:8020"); + } + + public static void createDirectory() throws IOException { + FileSystem fileSystem = FileSystem.get(config); + String directoryName = "/hadoop/dfs/name/javadeveloperzone"; + Path path = new Path(directoryName); + fileSystem.mkdirs(path); + } + + public static void createNewHDFSFile(String toCreateFilePath, String content) + throws IOException, InterruptedException { + FileSystem fs = FileSystem.get(config); + + FSDataOutputStream os = fs.create(new Path(toCreateFilePath)); + os.write(content.getBytes(StandardCharsets.UTF_8)); + os.flush(); + TimeUnit.SECONDS.sleep(4); + os.close(); + fs.close(); + } + + public static void listFiles(String DirFile) throws IOException { + FileSystem fs = FileSystem.get(URI.create(DirFile), config); + Path path = new Path(DirFile); + FileStatus[] status = fs.listStatus(path); + + //方法1 + for (FileStatus f : status) { + System.out.println(f.getPath().toString()); + } + + //方法2 +// Path[] listedPaths = FileUtil.stat2Paths(status); +// for (Path p : listedPaths) { +// System.out.println(p.toString()); +// } + } + + public static boolean deleteHDFSFile(String dst) throws IOException { + FileSystem fs = FileSystem.get(config); + Path path = new Path(dst); + boolean isDeleted = fs.deleteOnExit(path); + fs.close(); + return isDeleted; + } + + public static void writeFileToHDFS() throws IOException { + Configuration configuration = new Configuration(); + configuration.set("fs.defaultFS", "hdfs://localhost:9000"); + FileSystem fileSystem = FileSystem.get(configuration); + //Create a path + String fileName = "read_write_hdfs_example.txt"; + Path hdfsWritePath = new Path("/user/javadeveloperzone/javareadwriteexample/" + fileName); + FSDataOutputStream fsDataOutputStream = fileSystem.create(hdfsWritePath, true); + BufferedWriter bufferedWriter = new BufferedWriter( + new OutputStreamWriter(fsDataOutputStream, StandardCharsets.UTF_8)); + bufferedWriter.write("Java API to write data in HDFS"); + bufferedWriter.newLine(); + bufferedWriter.close(); + fileSystem.close(); + } +} diff --git a/java-hadoop/src/test/java/com/github/kuangcp/hdfs/hi/GeneralIODemoTest.java b/java-hadoop/src/test/java/com/github/kuangcp/hdfs/hi/GeneralIODemoTest.java new file mode 100644 index 00000000..b9a06bd0 --- /dev/null +++ b/java-hadoop/src/test/java/com/github/kuangcp/hdfs/hi/GeneralIODemoTest.java @@ -0,0 +1,33 @@ +package com.github.kuangcp.hdfs.hi; + +import java.io.IOException; +import org.junit.Test; + +/** + * @author https://github.com/kuangcp + * @date 2019-05-20 11:28 + */ +public class GeneralIODemoTest { + + @Test + public void testCreateDirectory() throws Exception { + GeneralIODemo.createDirectory(); + } + + @Test + public void testCreateFile() throws IOException, InterruptedException { + GeneralIODemo.createNewHDFSFile("/input/b.md", "fdasfasdfasd"); + } + + @Test + public void testList() throws IOException { +// GeneralIODemo.listFiles("hdfs://172.16.16.80:8020/input"); + GeneralIODemo.listFiles("hdfs://localhost:8020/input"); + } + + @Test + public void testDelete() throws IOException { + boolean result = GeneralIODemo.deleteHDFSFile("hdfs://172.16.16.80:8020/input/b.md"); + System.out.println(result); + } +} \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 701f58a0..b1e51939 100644 --- a/settings.gradle +++ b/settings.gradle @@ -16,4 +16,5 @@ include( , 'java-netty' , 'java-spring' , 'java-kafka' + , 'java-hadoop' ) From 2813c170895f93967af727e49e522659173816f5 Mon Sep 17 00:00:00 2001 From: kcp Date: Tue, 21 May 2019 16:24:17 +0800 Subject: [PATCH 037/476] +) test send object --- java-kafka/build.gradle | 2 + .../java/com/github/kuangcp/hi/Constants.java | 4 +- .../com/github/kuangcp/hi/ConsumerDemo.java | 30 +++++-- .../com/github/kuangcp/hi/ProducerDemo.java | 67 ++++++++++++++- .../hi/dto/ProductStatisticJobCommand.java | 41 ++++++++++ .../kuangcp/hi/dto/ProductStatisticSpan.java | 62 ++++++++++++++ .../github/kuangcp/hi/dto/StartCommand.java | 20 +++++ .../github/kuangcp/hi/dto/StatisticSpan.java | 25 ++++++ java-kafka/src/main/resources/logback.xml | 82 +++++++++++++++++++ .../github/kuangcp/hi/ConsumerDemoTest.java | 22 +++++ .../github/kuangcp/hi/ProducerDemoTest.java | 25 ++++++ 11 files changed, 368 insertions(+), 12 deletions(-) create mode 100644 java-kafka/src/main/java/com/github/kuangcp/hi/dto/ProductStatisticJobCommand.java create mode 100644 java-kafka/src/main/java/com/github/kuangcp/hi/dto/ProductStatisticSpan.java create mode 100644 java-kafka/src/main/java/com/github/kuangcp/hi/dto/StartCommand.java create mode 100644 java-kafka/src/main/java/com/github/kuangcp/hi/dto/StatisticSpan.java create mode 100644 java-kafka/src/main/resources/logback.xml create mode 100644 java-kafka/src/test/java/com/github/kuangcp/hi/ConsumerDemoTest.java create mode 100644 java-kafka/src/test/java/com/github/kuangcp/hi/ProducerDemoTest.java diff --git a/java-kafka/build.gradle b/java-kafka/build.gradle index fe452827..b431af23 100644 --- a/java-kafka/build.gradle +++ b/java-kafka/build.gradle @@ -1,3 +1,5 @@ dependencies { implementation libs['kafka-clients'] + implementation libs['jackson_core'] + implementation libs['jackson_databind'] } \ No newline at end of file diff --git a/java-kafka/src/main/java/com/github/kuangcp/hi/Constants.java b/java-kafka/src/main/java/com/github/kuangcp/hi/Constants.java index 22c556c8..02eb0026 100644 --- a/java-kafka/src/main/java/com/github/kuangcp/hi/Constants.java +++ b/java-kafka/src/main/java/com/github/kuangcp/hi/Constants.java @@ -9,5 +9,7 @@ public interface Constants { String KAFKA_SERVER = "127.0.0.1:9092"; - String TOPIC = "Hi"; + String HI_TOPIC = "Hi"; + + String START_TOPIC = "start"; } diff --git a/java-kafka/src/main/java/com/github/kuangcp/hi/ConsumerDemo.java b/java-kafka/src/main/java/com/github/kuangcp/hi/ConsumerDemo.java index 0a76b185..fd8ea18e 100644 --- a/java-kafka/src/main/java/com/github/kuangcp/hi/ConsumerDemo.java +++ b/java-kafka/src/main/java/com/github/kuangcp/hi/ConsumerDemo.java @@ -1,8 +1,11 @@ package com.github.kuangcp.hi; +import static com.github.kuangcp.hi.Constants.HI_TOPIC; import static com.github.kuangcp.hi.Constants.KAFKA_SERVER; -import static com.github.kuangcp.hi.Constants.TOPIC; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.kuangcp.hi.dto.StartCommand; +import java.io.IOException; import java.time.Duration; import java.util.Collections; import java.util.Properties; @@ -10,6 +13,7 @@ import org.apache.kafka.clients.consumer.ConsumerRecord; import org.apache.kafka.clients.consumer.ConsumerRecords; import org.apache.kafka.clients.consumer.KafkaConsumer; +import org.apache.kafka.common.serialization.StringDeserializer; /** * @author kuangcp @@ -19,18 +23,31 @@ @Slf4j public class ConsumerDemo { - public static void main(String[] args) { - Properties properties = getConf(); + private static Properties properties = getConf(); + private static ObjectMapper mapper = new ObjectMapper(); + static void receiveHi() { KafkaConsumer kafkaConsumer = new KafkaConsumer<>(properties); - kafkaConsumer.subscribe(Collections.singletonList(TOPIC)); + kafkaConsumer.subscribe(Collections.singletonList(HI_TOPIC)); while (true) { ConsumerRecords records = kafkaConsumer.poll(Duration.ofMillis(100)); for (ConsumerRecord record : records) { log.info("offset = {}, value = %{}", record.offset(), record.value()); } } + } + static void receiveStart() throws IOException { + KafkaConsumer kafkaConsumer = new KafkaConsumer<>(properties); + kafkaConsumer.subscribe(Collections.singletonList("OFC_PRODUCT_STATISTIC_JOB_DISPATCHING")); + while (true) { + ConsumerRecords records = kafkaConsumer.poll(Duration.ofMillis(100)); + for (ConsumerRecord record : records) { + log.info("offset = {}, value = {}", record.offset(), record.value()); + StartCommand command = mapper.readValue(record.value(), StartCommand.class); + System.out.println(command); + } + } } private static Properties getConf() { @@ -41,9 +58,8 @@ private static Properties getConf() { properties.put("auto.commit.interval.ms", "1000"); properties.put("auto.offset.reset", "earliest"); properties.put("session.timeout.ms", "30000"); - properties.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer"); - properties - .put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer"); + properties.put("key.deserializer", StringDeserializer.class.getName()); + properties.put("value.deserializer", StringDeserializer.class.getName()); return properties; } } \ No newline at end of file diff --git a/java-kafka/src/main/java/com/github/kuangcp/hi/ProducerDemo.java b/java-kafka/src/main/java/com/github/kuangcp/hi/ProducerDemo.java index 678403ec..857adf01 100644 --- a/java-kafka/src/main/java/com/github/kuangcp/hi/ProducerDemo.java +++ b/java-kafka/src/main/java/com/github/kuangcp/hi/ProducerDemo.java @@ -1,10 +1,17 @@ package com.github.kuangcp.hi; +import static com.github.kuangcp.hi.Constants.HI_TOPIC; import static com.github.kuangcp.hi.Constants.KAFKA_SERVER; -import static com.github.kuangcp.hi.Constants.TOPIC; +import static com.github.kuangcp.hi.Constants.START_TOPIC; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.kuangcp.hi.dto.ProductStatisticJobCommand; +import com.github.kuangcp.hi.dto.ProductStatisticSpan; +import com.github.kuangcp.hi.dto.StartCommand; import com.github.kuangcp.io.ResourceTool; import java.io.IOException; +import java.util.Date; +import java.util.HashSet; import java.util.Properties; import lombok.extern.slf4j.Slf4j; import org.apache.kafka.clients.producer.KafkaProducer; @@ -19,15 +26,16 @@ @Slf4j public class ProducerDemo { - public static void main(String[] args) { - Properties properties = getConf(); + private static Properties properties = getConf(); + private static ObjectMapper mapper = new ObjectMapper(); + static void sendHi() { Producer producer = null; try { producer = new KafkaProducer<>(properties); for (int i = 0; i < 100; i++) { String msg = "Message " + i; - producer.send(new ProducerRecord<>(TOPIC, msg)); + producer.send(new ProducerRecord<>(HI_TOPIC, msg)); log.info("Sent: {}", msg); } } catch (Exception e) { @@ -41,6 +49,56 @@ public static void main(String[] args) { } } + static void sendStart() { + Producer producer = null; + try { + producer = new KafkaProducer<>(properties); + for (int i = 0; i < 20; i++) { + StartCommand msg = StartCommand.builder().place("There").scale(i).startTime(new Date()) + .build(); + producer.send(new ProducerRecord<>(START_TOPIC, mapper.writeValueAsString(msg))); + log.info("Sent: {}", msg); + } + } catch (Exception e) { + log.error(e.getMessage(), e); + } finally { + try { + ResourceTool.close(producer); + } catch (IOException e) { + log.error(e.getMessage(), e); + } + } + } + + static void sendCommand() { + Producer producer = null; + HashSet spans = new HashSet<>(); + spans.add(ProductStatisticSpan.MONTH); + spans.add(ProductStatisticSpan.YEAR); + + try { + producer = new KafkaProducer<>(properties); + for (int i = 0; i < 20; i++) { + ProductStatisticJobCommand msg = ProductStatisticJobCommand.builder() + .startTime(new Date()).endTime(new Date()).id("32131" + i).productStatisticSpan(spans) + .build(); + ProducerRecord record = new ProducerRecord<>( + "OFC_PRODUCT_STATISTIC_JOB_DISPATCHING", mapper.writeValueAsString(msg)); + producer.send(record); + log.info("Sent: {}\n", msg); + } + } catch (Exception e) { + log.error(e.getMessage(), e); + } finally { + try { + ResourceTool.close(producer); + } catch (IOException e) { + log.error(e.getMessage(), e); + } + } + } + + private static Properties getConf() { Properties properties = new Properties(); properties.put("bootstrap.servers", KAFKA_SERVER); @@ -49,6 +107,7 @@ private static Properties getConf() { properties.put("batch.size", 16384); properties.put("linger.ms", 1); properties.put("buffer.memory", 33554432); + properties.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer"); properties.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer"); return properties; diff --git a/java-kafka/src/main/java/com/github/kuangcp/hi/dto/ProductStatisticJobCommand.java b/java-kafka/src/main/java/com/github/kuangcp/hi/dto/ProductStatisticJobCommand.java new file mode 100644 index 00000000..ee018139 --- /dev/null +++ b/java-kafka/src/main/java/com/github/kuangcp/hi/dto/ProductStatisticJobCommand.java @@ -0,0 +1,41 @@ +package com.github.kuangcp.hi.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.Date; +import java.util.Set; + +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class ProductStatisticJobCommand implements Serializable { + + /** + * 任务ID + */ + private String id; + + + /** + * 统计维度 + */ + private Set productStatisticSpan; + + + /** + * 统计起始时间 + */ + private Date startTime; + + + /** + * 统计结束时间 + */ + private Date endTime; + +} diff --git a/java-kafka/src/main/java/com/github/kuangcp/hi/dto/ProductStatisticSpan.java b/java-kafka/src/main/java/com/github/kuangcp/hi/dto/ProductStatisticSpan.java new file mode 100644 index 00000000..073a3370 --- /dev/null +++ b/java-kafka/src/main/java/com/github/kuangcp/hi/dto/ProductStatisticSpan.java @@ -0,0 +1,62 @@ +package com.github.kuangcp.hi.dto; + +import lombok.Getter; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +@Getter +public enum ProductStatisticSpan { + + /** + * 按日统计 + */ + DAY("DAY00_", StatisticSpan.SPAN_DAY), + + /** + * 按月统计 + */ + MONTH("MONTH_", StatisticSpan.SPAN_MONTH), + + /** + * 按季度统计 + */ + SEASON("SEASON", StatisticSpan.SPAN_SEASON), + + /** + * 按年统计 + */ + YEAR("YEAR0_", StatisticSpan.SPAN_YEAR); + + private String prefix; + + private Integer span; + + private static final List ALL = Arrays.asList(values()); + + private static final Map MAP = ALL.stream() + .collect(Collectors.toMap(ProductStatisticSpan::getSpan, Function.identity())); + + ProductStatisticSpan(String prefix, Integer span) { + this.prefix = prefix; + this.span = span; + } + + + public static List getAll() { + return ALL; + } + + public static Map getMap() { + return MAP; + } + + public static ProductStatisticSpan getBySpan(Integer span) { + return MAP.getOrDefault(span, ProductStatisticSpan.SEASON); + } + +} + diff --git a/java-kafka/src/main/java/com/github/kuangcp/hi/dto/StartCommand.java b/java-kafka/src/main/java/com/github/kuangcp/hi/dto/StartCommand.java new file mode 100644 index 00000000..a278cc9e --- /dev/null +++ b/java-kafka/src/main/java/com/github/kuangcp/hi/dto/StartCommand.java @@ -0,0 +1,20 @@ +package com.github.kuangcp.hi.dto; + +import java.util.Date; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class StartCommand { + + private Date startTime; + + private String place; + + private int scale; +} diff --git a/java-kafka/src/main/java/com/github/kuangcp/hi/dto/StatisticSpan.java b/java-kafka/src/main/java/com/github/kuangcp/hi/dto/StatisticSpan.java new file mode 100644 index 00000000..b1ba2676 --- /dev/null +++ b/java-kafka/src/main/java/com/github/kuangcp/hi/dto/StatisticSpan.java @@ -0,0 +1,25 @@ +package com.github.kuangcp.hi.dto; + +public interface StatisticSpan { + + /** + * 天 + */ + Integer SPAN_DAY = 2; + + /** + * 月 + */ + Integer SPAN_MONTH = 3; + + /** + * 季度 + */ + Integer SPAN_SEASON = 5; + + /** + * 年 + */ + Integer SPAN_YEAR = 4; + +} diff --git a/java-kafka/src/main/resources/logback.xml b/java-kafka/src/main/resources/logback.xml new file mode 100644 index 00000000..5e375431 --- /dev/null +++ b/java-kafka/src/main/resources/logback.xml @@ -0,0 +1,82 @@ + + + + + + + + %d{HH:mm:ss.SSS} %highlight(%-5level) [%thread] %logger{30}:%yellow(%-3L) %msg%n + + + DEBUG + + + + + + ${log.base}debug.log + true + + ${log.base}debug.%d{yyyy-MM-dd}.log + + + %d{MM-dd HH:mm:ss.SSS} |-%-5level |%thread| %logger{36}:%L - %msg%n + + + DEBUG + + + + + + ${log.base}warn.log + true + + ${log.base}warn.%d{yyyy-MM-dd}.log + + + %d{MM-dd HH:mm:ss.SSS} |-%-5level %logger{36}:%L - %msg%n + + + WARN + + + + + + ${log.base}info.log + true + + ${log.base}info.%d{yyyy-MM-dd}.log + + + %d{MM-dd HH:mm:ss.SSS} |-%-5level %logger{36}:%L - %msg%n + + + INFO + + + + + + ${log.base}error.log + true + + ${log.base}error.%d{yyyy-MM-dd}.log + + + %d{MM-dd HH:mm:ss.SSS} | %thread |-%-5level %logger{36}:%L - %msg%n + + + ERROR + + + + + + + + + + + \ No newline at end of file diff --git a/java-kafka/src/test/java/com/github/kuangcp/hi/ConsumerDemoTest.java b/java-kafka/src/test/java/com/github/kuangcp/hi/ConsumerDemoTest.java new file mode 100644 index 00000000..2a7835cd --- /dev/null +++ b/java-kafka/src/test/java/com/github/kuangcp/hi/ConsumerDemoTest.java @@ -0,0 +1,22 @@ +package com.github.kuangcp.hi; + +import java.io.IOException; +import org.junit.Test; + +/** + * @author https://github.com/kuangcp + * @date 2019-05-21 16:20 + */ +public class ConsumerDemoTest { + + @Test + public void testReceiveHi() { + ConsumerDemo.receiveHi(); + } + + + @Test + public void testReceiveStart() throws IOException { + ConsumerDemo.receiveStart(); + } +} diff --git a/java-kafka/src/test/java/com/github/kuangcp/hi/ProducerDemoTest.java b/java-kafka/src/test/java/com/github/kuangcp/hi/ProducerDemoTest.java new file mode 100644 index 00000000..e2fbf236 --- /dev/null +++ b/java-kafka/src/test/java/com/github/kuangcp/hi/ProducerDemoTest.java @@ -0,0 +1,25 @@ +package com.github.kuangcp.hi; + +import org.junit.Test; + +/** + * @author https://github.com/kuangcp + * @date 2019-05-21 16:19 + */ +public class ProducerDemoTest { + + @Test + public void testSendCommand() { + ProducerDemo.sendCommand(); + } + + @Test + public void testSendStart() { + ProducerDemo.sendStart(); + } + + @Test + public void testSendHi() { + ProducerDemo.sendHi(); + } +} \ No newline at end of file From 1cbe90e80f5fa2f3b1549c21ec053abb73e8a5b1 Mon Sep 17 00:00:00 2001 From: kcp Date: Tue, 21 May 2019 19:47:30 +0800 Subject: [PATCH 038/476] +) add callback --- .../java/com/github/kuangcp/hi/ProducerDemo.java | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/java-kafka/src/main/java/com/github/kuangcp/hi/ProducerDemo.java b/java-kafka/src/main/java/com/github/kuangcp/hi/ProducerDemo.java index 857adf01..a7e487cf 100644 --- a/java-kafka/src/main/java/com/github/kuangcp/hi/ProducerDemo.java +++ b/java-kafka/src/main/java/com/github/kuangcp/hi/ProducerDemo.java @@ -84,8 +84,19 @@ static void sendCommand() { .build(); ProducerRecord record = new ProducerRecord<>( "OFC_PRODUCT_STATISTIC_JOB_DISPATCHING", mapper.writeValueAsString(msg)); - producer.send(record); - log.info("Sent: {}\n", msg); + + long time = System.currentTimeMillis(); + producer.send(record, (metadata, e) -> { + long elapsedTime = System.currentTimeMillis() - time; + if (metadata != null) { + System.out.printf("sent record(key=%s value=%s) " + + "meta(partition=%d, offset=%d) time=%d\n", + record.key(), record.value(), metadata.partition(), + metadata.offset(), elapsedTime); + } else { + log.error(e.getMessage(), e); + } + }); } } catch (Exception e) { log.error(e.getMessage(), e); From b11501b2309583951da96b6231b2ccd1f21a5a49 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Wed, 22 May 2019 22:48:17 +0800 Subject: [PATCH 039/476] *) update readme --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 49dfaba7..f0a116a1 100644 --- a/README.md +++ b/README.md @@ -8,14 +8,14 @@ ************************ -| 基础 | 进阶 | 应用框架 | +| 基础 | 进阶 | 应用 | |:----|:----|:----| | [Java8](/java-8) | [并发](/java-concurrency) | [Netty](/java-netty)| | [集合](/java-collection) | [网络](/java-network) | [Guava](/java-guava)| | [算法](/java-algorithms) | [设计模式](/java-pattern) | [Spring](/java-spring) | [字节码](/java-class) | [测试](/java-test) | [Kafka](/java-kafka)| -| [泛型](/java-generic) | | -| [IO](/java-io) | | | +| [泛型](/java-generic) | | [Hadoop](/java-hadoop) | +| [IO](/java-io) | | | [图形化](/java-gui) | [实际问题](/java-question) @@ -27,7 +27,7 @@ ************************ ## 同类仓库 -> [tutorials](https://github.com/eugenp/tutorials) `baeldung` -> [Java 学习笔记](https://github.com/brianway/java-learning) -> [demo](https://gitee.com/code4everything/demo) +> [tutorials](https://github.com/eugenp/tutorials) `baeldung` +> [Java 学习笔记](https://github.com/brianway/java-learning) +> [demo](https://gitee.com/code4everything/demo) From ced38e9a5173a12bfc4c8332bf3fabee61fb3814 Mon Sep 17 00:00:00 2001 From: kcp Date: Thu, 23 May 2019 10:15:12 +0800 Subject: [PATCH 040/476] +) log level for client --- .../src/main/java/com/github/kuangcp/hi/ConsumerDemo.java | 2 +- java-kafka/src/main/resources/logback.xml | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/java-kafka/src/main/java/com/github/kuangcp/hi/ConsumerDemo.java b/java-kafka/src/main/java/com/github/kuangcp/hi/ConsumerDemo.java index fd8ea18e..641b9c8c 100644 --- a/java-kafka/src/main/java/com/github/kuangcp/hi/ConsumerDemo.java +++ b/java-kafka/src/main/java/com/github/kuangcp/hi/ConsumerDemo.java @@ -32,7 +32,7 @@ static void receiveHi() { while (true) { ConsumerRecords records = kafkaConsumer.poll(Duration.ofMillis(100)); for (ConsumerRecord record : records) { - log.info("offset = {}, value = %{}", record.offset(), record.value()); + log.info("offset = {}, value = {}", record.offset(), record.value()); } } } diff --git a/java-kafka/src/main/resources/logback.xml b/java-kafka/src/main/resources/logback.xml index 5e375431..49ed46df 100644 --- a/java-kafka/src/main/resources/logback.xml +++ b/java-kafka/src/main/resources/logback.xml @@ -72,6 +72,8 @@ + + From 2ccb0072715856bc71d90709041a1f4032f2b0c3 Mon Sep 17 00:00:00 2001 From: kcp Date: Thu, 23 May 2019 18:12:54 +0800 Subject: [PATCH 041/476] *) rename --- java-hadoop/build.gradle | 12 +- .../hdfs/hi/GeneralFileActionDemo.java | 140 ++++++++++++++++++ .../github/kuangcp/hdfs/hi/GeneralIODemo.java | 89 ----------- java-hadoop/src/main/resources/logback.xml | 84 +++++++++++ .../hdfs/hi/GeneralFileActionDemoTest.java | 54 +++++++ .../kuangcp/hdfs/hi/GeneralIODemoTest.java | 33 ----- 6 files changed, 287 insertions(+), 125 deletions(-) create mode 100644 java-hadoop/src/main/java/com/github/kuangcp/hdfs/hi/GeneralFileActionDemo.java delete mode 100644 java-hadoop/src/main/java/com/github/kuangcp/hdfs/hi/GeneralIODemo.java create mode 100644 java-hadoop/src/main/resources/logback.xml create mode 100644 java-hadoop/src/test/java/com/github/kuangcp/hdfs/hi/GeneralFileActionDemoTest.java delete mode 100644 java-hadoop/src/test/java/com/github/kuangcp/hdfs/hi/GeneralIODemoTest.java diff --git a/java-hadoop/build.gradle b/java-hadoop/build.gradle index c6aed60e..955047b9 100644 --- a/java-hadoop/build.gradle +++ b/java-hadoop/build.gradle @@ -1,5 +1,11 @@ dependencies { - implementation libs["hadoop-common"] - implementation libs["hadoop-client"] - implementation libs["hadoop-hdfs"] + implementation (libs["hadoop-common"]) { + exclude group: 'org.slf4j' + } + implementation (libs["hadoop-client"]){ + exclude group: 'org.slf4j' + } + implementation (libs["hadoop-hdfs"]){ + exclude group: 'org.slf4j' + } } \ No newline at end of file diff --git a/java-hadoop/src/main/java/com/github/kuangcp/hdfs/hi/GeneralFileActionDemo.java b/java-hadoop/src/main/java/com/github/kuangcp/hdfs/hi/GeneralFileActionDemo.java new file mode 100644 index 00000000..ca213da5 --- /dev/null +++ b/java-hadoop/src/main/java/com/github/kuangcp/hdfs/hi/GeneralFileActionDemo.java @@ -0,0 +1,140 @@ +package com.github.kuangcp.hdfs.hi; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.net.URI; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; +import lombok.extern.slf4j.Slf4j; +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.fs.FSDataInputStream; +import org.apache.hadoop.fs.FSDataOutputStream; +import org.apache.hadoop.fs.FileStatus; +import org.apache.hadoop.fs.FileSystem; +import org.apache.hadoop.fs.Path; + +/** + * @author https://github.com/kuangcp + * @date 2019-05-20 11:15 + */ +@Slf4j +public class GeneralFileActionDemo { + + private static String HDFS_HOST; + private static Configuration config; + + static { + // "hdfs://127.0.0.1:8020" + HDFS_HOST = "hdfs://172.16.16.80:8020"; + + // warn for what ? +// System.setProperty("hadoop.home.dir", "/"); + + config = new Configuration(); + config.set("fs.defaultFS", HDFS_HOST); + } + + public static boolean createDirectory(String dirPath) throws IOException { + FileSystem fileSystem = FileSystem.get(config); + boolean result = fileSystem.mkdirs(new Path(dirPath)); + log.info("result={}", result); + return result; + } + + public static void createNewFile(String toCreateFilePath, String content) + throws IOException, InterruptedException { + FileSystem fs = FileSystem.get(config); + + FSDataOutputStream os = fs.create(new Path(toCreateFilePath)); + os.write(content.getBytes(StandardCharsets.UTF_8)); + os.flush(); + TimeUnit.SECONDS.sleep(4); + os.close(); + fs.close(); + } + + public static List listFiles(String DirFile) throws IOException { + FileSystem fs = FileSystem.get(URI.create(HDFS_HOST + DirFile), config); + Path path = new Path(DirFile); + FileStatus[] status = fs.listStatus(path); + + return Arrays.stream(status).map(v -> v.getPath().toString()).collect(Collectors.toList()); + //方法1 +// for (FileStatus f : status) { +// log.info(f.getPath().toString()); +// } + + //方法2 +// Path[] listedPaths = FileUtil.stat2Paths(status); +// for (Path p : listedPaths) { +// System.out.println(p.toString()); +// } + } + + public static boolean deleteByPath(String filePath) throws IOException { + return deleteByURL(HDFS_HOST + filePath); + } + + public static boolean deleteByURL(String url) throws IOException { + FileSystem fs = FileSystem.get(config); + Path path = new Path(url); + boolean isDeleted = fs.deleteOnExit(path); + fs.close(); + return isDeleted; + } + + public static void writeFileToHDFS() throws IOException { + Configuration configuration = new Configuration(); + configuration.set("fs.defaultFS", "hdfs://localhost:9000"); + FileSystem fileSystem = FileSystem.get(configuration); + //Create a path + String fileName = "read_write_hdfs_example.txt"; + Path hdfsWritePath = new Path("/user/javadeveloperzone/javareadwriteexample/" + fileName); + FSDataOutputStream fsDataOutputStream = fileSystem.create(hdfsWritePath, true); + BufferedWriter bufferedWriter = new BufferedWriter( + new OutputStreamWriter(fsDataOutputStream, StandardCharsets.UTF_8)); + bufferedWriter.write("Java API to write data in HDFS"); + bufferedWriter.newLine(); + bufferedWriter.close(); + fileSystem.close(); + } + + /** + * read the hdfs file content + * + * notice that the url is the full path name + */ + public static void readHDFSFile(String url) throws Exception { + FileSystem fs = FileSystem.get(config); + Path path = new Path(url); + + // check if the file exists + if (!fs.exists(path)) { + throw new Exception("the file is not found ."); + } + + FSDataInputStream is = fs.open(path); + + // read line by line + BufferedReader bufferedReader = new BufferedReader( + new InputStreamReader(is, StandardCharsets.UTF_8)); + String line; + while ((line = bufferedReader.readLine()) != null) { + System.out.println(line); + } + + // read as byte[] +// FileStatus stat = fs.getFileStatus(path); +// byte[] buffer = new byte[Integer.parseInt(String.valueOf(stat.getLen()))]; +// is.readFully(0, buffer); + + is.close(); + fs.close(); + } +} diff --git a/java-hadoop/src/main/java/com/github/kuangcp/hdfs/hi/GeneralIODemo.java b/java-hadoop/src/main/java/com/github/kuangcp/hdfs/hi/GeneralIODemo.java deleted file mode 100644 index 35dbbbbe..00000000 --- a/java-hadoop/src/main/java/com/github/kuangcp/hdfs/hi/GeneralIODemo.java +++ /dev/null @@ -1,89 +0,0 @@ -package com.github.kuangcp.hdfs.hi; - -import java.io.BufferedWriter; -import java.io.IOException; -import java.io.OutputStreamWriter; -import java.net.URI; -import java.nio.charset.StandardCharsets; -import java.util.concurrent.TimeUnit; -import org.apache.hadoop.conf.Configuration; -import org.apache.hadoop.fs.FSDataOutputStream; -import org.apache.hadoop.fs.FileStatus; -import org.apache.hadoop.fs.FileSystem; -import org.apache.hadoop.fs.Path; - -/** - * @author https://github.com/kuangcp - * @date 2019-05-20 11:15 - */ -public class GeneralIODemo { - - private static Configuration config; - - static { - System.setProperty("hadoop.home.dir", "/"); - config = new Configuration(); -// config.set("fs.defaultFS", "hdfs://127.0.0.1:8020"); - config.set("fs.defaultFS", "hdfs://172.16.16.80:8020"); - } - - public static void createDirectory() throws IOException { - FileSystem fileSystem = FileSystem.get(config); - String directoryName = "/hadoop/dfs/name/javadeveloperzone"; - Path path = new Path(directoryName); - fileSystem.mkdirs(path); - } - - public static void createNewHDFSFile(String toCreateFilePath, String content) - throws IOException, InterruptedException { - FileSystem fs = FileSystem.get(config); - - FSDataOutputStream os = fs.create(new Path(toCreateFilePath)); - os.write(content.getBytes(StandardCharsets.UTF_8)); - os.flush(); - TimeUnit.SECONDS.sleep(4); - os.close(); - fs.close(); - } - - public static void listFiles(String DirFile) throws IOException { - FileSystem fs = FileSystem.get(URI.create(DirFile), config); - Path path = new Path(DirFile); - FileStatus[] status = fs.listStatus(path); - - //方法1 - for (FileStatus f : status) { - System.out.println(f.getPath().toString()); - } - - //方法2 -// Path[] listedPaths = FileUtil.stat2Paths(status); -// for (Path p : listedPaths) { -// System.out.println(p.toString()); -// } - } - - public static boolean deleteHDFSFile(String dst) throws IOException { - FileSystem fs = FileSystem.get(config); - Path path = new Path(dst); - boolean isDeleted = fs.deleteOnExit(path); - fs.close(); - return isDeleted; - } - - public static void writeFileToHDFS() throws IOException { - Configuration configuration = new Configuration(); - configuration.set("fs.defaultFS", "hdfs://localhost:9000"); - FileSystem fileSystem = FileSystem.get(configuration); - //Create a path - String fileName = "read_write_hdfs_example.txt"; - Path hdfsWritePath = new Path("/user/javadeveloperzone/javareadwriteexample/" + fileName); - FSDataOutputStream fsDataOutputStream = fileSystem.create(hdfsWritePath, true); - BufferedWriter bufferedWriter = new BufferedWriter( - new OutputStreamWriter(fsDataOutputStream, StandardCharsets.UTF_8)); - bufferedWriter.write("Java API to write data in HDFS"); - bufferedWriter.newLine(); - bufferedWriter.close(); - fileSystem.close(); - } -} diff --git a/java-hadoop/src/main/resources/logback.xml b/java-hadoop/src/main/resources/logback.xml new file mode 100644 index 00000000..49ed46df --- /dev/null +++ b/java-hadoop/src/main/resources/logback.xml @@ -0,0 +1,84 @@ + + + + + + + + %d{HH:mm:ss.SSS} %highlight(%-5level) [%thread] %logger{30}:%yellow(%-3L) %msg%n + + + DEBUG + + + + + + ${log.base}debug.log + true + + ${log.base}debug.%d{yyyy-MM-dd}.log + + + %d{MM-dd HH:mm:ss.SSS} |-%-5level |%thread| %logger{36}:%L - %msg%n + + + DEBUG + + + + + + ${log.base}warn.log + true + + ${log.base}warn.%d{yyyy-MM-dd}.log + + + %d{MM-dd HH:mm:ss.SSS} |-%-5level %logger{36}:%L - %msg%n + + + WARN + + + + + + ${log.base}info.log + true + + ${log.base}info.%d{yyyy-MM-dd}.log + + + %d{MM-dd HH:mm:ss.SSS} |-%-5level %logger{36}:%L - %msg%n + + + INFO + + + + + + ${log.base}error.log + true + + ${log.base}error.%d{yyyy-MM-dd}.log + + + %d{MM-dd HH:mm:ss.SSS} | %thread |-%-5level %logger{36}:%L - %msg%n + + + ERROR + + + + + + + + + + + + + \ No newline at end of file diff --git a/java-hadoop/src/test/java/com/github/kuangcp/hdfs/hi/GeneralFileActionDemoTest.java b/java-hadoop/src/test/java/com/github/kuangcp/hdfs/hi/GeneralFileActionDemoTest.java new file mode 100644 index 00000000..d058d791 --- /dev/null +++ b/java-hadoop/src/test/java/com/github/kuangcp/hdfs/hi/GeneralFileActionDemoTest.java @@ -0,0 +1,54 @@ +package com.github.kuangcp.hdfs.hi; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.IsEqual.equalTo; + +import java.io.IOException; +import java.util.List; +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; + +/** + * @author https://github.com/kuangcp + * @date 2019-05-20 11:28 + */ +@Slf4j +public class GeneralFileActionDemoTest { + + @Test + public void testCreateDirectory() throws Exception { + boolean result = GeneralFileActionDemo.createDirectory("/flink-batch/test"); + assertThat(result, equalTo(true)); + } + + @Test + public void testCreateFile() throws IOException, InterruptedException { + GeneralFileActionDemo.createNewFile("/input/b.md", "fdasfasdfasd"); + } + + @Test + public void testList() throws IOException { + List files = GeneralFileActionDemo.listFiles("/flink-batch"); + files.forEach(v -> { + log.info("{}", v); +// try { +// GeneralFileActionDemo.deleteByURL(v); +// } catch (IOException e) { +// e.printStackTrace(); +// } + }); + } + + @Test + public void testDelete() throws IOException { + boolean result = GeneralFileActionDemo.deleteByPath("/input/b.md"); + System.out.println(result); + } + + @Test + public void testRead() throws Exception { + // hdfs://172.16.16.80:8020 + String url = "/flink-batch/SEASON2018-10-01_2018-12-31.csv"; + GeneralFileActionDemo.readHDFSFile(url); + } +} \ No newline at end of file diff --git a/java-hadoop/src/test/java/com/github/kuangcp/hdfs/hi/GeneralIODemoTest.java b/java-hadoop/src/test/java/com/github/kuangcp/hdfs/hi/GeneralIODemoTest.java deleted file mode 100644 index b9a06bd0..00000000 --- a/java-hadoop/src/test/java/com/github/kuangcp/hdfs/hi/GeneralIODemoTest.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.github.kuangcp.hdfs.hi; - -import java.io.IOException; -import org.junit.Test; - -/** - * @author https://github.com/kuangcp - * @date 2019-05-20 11:28 - */ -public class GeneralIODemoTest { - - @Test - public void testCreateDirectory() throws Exception { - GeneralIODemo.createDirectory(); - } - - @Test - public void testCreateFile() throws IOException, InterruptedException { - GeneralIODemo.createNewHDFSFile("/input/b.md", "fdasfasdfasd"); - } - - @Test - public void testList() throws IOException { -// GeneralIODemo.listFiles("hdfs://172.16.16.80:8020/input"); - GeneralIODemo.listFiles("hdfs://localhost:8020/input"); - } - - @Test - public void testDelete() throws IOException { - boolean result = GeneralIODemo.deleteHDFSFile("hdfs://172.16.16.80:8020/input/b.md"); - System.out.println(result); - } -} \ No newline at end of file From d189e2c7a3cff9be1e89c52bc05509fc6dac96eb Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Thu, 23 May 2019 23:22:58 +0800 Subject: [PATCH 042/476] +) remove from list --- .../list/ConcurrentModificationTest.java | 36 +++++++++++++++++++ java-io/build.gradle | 2 +- 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/java-collection/src/test/java/com/github/kuangcp/list/ConcurrentModificationTest.java b/java-collection/src/test/java/com/github/kuangcp/list/ConcurrentModificationTest.java index 76e8f335..7f6cd355 100644 --- a/java-collection/src/test/java/com/github/kuangcp/list/ConcurrentModificationTest.java +++ b/java-collection/src/test/java/com/github/kuangcp/list/ConcurrentModificationTest.java @@ -1,6 +1,10 @@ package com.github.kuangcp.list; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; import lombok.extern.slf4j.Slf4j; +import org.junit.Test; /** * TODO list 发生 并发修改异常时的处理方式 @@ -10,4 +14,36 @@ @Slf4j public class ConcurrentModificationTest { + private List list = new ArrayList<>(10); + + @Test + public void testGenerate() { + for (int i = 0; i < list.size(); i++) { + if (list.get(i).equals("del")) { + list.remove(i); + } + } + } + + @Test + public void testForEach() { + for (String x : list) { + if (x.equals("del")) { + list.remove(x); + } + } + } + + @Test + public void testIterator(){ + list.removeIf(x -> x.equals("del")); + + Iterator it = list.iterator(); + while(it.hasNext()){ + String x = it.next(); + if(x.equals("del")){ + it.remove(); + } + } + } } diff --git a/java-io/build.gradle b/java-io/build.gradle index 65fd0f7b..cebfb678 100644 --- a/java-io/build.gradle +++ b/java-io/build.gradle @@ -1,3 +1,3 @@ dependencies { - compile libs['common-lang'] + implementation libs['common-lang'] } From cb64efb16ff6800c5adba827ebb1a54dcdcc86f3 Mon Sep 17 00:00:00 2001 From: kcp Date: Fri, 24 May 2019 19:36:33 +0800 Subject: [PATCH 043/476] -) delete useless class --- .../java/syntax/string/SplitDemoTest.java | 15 +++++++-- .../com/github/kuangcp/list/FilterTest.java | 31 ------------------- ...oArrayTest.java => ListWithArrayTest.java} | 30 +++++++++++++----- .../com/github/kuangcp/set/TreeSetTest.java | 25 +++++++++++++++ .../hdfs/hi/GeneralFileActionDemoTest.java | 10 +++--- 5 files changed, 64 insertions(+), 47 deletions(-) delete mode 100644 java-collection/src/test/java/com/github/kuangcp/list/FilterTest.java rename java-collection/src/test/java/com/github/kuangcp/list/{ListToArrayTest.java => ListWithArrayTest.java} (53%) create mode 100644 java-collection/src/test/java/com/github/kuangcp/set/TreeSetTest.java diff --git a/java-class/src/test/java/syntax/string/SplitDemoTest.java b/java-class/src/test/java/syntax/string/SplitDemoTest.java index 5e513807..d27dec03 100644 --- a/java-class/src/test/java/syntax/string/SplitDemoTest.java +++ b/java-class/src/test/java/syntax/string/SplitDemoTest.java @@ -13,9 +13,8 @@ @Slf4j public class SplitDemoTest { - // 截取第一次 target 出现之前的字符串 - public String removeChar(String origin, String target) { + private String removeChar(String origin, String target) { int indexOf = origin.indexOf(target); if (indexOf == -1) { return origin; @@ -24,7 +23,7 @@ public String removeChar(String origin, String target) { } // 去除最后一个字符 - public String removeLast(String target) { + private String removeLast(String target) { if (Objects.isNull(target) || target.isEmpty()) { return ""; } @@ -48,4 +47,14 @@ public void testRemoveChar() { } getRunTime.endCount("去除最后一个字符"); } + + @Test + public void testEmpty() { + String line = "\"\",QP001,5"; + String[] datas = line.split(","); + System.out.println(datas.length); + for (String data : datas) { + System.out.println(data); + } + } } \ No newline at end of file diff --git a/java-collection/src/test/java/com/github/kuangcp/list/FilterTest.java b/java-collection/src/test/java/com/github/kuangcp/list/FilterTest.java deleted file mode 100644 index 624a1cda..00000000 --- a/java-collection/src/test/java/com/github/kuangcp/list/FilterTest.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.github.kuangcp.list; - -import java.util.LinkedList; -import java.util.List; -import java.util.stream.Collectors; -import lombok.extern.slf4j.Slf4j; -import org.junit.Test; - -/** - * @author kuangcp on 18-9-17-下午2:43 - */ -@Slf4j -public class FilterTest { - - - private List list = new LinkedList<>(); - - // @Before - public void initList() { - for (int i = 0; i < 10; i++) { - list.add("" + i); - } - } - - @Test - public void testFilter() { - List result = list.stream().filter(String::isEmpty).collect(Collectors.toList()); - - log.info("re: result={} {}", result, result.isEmpty()); - } -} diff --git a/java-collection/src/test/java/com/github/kuangcp/list/ListToArrayTest.java b/java-collection/src/test/java/com/github/kuangcp/list/ListWithArrayTest.java similarity index 53% rename from java-collection/src/test/java/com/github/kuangcp/list/ListToArrayTest.java rename to java-collection/src/test/java/com/github/kuangcp/list/ListWithArrayTest.java index 5f111582..2c40dc57 100644 --- a/java-collection/src/test/java/com/github/kuangcp/list/ListToArrayTest.java +++ b/java-collection/src/test/java/com/github/kuangcp/list/ListWithArrayTest.java @@ -2,6 +2,7 @@ import java.util.ArrayList; import java.util.Arrays; +import java.util.List; import lombok.extern.slf4j.Slf4j; import org.junit.Test; @@ -9,33 +10,46 @@ * Created by https://github.com/kuangcp on 17-8-20 下午3:02 */ @Slf4j -public class ListToArrayTest { +public class ListWithArrayTest { /** * 测试toArray方法,得到的结论就是 无参数那个是复制一份返回一个等大的数组回来 * 有数组做参数那个是将传入的数组作为容器,将调用方这个数组里的数据复制一份到这个数组里去, - * 但是若空间小了就采取无参的做法新建一个 + * 若传入的数组空间小了就采取无参的做法新建一个 * 若空间大了 那么会将结果数组最后一个元素之后的元素设置为 null。 */ @Test - public void toArrayMethod() { + public void testToArray() { ArrayList list = new ArrayList<>(); for (int i = 0; i < 10; i++) { list.add(i); } log.debug("{}", list); Object[] copy1 = list.toArray(); - log.debug(Arrays.toString(copy1)); + log.debug("toArray {}", Arrays.toString(copy1)); Integer[] target = new Integer[10]; - log.debug(Arrays.toString(target)); Object[] copy2 = list.toArray(target); - log.debug(Arrays.toString(copy2)); + log.debug("toArray {}", Arrays.toString(copy2)); Integer[] target2 = new Integer[5]; - log.debug(Arrays.toString(target2)); Object[] copy3 = list.toArray(target2); - log.debug(Arrays.toString(copy3)); + log.debug("small than toArray \n{}", Arrays.toString(copy3)); + + Integer[] target3 = new Integer[14]; + Object[] copy4 = list.toArray(target3); + log.debug("bigger than toArray \n{}", Arrays.toString(copy4)); + } + + @Test + public void testToList() { + // T 指代了 int[], 原始类型数组就是一个对象 + int[] data = {}; + List datas = Arrays.asList(data); + + // 因为 asList方法接收变长参数, 所以 T 指代了 Integer + Integer[] temp = new Integer[1]; + List temps = Arrays.asList(temp); } } diff --git a/java-collection/src/test/java/com/github/kuangcp/set/TreeSetTest.java b/java-collection/src/test/java/com/github/kuangcp/set/TreeSetTest.java new file mode 100644 index 00000000..1cf2054d --- /dev/null +++ b/java-collection/src/test/java/com/github/kuangcp/set/TreeSetTest.java @@ -0,0 +1,25 @@ +package com.github.kuangcp.set; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.IsEqual.equalTo; + +import java.util.Arrays; +import java.util.TreeSet; +import org.junit.Test; +/** + * @author https://github.com/kuangcp + * @date 2019-05-24 09:22 + */ +public class TreeSetTest { + + @Test + public void testSort() { + // 获取第二大元素 + Integer[] nums = {1, 2, 432, 4523, 5433452, 34523452, 4573, 33421, 3456, 8567, 342, 76, 1234}; + TreeSet treeSet = new TreeSet<>(Arrays.asList(nums)); + Integer result = treeSet.lower(treeSet.last()); + System.out.println(treeSet); + + assertThat(result, equalTo(5433452)); + } +} diff --git a/java-hadoop/src/test/java/com/github/kuangcp/hdfs/hi/GeneralFileActionDemoTest.java b/java-hadoop/src/test/java/com/github/kuangcp/hdfs/hi/GeneralFileActionDemoTest.java index d058d791..50a92c6d 100644 --- a/java-hadoop/src/test/java/com/github/kuangcp/hdfs/hi/GeneralFileActionDemoTest.java +++ b/java-hadoop/src/test/java/com/github/kuangcp/hdfs/hi/GeneralFileActionDemoTest.java @@ -31,11 +31,11 @@ public void testList() throws IOException { List files = GeneralFileActionDemo.listFiles("/flink-batch"); files.forEach(v -> { log.info("{}", v); -// try { -// GeneralFileActionDemo.deleteByURL(v); -// } catch (IOException e) { -// e.printStackTrace(); -// } + try { + GeneralFileActionDemo.deleteByURL(v); + } catch (IOException e) { + e.printStackTrace(); + } }); } From c613ad52467d98b6493c673e5c4fd3024f5427fe Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Sat, 25 May 2019 20:53:33 +0800 Subject: [PATCH 044/476] +) learn the reason of ConcurrentModifyException --- .../list/ConcurrentModificationTest.java | 87 +++++++++++++++++-- .../kuangcp/list/ListWithArrayTest.java | 23 +++-- ...{ArrayListTest.java => OperationTest.java} | 4 +- 3 files changed, 97 insertions(+), 17 deletions(-) rename java-collection/src/test/java/com/github/kuangcp/list/{ArrayListTest.java => OperationTest.java} (97%) diff --git a/java-collection/src/test/java/com/github/kuangcp/list/ConcurrentModificationTest.java b/java-collection/src/test/java/com/github/kuangcp/list/ConcurrentModificationTest.java index 7f6cd355..86cf28cf 100644 --- a/java-collection/src/test/java/com/github/kuangcp/list/ConcurrentModificationTest.java +++ b/java-collection/src/test/java/com/github/kuangcp/list/ConcurrentModificationTest.java @@ -1,49 +1,118 @@ package com.github.kuangcp.list; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.IsEqual.equalTo; + import java.util.ArrayList; +import java.util.ConcurrentModificationException; import java.util.Iterator; import java.util.List; import lombok.extern.slf4j.Slf4j; +import org.junit.After; +import org.junit.Before; import org.junit.Test; /** - * TODO list 发生 并发修改异常时的处理方式 + * ConcurrentModificationException 的产生是因为 failFast 策略 + * List 发生修改后就会修改 List 的 modCount + * + * 当 List 通过迭代器进行迭代时, 每次迭代会将迭代器中的 expectedModCount 和 List 的 modCount 进行比较 如果不一致就抛出异常 快速失败 + * + * 所以通过下标进行 remove 不会有异常 * * @author kuangcp on 19-1-16-上午9:07 */ @Slf4j public class ConcurrentModificationTest { - private List list = new ArrayList<>(10); + // 经测试 ArrayList LinkedList Vector 行为均一致 + private List list = new ArrayList<>(); + // 虽然不会抛出异常, 但是会有bug, 因为list的长度随着remove在变小, + // 但是 遍历用的index没有随之变化, 就会出现元素被跳过没有被索引到的情况 @Test - public void testGenerate() { + public void testNormal() { for (int i = 0; i < list.size(); i++) { if (list.get(i).equals("del")) { list.remove(i); } } + + assertThat(list.size(), equalTo(6)); } + // 这样写就没有任何错误了, 但是显然的这代码性能低下, 可读性差 @Test + public void testNormalCorrect() { + while (true) { + int i; + for (i = 0; i < list.size(); i++) { + if (list.get(i).equals("del")) { + list.remove(i); + break; + } + } + if (i == list.size()) { + break; + } + } + + assertThat(list.size(), equalTo(4)); + } + + // 反编译后的方法, forEach 实际上是语法糖 + // Iterator var1 = this.list.iterator(); + // while(var1.hasNext()) { + // String x = (String)var1.next(); + // if (x.equals("del")) { + // this.list.remove(x); + // } + // } + @Test(expected = ConcurrentModificationException.class) public void testForEach() { for (String x : list) { if (x.equals("del")) { + // 关键在于这里, 这个remove是遍历list, 找到一致的再复制数组将对应的对象移除 + // 其中调用了 checkForComodification 方法, 由于 remove的调用增加了 modCount 所以就抛出异常了 list.remove(x); + // 如果这里加上 break语句 就没有异常, 这是因为修改了 modCount 但是不会执行到 next 方法 +// break; } } } - - @Test - public void testIterator(){ - list.removeIf(x -> x.equals("del")); + @Test + public void testIterator() { Iterator it = list.iterator(); - while(it.hasNext()){ + while (it.hasNext()) { String x = it.next(); - if(x.equals("del")){ + if (x.equals("del")) { + // remove中有 expectedModCount = modCount; 避免了 ConcurrentModificationException it.remove(); } } + assertThat(list.size(), equalTo(4)); + + // 以上在Java8中可以简写为 + // list.removeIf(x -> x.equals("del")); + } + + @Before + public void before() { + list.add("del"); + for (int i = 0; i < 3; i++) { + list.add(i + ""); + list.add("del"); + } + for (int i = 0; i < 3; i++) { + list.add("del"); + } + list.add("test"); + list.add("del"); + } + + @After + public void after() { + list.forEach(System.out::println); } } diff --git a/java-collection/src/test/java/com/github/kuangcp/list/ListWithArrayTest.java b/java-collection/src/test/java/com/github/kuangcp/list/ListWithArrayTest.java index 2c40dc57..a046521f 100644 --- a/java-collection/src/test/java/com/github/kuangcp/list/ListWithArrayTest.java +++ b/java-collection/src/test/java/com/github/kuangcp/list/ListWithArrayTest.java @@ -1,5 +1,8 @@ package com.github.kuangcp.list; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.IsEqual.equalTo; + import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -7,14 +10,15 @@ import org.junit.Test; /** - * Created by https://github.com/kuangcp on 17-8-20 下午3:02 + * List Array 互相转换的一些注意点 */ @Slf4j public class ListWithArrayTest { /** - * 测试toArray方法,得到的结论就是 无参数那个是复制一份返回一个等大的数组回来 - * 有数组做参数那个是将传入的数组作为容器,将调用方这个数组里的数据复制一份到这个数组里去, + * 测试toArray方法,得到的结论就是 无参的 toArray 是复制一份返回一个等大的数组回来 + * + * toArray(T[]) 是将传入的数组作为容器,将调用方这个数组里的数据复制一份到这个数组里去 * 若传入的数组空间小了就采取无参的做法新建一个 * 若空间大了 那么会将结果数组最后一个元素之后的元素设置为 null。 */ @@ -41,15 +45,22 @@ public void testToArray() { log.debug("bigger than toArray \n{}", Arrays.toString(copy4)); } + /** + * asList 方法的参数是变长参数,也就是 T[] + */ @Test public void testToList() { - // T 指代了 int[], 原始类型数组就是一个对象 + // 由于基本类型不能用于泛型, 所以不能按设想的那样将data转为list, T 指代了 int[] (原始类型数组是一个对象) + // 不符合预期 int[] data = {}; - List datas = Arrays.asList(data); + List list = Arrays.asList(data); + assertThat(list.get(0) instanceof int[], equalTo(true)); - // 因为 asList方法接收变长参数, 所以 T 指代了 Integer + // T 指代了 Integer 符合预期 Integer[] temp = new Integer[1]; + temp[0] = 3; List temps = Arrays.asList(temp); + assertThat(temps.get(0) instanceof Integer,equalTo(true)); } } diff --git a/java-collection/src/test/java/com/github/kuangcp/list/ArrayListTest.java b/java-collection/src/test/java/com/github/kuangcp/list/OperationTest.java similarity index 97% rename from java-collection/src/test/java/com/github/kuangcp/list/ArrayListTest.java rename to java-collection/src/test/java/com/github/kuangcp/list/OperationTest.java index 9df99d5b..8039ddbb 100644 --- a/java-collection/src/test/java/com/github/kuangcp/list/ArrayListTest.java +++ b/java-collection/src/test/java/com/github/kuangcp/list/OperationTest.java @@ -12,13 +12,13 @@ import org.junit.Test; /** - * 交 差 并 补 + * 交 差 并 补 集合运算 * * @author kuangcp on 18-7-25-上午10:42 */ @Ignore @Slf4j -public class ArrayListTest { +public class OperationTest { private List one = new ArrayList<>(); private List two = new ArrayList<>(); From 85b8aa91d21ad6ef90d00b574f634a280fabb598 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Sun, 26 May 2019 12:16:52 +0800 Subject: [PATCH 045/476] +) genric array --- .../kuangcp/constraint/GenericArrayTest.java | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 java-generic/src/test/java/com/github/kuangcp/constraint/GenericArrayTest.java diff --git a/java-generic/src/test/java/com/github/kuangcp/constraint/GenericArrayTest.java b/java-generic/src/test/java/com/github/kuangcp/constraint/GenericArrayTest.java new file mode 100644 index 00000000..9ae5dba9 --- /dev/null +++ b/java-generic/src/test/java/com/github/kuangcp/constraint/GenericArrayTest.java @@ -0,0 +1,53 @@ +package com.github.kuangcp.constraint; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.junit.Test; + +/** + * 不能实例化 参数化类型的数组, 声明是可以的 正确的解决方式应该是 参数化集合 + * 不能实例化 泛型数组 + * @author https://github.com/kuangcp + * 2019-05-25 21:02 + */ +public class GenericArrayTest { + + // 参数化类型 + @Test(expected = ClassCastException.class) + public void testParametricType(){ + // 不能通过编译 +// List[] list = new ArrayList<>[3]; + // 经过类型擦除后, 就是 List[] 可以转换为 Object[] 就可以放入任意类型的对象, 但是会抛出 ArrayStoreException + + // 声明为通配类型, 然后类型转换 可以绕过编译检查 + List[] list = (List[]) new ArrayList[3]; + list[0] = new ArrayList(); + List strings = list[0]; + strings.add(12); + + // 因为前面绕过了泛型约束, 这里取值就会将Integer强转为String而抛异常 + System.out.println(list[0].get(0)); + } + + // 泛型数组 + @Test + public void testCreate() { + String[] ones = create("one", "two"); + ones[0] = "1"; + + for (String one : ones) { + System.out.println(one); + } + } + + // 本质上是得到了 Object[] 强转为 T[] + private T[] create(T... element) { + return (T[]) Arrays.asList(element).toArray(); + } + +// private T[] create2(T... element) { +// T[] data = new T[element.length]; +// return Arrays.asList(element).toArray(data); +// } +} From 18197be8a42c99bb078b508058203698e5f8316f Mon Sep 17 00:00:00 2001 From: kcp Date: Mon, 27 May 2019 13:45:12 +0800 Subject: [PATCH 046/476] -) delete useless code --- java-collection/src/test/java/README.md | 2 +- .../kuangcp/map/CurrentModificationTest.java | 17 ++---- .../com/github/kuangcp/map/HashMapTest.java | 59 +++++++++++-------- .../java/com/github/kuangcp/map/README.md | 3 + .../com/github/kuangcp/map/Readme-hashmap.md | 3 - 5 files changed, 42 insertions(+), 42 deletions(-) create mode 100644 java-collection/src/test/java/com/github/kuangcp/map/README.md delete mode 100644 java-collection/src/test/java/com/github/kuangcp/map/Readme-hashmap.md diff --git a/java-collection/src/test/java/README.md b/java-collection/src/test/java/README.md index 4e7d1799..12846c45 100644 --- a/java-collection/src/test/java/README.md +++ b/java-collection/src/test/java/README.md @@ -1,2 +1,2 @@ # 集合 -> [笔记详情](https://github.com/Kuangcp/Note/blob/master/Java/AdvancedLearning/Collection.md) \ No newline at end of file +> [笔记详情](https://github.com/Kuangcp/Note/blob/master/Java/AdvancedLearning/Collection.md) diff --git a/java-collection/src/test/java/com/github/kuangcp/map/CurrentModificationTest.java b/java-collection/src/test/java/com/github/kuangcp/map/CurrentModificationTest.java index 5951624a..200be612 100644 --- a/java-collection/src/test/java/com/github/kuangcp/map/CurrentModificationTest.java +++ b/java-collection/src/test/java/com/github/kuangcp/map/CurrentModificationTest.java @@ -13,15 +13,6 @@ @Slf4j public class CurrentModificationTest { - - @Test - public void testLoop() { - // 如何 判断 5个数中有四个数一样 -// int size = Stream.of(1, 3, 4, 4, 4).collect(Collectors.groupingBy(Integer::intValue), count()); -// System.out.println(size); -// assert size != 2; - } - // 使用HashMap 并发地发生修改(新增,删除)和读操作就会引发 ConcurrentModificationException // 使用 ConcurrentHashMap 就不会 // TODO 为什么 @@ -34,7 +25,7 @@ public void testHashMap() { testReadAndModifyMapByForEach(map); log.info("lambda"); - testReadAndModifyMapByLambda(map); + readAndModifyMapByLambda(map); } @Test @@ -45,7 +36,7 @@ public void testConcurrentHashMap() { testReadAndModifyMapByForEach(map); log.info("lambda"); - testReadAndModifyMapByLambda(map); + readAndModifyMapByLambda(map); } private void testReadAndModifyMapByForEach(Map map) { @@ -58,12 +49,14 @@ private void testReadAndModifyMapByForEach(Map map) { } catch (InterruptedException e) { e.printStackTrace(); } + for (String value : map.values()) { + value.notify(); } } } - private void testReadAndModifyMapByLambda(Map map) { + private void readAndModifyMapByLambda(Map map) { new Thread(() -> addItemToMap(map)).start(); for (int i = 0; i < 100; i++) { diff --git a/java-collection/src/test/java/com/github/kuangcp/map/HashMapTest.java b/java-collection/src/test/java/com/github/kuangcp/map/HashMapTest.java index 98aaed59..d8b689c9 100644 --- a/java-collection/src/test/java/com/github/kuangcp/map/HashMapTest.java +++ b/java-collection/src/test/java/com/github/kuangcp/map/HashMapTest.java @@ -1,10 +1,10 @@ package com.github.kuangcp.map; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.core.IsEqual.equalTo; - import java.util.HashMap; -import java.util.UUID; +import java.util.Map; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.NoArgsConstructor; import org.junit.Test; /** @@ -12,32 +12,39 @@ */ public class HashMapTest { - @Test - public void testPut() { - HashMap map = new HashMap<>(3); // 初始大小4 扩容阈值为 3 大于才扩容 - for (int i = 0; i < 100; i++) { - String key = i + " " + UUID.randomUUID().toString(); - map.put(key, " "); - int i1 = key.hashCode(); - map.size(); + @Builder + @NoArgsConstructor + @AllArgsConstructor + static class Foo { + + private String name; + + @Override + public int hashCode() { + return 1; } - } - static int hash(Object key) { - int h; - return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); + @Override + public String toString() { + return name; + } } + /** + * 由于hashCode都是1, 就形成了 table[1] 这个单链表, 但是不影响正常使用 + */ @Test - public void testR() { - int value = 0b101010; - // 101010 - // 000101 - // 101111 - assertThat(value ^ (value >>> 3), equalTo(0b101111)); - - assertThat( 0b10111010111101110101001100000011 - ^ 0b00000000000000001011101011110111, - equalTo(0b10111010111101111110100111110100)); + public void testSameHashCode() { + Map map = new HashMap<>(); + for (int i = 0; i < 10; i++) { + map.put(new Foo(), 1); + } + + Foo you = Foo.builder().name("you").build(); + map.put(you, 12); + map.remove(new Foo()); + + System.out.println("size=" + map.size()); + map.keySet().forEach(System.out::println); } } diff --git a/java-collection/src/test/java/com/github/kuangcp/map/README.md b/java-collection/src/test/java/com/github/kuangcp/map/README.md new file mode 100644 index 00000000..4dfc1af3 --- /dev/null +++ b/java-collection/src/test/java/com/github/kuangcp/map/README.md @@ -0,0 +1,3 @@ +# Map + +- [ ] WeakHashMap diff --git a/java-collection/src/test/java/com/github/kuangcp/map/Readme-hashmap.md b/java-collection/src/test/java/com/github/kuangcp/map/Readme-hashmap.md deleted file mode 100644 index c4ac7fd5..00000000 --- a/java-collection/src/test/java/com/github/kuangcp/map/Readme-hashmap.md +++ /dev/null @@ -1,3 +0,0 @@ -# HashMap -> source code analysis - From 89cc978155663984e0c513f88e357d1c07972e63 Mon Sep 17 00:00:00 2001 From: kcp Date: Tue, 28 May 2019 19:04:42 +0800 Subject: [PATCH 047/476] *) optimized logic --- .../hdfs/hi/GeneralFileActionDemo.java | 53 ++++++++----------- .../hdfs/hi/GeneralFileActionDemoTest.java | 16 +++--- 2 files changed, 32 insertions(+), 37 deletions(-) diff --git a/java-hadoop/src/main/java/com/github/kuangcp/hdfs/hi/GeneralFileActionDemo.java b/java-hadoop/src/main/java/com/github/kuangcp/hdfs/hi/GeneralFileActionDemo.java index ca213da5..fb2f85b5 100644 --- a/java-hadoop/src/main/java/com/github/kuangcp/hdfs/hi/GeneralFileActionDemo.java +++ b/java-hadoop/src/main/java/com/github/kuangcp/hdfs/hi/GeneralFileActionDemo.java @@ -26,32 +26,18 @@ @Slf4j public class GeneralFileActionDemo { - private static String HDFS_HOST; - private static Configuration config; - - static { - // "hdfs://127.0.0.1:8020" - HDFS_HOST = "hdfs://172.16.16.80:8020"; - - // warn for what ? -// System.setProperty("hadoop.home.dir", "/"); - - config = new Configuration(); - config.set("fs.defaultFS", HDFS_HOST); - } - - public static boolean createDirectory(String dirPath) throws IOException { - FileSystem fileSystem = FileSystem.get(config); - boolean result = fileSystem.mkdirs(new Path(dirPath)); + public static boolean createDirectory(String url) throws IOException { + FileSystem fileSystem = FileSystem.get(getConfig(url)); + boolean result = fileSystem.mkdirs(new Path(url)); log.info("result={}", result); return result; } - public static void createNewFile(String toCreateFilePath, String content) + public static void createNewFile(String url, String content) throws IOException, InterruptedException { - FileSystem fs = FileSystem.get(config); + FileSystem fs = FileSystem.get(getConfig(url)); - FSDataOutputStream os = fs.create(new Path(toCreateFilePath)); + FSDataOutputStream os = fs.create(new Path(URI.create(url).getPath())); os.write(content.getBytes(StandardCharsets.UTF_8)); os.flush(); TimeUnit.SECONDS.sleep(4); @@ -59,9 +45,9 @@ public static void createNewFile(String toCreateFilePath, String content) fs.close(); } - public static List listFiles(String DirFile) throws IOException { - FileSystem fs = FileSystem.get(URI.create(HDFS_HOST + DirFile), config); - Path path = new Path(DirFile); + public static List listFiles(String url) throws IOException { + FileSystem fs = FileSystem.get(URI.create(url), getConfig(url)); + Path path = new Path(url); FileStatus[] status = fs.listStatus(path); return Arrays.stream(status).map(v -> v.getPath().toString()).collect(Collectors.toList()); @@ -77,12 +63,8 @@ public static List listFiles(String DirFile) throws IOException { // } } - public static boolean deleteByPath(String filePath) throws IOException { - return deleteByURL(HDFS_HOST + filePath); - } - public static boolean deleteByURL(String url) throws IOException { - FileSystem fs = FileSystem.get(config); + FileSystem fs = FileSystem.get(getConfig(url)); Path path = new Path(url); boolean isDeleted = fs.deleteOnExit(path); fs.close(); @@ -111,7 +93,7 @@ public static void writeFileToHDFS() throws IOException { * notice that the url is the full path name */ public static void readHDFSFile(String url) throws Exception { - FileSystem fs = FileSystem.get(config); + FileSystem fs = FileSystem.get(getConfig(url)); Path path = new Path(url); // check if the file exists @@ -120,7 +102,6 @@ public static void readHDFSFile(String url) throws Exception { } FSDataInputStream is = fs.open(path); - // read line by line BufferedReader bufferedReader = new BufferedReader( new InputStreamReader(is, StandardCharsets.UTF_8)); @@ -137,4 +118,16 @@ public static void readHDFSFile(String url) throws Exception { is.close(); fs.close(); } + + private static Configuration getConfig(String url) { + Configuration config = new Configuration(); + config.set("fs.defaultFS", getHost(url)); + + return config; + } + + private static String getHost(String url) { + URI uri = URI.create(url); + return uri.getScheme() + "://" + uri.getHost() + ":" + uri.getPort(); + } } diff --git a/java-hadoop/src/test/java/com/github/kuangcp/hdfs/hi/GeneralFileActionDemoTest.java b/java-hadoop/src/test/java/com/github/kuangcp/hdfs/hi/GeneralFileActionDemoTest.java index 50a92c6d..454788ae 100644 --- a/java-hadoop/src/test/java/com/github/kuangcp/hdfs/hi/GeneralFileActionDemoTest.java +++ b/java-hadoop/src/test/java/com/github/kuangcp/hdfs/hi/GeneralFileActionDemoTest.java @@ -15,20 +15,22 @@ @Slf4j public class GeneralFileActionDemoTest { + private static final String HDFS_HOST = "hdfs://172.16.16.80:8020"; + @Test public void testCreateDirectory() throws Exception { - boolean result = GeneralFileActionDemo.createDirectory("/flink-batch/test"); + boolean result = GeneralFileActionDemo.createDirectory(HDFS_HOST + "/flink-batch/test"); assertThat(result, equalTo(true)); } @Test public void testCreateFile() throws IOException, InterruptedException { - GeneralFileActionDemo.createNewFile("/input/b.md", "fdasfasdfasd"); + GeneralFileActionDemo.createNewFile(HDFS_HOST + "/input/b.md", "fdasfasdfasd"); } @Test public void testList() throws IOException { - List files = GeneralFileActionDemo.listFiles("/flink-batch"); + List files = GeneralFileActionDemo.listFiles(HDFS_HOST + "/flink-batch"); files.forEach(v -> { log.info("{}", v); try { @@ -41,14 +43,14 @@ public void testList() throws IOException { @Test public void testDelete() throws IOException { - boolean result = GeneralFileActionDemo.deleteByPath("/input/b.md"); + boolean result = GeneralFileActionDemo + .deleteByURL(HDFS_HOST + "/flink-batch/1559008370602_MONTH_2019-03-01_2019-03-27_SPU.csv"); System.out.println(result); } @Test public void testRead() throws Exception { - // hdfs://172.16.16.80:8020 - String url = "/flink-batch/SEASON2018-10-01_2018-12-31.csv"; - GeneralFileActionDemo.readHDFSFile(url); + String url = "/flink-batch/1559008370602_MONTH_2019-03-01_2019-03-27_SPU.csv"; + GeneralFileActionDemo.readHDFSFile(HDFS_HOST + url); } } \ No newline at end of file From 7a52acd528d29f613ac3cff76e9f9f0e8e1d73d2 Mon Sep 17 00:00:00 2001 From: kcp Date: Thu, 30 May 2019 11:46:01 +0800 Subject: [PATCH 048/476] *) rename all subject --- {java-algorithms => algorithms}/build.gradle | 0 .../github/kuangcp/found/BinarySearch.java | 0 .../kuangcp/organizationstructure/Node.java | 0 .../organizationstructure/NodeAction.java | 0 .../organizationstructure/NodeMgr.java | 0 .../organizationstructure/package-info.java | 0 .../github/kuangcp/recursion/Fibonacci.java | 0 .../java/com/github/kuangcp/sort/Bubble.java | 0 .../java/com/github/kuangcp/sort/Insert.java | 0 .../java/com/github/kuangcp/sort/Quick.java | 0 .../java/com/github/kuangcp/sort/Radix.java | 0 .../java/com/github/kuangcp/sort/Select.java | 0 .../java/com/github/kuangcp/sort/Shell.java | 0 .../github/kuangcp/sort/SortAlgorithm.java | 0 .../com/github/kuangcp/sort/SortHelper.java | 0 .../java/com/github/kuangcp/sort/Tim.java | 0 .../strcuture/stack/MythArrayStack.java | 0 .../strcuture/stack/MythBaseStack.java | 0 .../strcuture/stack/MythLinkedStack.java | 0 .../strcuture/stackapp/AddTwoBigInteger.java | 0 .../strcuture/stackapp/MatchBracket.java | 0 .../strcuture/stackapp/OutOfTheMaze.java | 0 .../strcuture/stackapp/ShowStackPop.java | 0 .../stackapp/StackReplaceRecurrence.java | 0 .../github/kuangcp/strcuture/tree/BiTree.java | 0 .../src/main/resources/logback.xml | 0 .../src/test/java/com/github/kuangcp/a/A.java | 0 .../src/test/java/com/github/kuangcp/a/B.java | 0 .../kuangcp/dynamicprogramming/Readme.md | 0 .../kuangcp/found/BinarySearchTest.java | 0 .../organizationstructure/NodeMgrTest.java | 0 .../kuangcp/recursion/FibonacciTest.java | 0 .../com/github/kuangcp/sort/SortTest.java | 0 .../strcuture/stack/MythArrayStackTest.java | 0 .../strcuture/stack/MythLinkedStackTest.java | 0 .../stackapp/AddTwoBigIntegerTest.java | 0 .../strcuture/stackapp/MatchBracketTest.java | 0 .../strcuture/stackapp/ShowStackPopTest.java | 0 .../strcuture/stackapp/SimulationQueue.java | 0 {java-class => class}/README.md | 0 {java-class => class}/build.gradle | 0 .../main/java/com/github/kuangcp/README.md | 0 .../com/github/kuangcp/ValueCacheDemo.java | 0 .../com/github/kuangcp/annotation/First.java | 0 .../github/kuangcp/authorityscope/README.md | 0 .../kuangcp/authorityscope/package1/A.java | 0 .../kuangcp/authorityscope/package1/B.java | 0 .../kuangcp/authorityscope/package1/C.java | 0 .../kuangcp/authorityscope/package2/D.java | 0 .../kuangcp/authorityscope/package2/E.java | 0 .../instantiation/ComplexConstructor.java | 0 .../InstantiationAndConstructor.java | 0 .../github/kuangcp/instantiation/Readme.md | 0 .../instantiation/StaticFieldInit.java | 0 .../com/github/kuangcp/read/ClassScanner.java | 0 .../kuangcp/reflects/ReflectTargetObject.java | 0 .../com/github/kuangcp/serialize/Person.java | 0 .../serialize/customserialize/Myth.java | 0 .../customserialize/MythSerialize.java | 0 .../json/JsonSmileMigrationService.java | 0 .../serialize/json/speed/FastJsonTool.java | 0 .../serialize/json/speed/GsonTool.java | 0 .../serialize/json/speed/JacksonTool.java | 0 .../serialize/json/speed/JsonTool.java | 0 .../kuangcp/serialize/json/speed/README.md | 0 .../com/github/kuangcp/util/ShowBinary.java | 0 .../main/java/jmh/StringBuilderBenchmark.java | 0 .../main/java/jvm/oom/DirectMemoryOOM.java | 0 .../src/main/java/jvm/oom/HeapOOM.java | 0 .../src/main/java/jvm/oom/JvmStackOOM.java | 0 .../main/java/jvm/oom/JvmStackOverFlow.java | 0 .../src/main/java/jvm/oom/MethodAreaOOM.java | 0 .../src/main/java/jvm/oom/Readme.md | 0 .../java/jvm/oom/RunTimeConstantPoolOOM.java | 0 .../src/main/java/syntax/enums/Color.java | 0 .../main/java/syntax/enums/SingletonDemo.java | 0 .../main/java/syntax/enums/package-info.java | 0 .../src/main/java/syntax/package-info.java | 0 .../src/main/resources/logback.groovy.back | 0 .../src/main/resources/logback.xml | 0 .../github/kuangcp/inherit/SameFieldTest.java | 0 .../instantiation/ComplexConstructorTest.java | 0 .../InstantiationAndConstructorTest.java | 0 .../instantiation/StaticFieldInitTest.java | 0 .../kuangcp/loader/ClassInitialTest.java | 0 .../kuangcp/loader/InstantiationSortTest.java | 0 .../com/github/kuangcp/read/BaseFather.java | 0 .../github/kuangcp/read/ClassScannerTest.java | 0 .../github/kuangcp/read/demo/FirstBase.java | 0 .../github/kuangcp/read/demo/SeconedBase.java | 0 .../kuangcp/read/demo/son/ThirdBase.java | 0 .../reflects/InvokeWithInheritParamTest.java | 0 .../reflects/ReflectPerformanceTest.java | 0 .../reflects/ReflectTargetObjectTest.java | 0 .../kuangcp/serialize/SerializeTest.java | 0 .../customserialize/MythSerializeTest.java | 0 .../kuangcp/serialize/json/GsonTest.java | 0 .../kuangcp/serialize/json/SpeedTest.java | 0 .../java/jmh/StringBuilderBenchmarkTest.java | 0 .../src/test/java/jvm/oom/HeapOOMTest.java | 0 .../src/test/java/log/CorrectLogTest.java | 0 .../src/test/java/math/RandomTest.java | 0 .../src/test/java/syntax/InstanceofTest.java | 0 .../base/method/EqualsAndHashCodeTest.java | 0 .../syntax/base/method/ParamTransferTest.java | 0 .../basetype/BaseTypeDefaultValueTest.java | 0 .../java/syntax/bit/BitOperatorsTest.java | 0 .../syntax/bit/StringPerformanceTest.java | 0 .../test/java/syntax/bytes/OperatorTest.java | 0 .../java/syntax/doubles/DoubleConstTest.java | 0 .../syntax/expression/ShortCircuitTest.java | 0 .../java/syntax/floats/AccuracyLoseTest.java | 0 .../java/syntax/innerclass/ServerConfig.java | 0 .../java/syntax/integer/IntegerCacheTest.java | 0 .../src/test/java/syntax/longs/LongTest.java | 0 .../src/test/java/syntax/package-info.java | 0 .../java/syntax/string/SplitDemoTest.java | 0 .../test/java/syntax/string/StringTest.java | 0 .../syntax/tryblock/FinallyWithReturn.java | 0 .../src/test/java/syntax/tryblock/TWR.java | 0 .../src/test/java/time/InstantTest.java | 0 .../src/test/java/time/LocalDateTest.java | 0 .../src/test/java/time/LocalDateTimeTest.java | 0 .../src/test/java/time/OffsetTest.java | 0 .../src/test/java/time/Readme.md | 0 {java-collection => collection}/build.gradle | 0 .../src/test/java/README.md | 0 .../list/ConcurrentModificationTest.java | 0 .../kuangcp/list/ListWithArrayTest.java | 0 .../github/kuangcp/list/OperationTest.java | 0 .../kuangcp/map/CurrentModificationTest.java | 0 .../com/github/kuangcp/map/HashMapTest.java | 0 .../github/kuangcp/map/IterateMapTest.java | 0 .../java/com/github/kuangcp/map/README.md | 0 .../kuangcp/map/RandomGetValueTest.java | 0 .../com/github/kuangcp/set/HashSetTest.java | 0 .../com/github/kuangcp/set/TreeSetTest.java | 0 {java-concurrency => concurrency}/Readme.md | 0 .../build.gradle | 0 .../main/java/com/github/kuangcp/README.md | 0 .../kuangcp/countlatch/CountLatchDemo.java | 0 .../kuangcp/countlatch/ProcessingThread.java | 0 .../github/kuangcp/forkjoin/Counter.groovy | 0 .../github/kuangcp/forkjoin/Element.groovy | 0 .../kuangcp/forkjoin/ElementSorter.groovy | 0 .../kuangcp/forkjoin/ForkJoinEasyDemo.groovy | 0 .../com/github/kuangcp/forkjoin/Readme.md | 0 .../kuangcp/forkjoin/SynchronizedDemo.groovy | 0 .../github/kuangcp/list/ArrayListDemo.java | 0 .../github/kuangcp/list/CopyOnWriteDemo.java | 0 .../java/com/github/kuangcp/list/Element.java | 0 .../github/kuangcp/list/ElementArrayList.java | 0 .../com/github/kuangcp/list/ElementList.java | 0 .../github/kuangcp/list/ShowCopyOnWrite.java | 0 .../github/kuangcp/lock/dead/DeadLock.java | 0 .../kuangcp/lock/dead/DeadLockDemo.java | 0 .../kuangcp/lock/dead/DeadLockHandler.java | 0 .../lock/dead/DeadLockHandlerDemo.java | 0 .../kuangcp/lock/dead/DeadLockRewrite.java | 0 .../lock/dead/DeadLockRewriteDemo.java | 0 .../com/github/kuangcp/lock/pvp/Player.java | 0 .../com/github/kuangcp/lock/sync/Action.java | 0 .../com/github/kuangcp/lock/sync/Readme.md | 0 .../github/kuangcp/lock/sync/SleepDemo.java | 0 .../com/github/kuangcp/old/BuildFactory.java | 0 .../kuangcp/old/CreateObjByBuilder.groovy | 0 .../java/com/github/kuangcp/old/DeadLock.java | 0 .../com/github/kuangcp/old/DeadLockShow.java | 0 .../java/com/github/kuangcp/old/Food.java | 0 .../com/github/kuangcp/old/MainThread.java | 0 .../com/github/kuangcp/old/ObjBuilder.java | 0 .../kuangcp/old/PrefectSynchronized.java | 0 .../com/github/kuangcp/queue/ShowMicro.groovy | 0 .../queue/use/blocking/Appointment.java | 0 .../kuangcp/queue/use/blocking/Cat.java | 0 .../kuangcp/queue/use/blocking/Dog.java | 0 .../kuangcp/queue/use/blocking/Pet.java | 0 .../queue/use/blocking/Veterinarian.java | 0 .../kuangcp/schedule/CreateModel.groovy | 0 .../github/kuangcp/schedule/ShowSTPE.groovy | 0 .../java/com/github/kuangcp/sync/Cache.groovy | 0 .../com/github/kuangcp/sync/SyncField.groovy | 0 .../com/github/kuangcp/sync/package-info.java | 0 .../kuangcp/volatiles/NeverStopThread.java | 0 .../src/main/java/redis/migration/Main.java | 0 .../src/main/java/redis/migration/Readme.md | 0 .../java/redis/migration/RedisDataType.java | 0 .../redis/migration/RedisPoolProperty.java | 0 .../main/java/redis/migration/RedisPools.java | 0 .../src/main/java/redis/package-info.java | 0 .../main/java/thread/HowToCreateThread.java | 0 .../src/main/java/thread/README.md | 0 .../thread/ShowCreateThreadForSimpleMain.java | 0 .../java/thread/ThreadInterruptedDemo.java | 0 .../src/main/java/thread/ThreadJoinDemo.java | 0 .../java/thread/ThreadStatusTransfer.java | 0 .../src/main/java/thread/UseThreadPool.java | 0 .../src/main/java/thread/order/README.md | 0 .../src/main/java/thread/order/Task.java | 0 .../java/thread/order/TaskWithVolatile.java | 0 .../main/java/thread/pcstatus/Consumer.java | 0 .../src/main/java/thread/pcstatus/Main.java | 0 .../main/java/thread/pcstatus/Producer.java | 0 .../src/main/java/thread/pcstatus/README.md | 0 .../src/main/java/thread/pcstatus/Share.java | 0 .../github/kuangcp/lock/pvp/PlayerTest.java | 0 .../github/kuangcp/lock/sync/ActionTest.java | 0 .../queue/use/blocking/VeterinarianTest.java | 0 .../volatiles/NeverStopThreadTest.java | 0 .../test/java/redis/migration/MainTest.java | 0 .../java/thread/ThreadStatusTransferTest.java | 0 .../java/thread/order/OrderedThreadTest.java | 0 .../java/thread/schdule/TimeTaskTest.java | 0 .../src/test/resources/logback.xml | 0 {java-guava => flink}/build.gradle | 0 {java-generic => generic}/build.gradle | 0 .../java/com/github/kuangcp/ArrayUtils.java | 0 .../main/java/com/github/kuangcp/README.md | 0 .../java/com/github/kuangcp/common/Human.java | 0 .../com/github/kuangcp/common/Junior.java | 0 .../com/github/kuangcp/common/Student.java | 0 .../kuangcp/distribution/SampleAble.java | 0 .../kuangcp/distribution/SampleUtil.java | 0 .../com/github/kuangcp/inherit/Container.java | 0 .../kuangcp/nesting/AbstractLoader.java | 0 .../github/kuangcp/nesting/HumanLoader.java | 0 .../com/github/kuangcp/nesting/HumanVO.java | 0 .../com/github/kuangcp/nesting/JsonVO.java | 0 .../java/com/github/kuangcp/nesting/Readme.md | 0 .../github/kuangcp/simple/DateInterval.java | 0 .../java/com/github/kuangcp/simple/Pair.java | 0 .../kuangcp/simple/SimpleGenericMethod.java | 0 .../src/main/resources/logback.xml | 0 .../com/github/kuangcp/ArrayUtilsTest.java | 0 .../kuangcp/constraint/GenericArrayTest.java | 0 .../github/kuangcp/distribution/DogRef.java | 0 .../kuangcp/distribution/SampleUtilTest.java | 0 .../github/kuangcp/inherit/InheritTest.java | 0 .../kuangcp/nesting/HumanLoaderTest.java | 0 .../com/github/kuangcp/simple/PairTest.java | 0 .../java/com/github/kuangcp/simple/Score.java | 0 .../simple/SimpleGenericMethodTest.java | 0 guava/build.gradle | 3 ++ .../src/test/java/guava/base/AvoidNull.java | 0 .../src/test/java/guava/package-info.java | 0 .../test/java/guava/service/package-info.java | 0 {java-gui => gui}/build.gradle | 0 .../main/java/com/github/kuangcp/README.md | 0 .../Calculate_btnBegin_actionAdapter.java | 0 .../Calculate_btnCancel_actionAdapter.java | 0 .../Calculate_btnEqual_actionAdapter.java | 0 .../Calculate_btnIncrease_actionAdapter.java | 0 .../Calculate_btnMinus_actionAdapter.java | 0 .../Calculate_btnPoint_actionAdapter.java | 0 .../Calculate_btnZero_actionAdapter.java | 0 .../github/kuangcp/caculator/Calculator.java | 0 .../github/kuangcp/jigsaw/ButtonListener.java | 0 .../com/github/kuangcp/jigsaw/ImageBlock.java | 0 .../github/kuangcp/jigsaw/ImageBlockMgr.java | 0 .../com/github/kuangcp/jigsaw/MainFrame.java | 0 .../com/github/kuangcp/jigsaw/MainPanel.java | 0 .../java/com/github/kuangcp/jigsaw/Move.java | 0 .../com/github/kuangcp/notepad/ICommand.java | 0 .../java/com/github/kuangcp/notepad/Note.java | 0 .../src/main/resources/jigsaw/0.jpg | Bin .../src/main/resources/jigsaw/1.jpg | Bin .../src/main/resources/jigsaw/2.jpg | Bin .../src/main/resources/jigsaw/3.jpg | Bin .../src/main/resources/jigsaw/4.jpg | Bin .../src/main/resources/jigsaw/5.jpg | Bin .../src/main/resources/jigsaw/6.jpg | Bin .../src/main/resources/jigsaw/7.jpg | Bin .../src/main/resources/jigsaw/8.jpg | Bin .../src/main/resources/logback.xml | 0 {java-hadoop => hadoop}/build.gradle | 0 .../hdfs/hi/GeneralFileActionDemo.java | 0 .../src/main/resources/logback.xml | 0 .../hdfs/hi/GeneralFileActionDemoTest.java | 0 {java-io => io}/README.md | 0 {java-io => io}/build.gradle | 0 .../com/github/kuangcp/file/CopyFile.java | 0 .../com/github/kuangcp/standard/Readme.md | 0 .../com/github/kuangcp/zip/package-info.java | 0 .../src/test/java/buffer/BufferTest.java | 0 .../com/github/kuangcp/PropertiesTest.java | 0 .../com/github/kuangcp/file/CopyFileTest.java | 0 .../test/resources/properties/main.properties | 0 .../kuangcp/proxy/salary/Privilege.java | 13 ------- .../kuangcp/proxy/salary/SalaryManager.java | 21 ----------- .../kuangcp/proxy/salary/SalaryTest.java | 15 -------- .../kuangcp/proxy/salary/proxy/Privilege.java | 11 ------ .../proxy/salary/proxy/SalaryManagerImpl.java | 8 ---- {java-8 => java8}/Readme.md | 0 {java-8 => java8}/build.gradle | 0 .../function/sort/BaseTypeSortTest.java | 0 .../kuangcp/function/sort/CustomSortTest.java | 0 .../kuangcp/function/sort/DateSortTest.java | 0 .../kuangcp/lambda/filter/FilterTest.java | 0 .../kuangcp/lambda/firstdemo/MainTest.java | 0 .../kuangcp/lambda/firstdemo/PointAction.java | 0 .../lambda/firstdemo/PointArrayList.java | 0 .../lambda/firstdemo/TranslateByOne.java | 0 .../lambda/firstdemo/TranslateByOne8.java | 0 .../github/kuangcp/optional/OptionalTest.java | 0 .../java/com/github/kuangcp/stream/Readme.md | 0 .../stream/debug/UsePluginWithDebugTest.java | 0 .../stream/list/HandleExceptionTest.java | 0 .../github/kuangcp/stream/map/ToMapTest.java | 0 .../com/github/kuangcp/time/CalendarTest.java | 0 .../com/github/kuangcp/time/InstantTest.java | 0 .../kuangcp/time/LocalDateTimeTest.java | 0 {java-kafka => kafka}/build.gradle | 0 .../java/com/github/kuangcp/hi/Constants.java | 0 .../com/github/kuangcp/hi/ConsumerDemo.java | 0 .../com/github/kuangcp/hi/ProducerDemo.java | 0 .../hi/dto/ProductStatisticJobCommand.java | 0 .../kuangcp/hi/dto/ProductStatisticSpan.java | 0 .../github/kuangcp/hi/dto/StartCommand.java | 0 .../github/kuangcp/hi/dto/StatisticSpan.java | 0 .../src/main/resources/logback.xml | 0 .../github/kuangcp/hi/ConsumerDemoTest.java | 0 .../github/kuangcp/hi/ProducerDemoTest.java | 0 {java-netty => netty}/build.gradle | 0 .../src/main/java/netty/package-info.java | 0 .../main/java/netty/timeServer/Command.java | 0 .../src/main/java/netty/timeServer/README.md | 0 .../java/netty/timeServer/TimeClient.java | 0 .../netty/timeServer/TimeClientHandler.java | 0 .../java/netty/timeServer/TimeServer.java | 0 .../netty/timeServer/TimeServerHandler.java | 0 .../src/main/resources/logback.xml | 0 .../java/netty/timeServer/TimeServerTest.java | 0 {java-network => network}/README.md | 0 {java-network => network}/build.gradle | 0 .../java/com/github/kuangcp/bio/Readme.md | 0 .../bio/chattingroom/ClientThread.java | 0 .../github/kuangcp/bio/chattingroom/README.md | 0 .../bio/chattingroom/ServerThread.java | 0 .../bio/chattingroom/SocketClient.java | 0 .../bio/chattingroom/SocketServer.java | 0 .../kuangcp/bio/onechatone/ChatMaps.java | 0 .../kuangcp/bio/onechatone/ChatProtocol.java | 0 .../github/kuangcp/bio/onechatone/Client.java | 0 .../kuangcp/bio/onechatone/ClientThread.java | 0 .../github/kuangcp/bio/onechatone/README.md | 0 .../github/kuangcp/bio/onechatone/Server.java | 0 .../kuangcp/bio/onechatone/ServerThread.java | 0 .../com/github/kuangcp/nio/EchoNIOServer.java | 0 .../com/github/kuangcp/nio/NIOClient.java | 0 .../com/github/kuangcp/nio/NIOServer.java | 0 .../com/github/kuangcp/port/LogicThread.java | 0 .../github/kuangcp/port/LogicThreadTest.java | 0 .../github/kuangcp/port/MulSocketServer.java | 0 .../kuangcp/runable/GreetingClient.java | 0 .../kuangcp/runable/GreetingServer.java | 0 .../kuangcp/selfclose/SocketSelfClose.java | 0 .../src/main/resources/logback.xml | 0 .../github/kuangcp/port/DomainQueryTest.java | 0 .../kuangcp/runable/TuLingRobotTest.java | 0 {java-pattern => pattern}/Readme.md | 0 {java-pattern => pattern}/build.gradle | 0 .../abstractfactory/AbstractFactory.java | 0 .../kuangcp/abstractfactory/ColorFactory.java | 0 .../abstractfactory/FactoryProducer.java | 0 .../kuangcp/abstractfactory/ShapeFactory.java | 0 .../kuangcp/abstractfactory/base/Color.java | 0 .../kuangcp/abstractfactory/base/Shape.java | 0 .../kuangcp/abstractfactory/domain/Green.java | 0 .../abstractfactory/domain/Rectangle.java | 0 .../kuangcp/abstractfactory/domain/Red.java | 0 .../abstractfactory/domain/Square.java | 0 .../github/kuangcp/decorator/Decorator.java | 0 .../kuangcp/decorator/FootDecorator.java | 0 .../kuangcp/decorator/HeadDecorator.java | 0 .../com/github/kuangcp/decorator/Invoice.java | 0 .../com/github/kuangcp/decorator/Readme.md | 0 .../github/kuangcp/singleton/DoubleCheck.java | 0 .../singleton/DoubleCheckWithVolatile.java | 0 .../com/github/kuangcp/singleton/Enum.java | 0 .../com/github/kuangcp/singleton/Readme.md | 0 .../github/kuangcp/singleton/StaticBlock.java | 0 .../github/kuangcp/singleton/StaticFinal.java | 0 .../github/kuangcp/singleton/StaticInit.java | 0 .../kuangcp/singleton/StaticInnerClass.java | 0 .../singleton/StaticLazyWithSyncBlock.java | 0 .../singleton/StaticLazyWithSyncMethod.java | 0 .../src/main/resources/logback.xml | 0 .../abstractfactory/FactoryProducerTest.java | 0 .../kuangcp/decorator/DecoratorTest.java | 0 {java-question => question}/README.md | 0 {java-question => question}/build.gradle | 0 .../kuangcp/caculator/ListDengShi2.java | 0 .../kuangcp/caculator/ListExpressionDemo.java | 0 .../com/github/kuangcp/key/CreateKey.java | 0 .../com/github/kuangcp/key/RSAEncoder.java | 0 .../com/github/kuangcp/simpleMethod/README.md | 0 .../simpleMethod/SimplexMethod/Equality.java | 0 .../SimplexMethod/SimplexMethod.java | 0 .../simpleMethod/SimplexMethod/Table.java | 0 .../SimplexMethodQuarter/Equality.java | 0 .../SimplexMethodQuarter/SimplexMethod.java | 0 .../SimplexMethodQuarter/Table.java | 0 .../simpleMethod/number/ReadProperties.java | 0 .../resources/math/SimplexMethod.properties | 0 .../src/main/resources/math/log4j.xml | 0 .../com/github/kuangcp/key/CreateKeyTest.java | 0 .../java/com/github/kuangcp/math/AbsTest.java | 0 .../SimplexMethodQuarter/MethodTest.java | 0 .../test/java/scattered/SmoothingTheData.java | 0 settings.gradle | 35 +++++++++--------- {java-spring => spring}/.gitignore | 0 {java-spring => spring}/Readme.md | 0 {java-spring => spring}/build.gradle | 0 .../proxy/dao/base/CustomInterceptor.java | 0 .../github/kuangcp/proxy/dao/base/Person.java | 0 .../kuangcp/proxy/dao/base/Transaction.java | 0 .../proxy/dao/cglibproxy/PersonDaoImpl.java | 0 .../dao/cglibproxy/PersonDaoInterceptor.java | 0 .../kuangcp/proxy/dao/jdkproxy/PersonDao.java | 0 .../proxy/dao/jdkproxy/PersonDaoImpl.java | 0 .../dao/jdkproxy/PersonDaoInterceptor.java | 0 .../src/main/resources/logback.xml | 0 .../com/github/kuangcp/aop/ExceptionTest.java | 0 .../github/kuangcp/aop/annotation/Person.java | 0 .../kuangcp/aop/annotation/PersonDao.java | 0 .../kuangcp/aop/annotation/PersonDaoImpl.java | 0 .../kuangcp/aop/annotation/PersonTest.java | 0 .../kuangcp/aop/annotation/Transaction.java | 0 .../aop/annotation/applicationContext.xml | 0 .../aop/applicationContext-exception.xml | 0 .../kuangcp/aop/exception/MyException.java | 0 .../github/kuangcp/aop/exception/Readme.md | 0 .../aop/exception/action/PersonAction.java | 0 .../kuangcp/aop/exception/dao/PersonDao.java | 0 .../aop/exception/dao/PersonDaoImpl.java | 0 .../aop/exception/service/PersonService.java | 0 .../exception/service/PersonServiceImpl.java | 0 .../com/github/kuangcp/aop/xml/Person.java | 0 .../com/github/kuangcp/aop/xml/PersonDao.java | 0 .../github/kuangcp/aop/xml/PersonDaoImpl.java | 0 .../github/kuangcp/aop/xml/PersonTest.java | 0 .../java/com/github/kuangcp/aop/xml/Readme.md | 0 .../github/kuangcp/aop/xml/Transaction.java | 0 .../kuangcp/aop/xml/applicationContext.xml | 0 .../github/kuangcp/aop/xml/salary/Logger.java | 0 .../kuangcp/aop/xml/salary/Privilege.java | 0 .../github/kuangcp/aop/xml/salary/Readmd.md | 0 .../kuangcp/aop/xml/salary/SalaryManager.java | 0 .../kuangcp/aop/xml/salary/SalaryTest.java | 0 .../kuangcp/aop/xml/salary/Security.java | 0 .../aop/xml/salary/applicationContext.xml | 0 .../di/annotation/AnnotationParse.java | 0 .../kuangcp/di/annotation/Description.java | 0 .../github/kuangcp/di/annotation/ITCAST.java | 0 .../github/kuangcp/di/annotation/Name.java | 0 .../kuangcp/di/di/annotation/Person.java | 0 .../kuangcp/di/di/annotation/PersonTest.java | 0 .../kuangcp/di/di/annotation/Student.java | 0 .../di/di/annotation/applicationContext.xml | 0 .../kuangcp/di/di/xml/constructor/Person.java | 0 .../di/di/xml/constructor/PersonTest.java | 0 .../di/di/xml/constructor/Student.java | 0 .../di/xml/constructor/applicationContext.xml | 0 .../github/kuangcp/di/di/xml/set/Person.java | 0 .../kuangcp/di/di/xml/set/PersonTest.java | 0 .../github/kuangcp/di/di/xml/set/Student.java | 0 .../di/di/xml/set/applicationContext.xml | 0 .../com/github/kuangcp/di/scan/Person.java | 0 .../github/kuangcp/di/scan/PersonTest.java | 0 .../com/github/kuangcp/di/scan/Student.java | 0 .../kuangcp/di/scan/applicationContext.xml | 0 .../com/github/kuangcp/document/Document.java | 0 .../kuangcp/document/DocumentManager.java | 0 .../github/kuangcp/document/DocumentTest.java | 0 .../kuangcp/document/ExcelDocument.java | 0 .../github/kuangcp/document/PDFDocument.java | 0 .../github/kuangcp/document/WordDocument.java | 0 .../kuangcp/document/spring/Document.java | 0 .../document/spring/DocumentManager.java | 0 .../kuangcp/document/spring/DocumentTest.java | 0 .../document/spring/ExcelDocument.java | 0 .../kuangcp/document/spring/PDFDocument.java | 0 .../kuangcp/document/spring/WordDocument.java | 0 .../document/spring/applicationContext.xml | 0 .../kuangcp/exception/ExceptionTest.java | 0 .../com/github/kuangcp/exception/Readme.md | 0 .../kuangcp/exception/ServiceInvocation.java | 0 .../kuangcp/exception/ServiceMapping.java | 0 .../exception/service/PersonServiceImpl.java | 0 .../kuangcp/exception/service/Service.java | 0 .../exception/service/StudentServiceImpl.java | 0 .../com/github/kuangcp/extend/Person.java | 0 .../com/github/kuangcp/extend/PersonTest.java | 0 .../com/github/kuangcp/extend/Student.java | 0 .../kuangcp/extend/applicationContext.xml | 0 .../java/com/github/kuangcp/extend/log4j.xml | 0 .../hibernate/annotation/Person.hbm.xml | 0 .../kuangcp/hibernate/annotation/Person.java | 0 .../hibernate/annotation/PersonDao.java | 0 .../hibernate/annotation/PersonDaoImpl.java | 0 .../hibernate/annotation/PersonService.java | 0 .../annotation/PersonServiceImpl.java | 0 .../hibernate/annotation/PersonTest.java | 0 .../annotation/applicationContext.xml | 0 .../kuangcp/hibernate/hibernate.cfg.xml | 0 .../kuangcp/hibernate/xml/Person.hbm.xml | 0 .../github/kuangcp/hibernate/xml/Person.java | 0 .../kuangcp/hibernate/xml/PersonDao.java | 0 .../kuangcp/hibernate/xml/PersonDaoImpl.java | 0 .../kuangcp/hibernate/xml/PersonService.java | 0 .../hibernate/xml/PersonServiceImpl.java | 0 .../kuangcp/hibernate/xml/PersonTest.java | 0 .../github/kuangcp/hibernate/xml/Readme.md | 0 .../hibernate/xml/applicationContext.xml | 0 .../github/kuangcp/ioc/alias/AliasTest.java | 0 .../github/kuangcp/ioc/alias/HelloWorld.java | 0 .../kuangcp/ioc/alias/applicationContext.xml | 0 .../ioc/createobject/CreateObjectTest.java | 0 .../kuangcp/ioc/createobject/HelloWorld.java | 0 .../ioc/createobject/HelloWorldFactory.java | 0 .../ioc/createobject/applicationContext.xml | 0 .../ioc/createobject/when/HelloWorld.java | 0 .../createobject/when/HelloWorldFactory.java | 0 .../kuangcp/ioc/createobject/when/Person.java | 0 .../ioc/createobject/when/WhenTest.java | 0 .../createobject/when/applicationContext.xml | 0 .../kuangcp/ioc/helloworld/HelloWorld.java | 0 .../ioc/helloworld/HelloWorldTest.java | 0 .../ioc/helloworld/applicationContext.xml | 0 .../kuangcp/ioc/initdestroy/HelloWorld.java | 0 .../ioc/initdestroy/InitDestroyTest.java | 0 .../ioc/initdestroy/applicationContext.xml | 0 .../github/kuangcp/ioc/scope/HelloWorld.java | 0 .../github/kuangcp/ioc/scope/ScopeTest.java | 0 .../kuangcp/ioc/scope/applicationContext.xml | 0 .../com/github/kuangcp/jdbc/jdbc.properties | 0 .../kuangcp/jdbc/jdbc/DataSourceTest.java | 0 .../com/github/kuangcp/jdbc/jdbc/Person.java | 0 .../github/kuangcp/jdbc/jdbc/PersonDao.java | 0 .../kuangcp/jdbc/jdbc/PersonDaoImpl.java | 0 .../kuangcp/jdbc/jdbc/PersonDaoImpl2.java | 0 .../kuangcp/jdbc/jdbc/PersonDaoImpl3.java | 0 .../kuangcp/jdbc/jdbc/PersonRowMapper.java | 0 .../github/kuangcp/jdbc/jdbc/PersonTest.java | 0 .../kuangcp/jdbc/jdbc/applicationContext.xml | 0 .../jdbc/jdbc/transaction/PersonDao.java | 0 .../jdbc/jdbc/transaction/PersonDaoImpl.java | 0 .../jdbc/jdbc/transaction/PersonService.java | 0 .../jdbc/transaction/PersonServiceImpl.java | 0 .../jdbc/jdbc/transaction/PersonTest.java | 0 .../transaction/annotation/PersonDao.java | 0 .../transaction/annotation/PersonDaoImpl.java | 0 .../transaction/annotation/PersonService.java | 0 .../annotation/PersonServiceImpl.java | 0 .../transaction/annotation/PersonTest.java | 0 .../annotation/applicationContext.xml | 0 .../jdbc/transaction/applicationContext.xml | 0 .../kuangcp/jdbc/transaction/PersonDao.java | 0 .../jdbc/transaction/PersonDaoImpl.java | 0 .../jdbc/transaction/PersonService.java | 0 .../jdbc/transaction/PersonServiceImpl.java | 0 .../kuangcp/jdbc/transaction/PersonTest.java | 0 .../jdbc/transaction/applicationContext.xml | 0 .../com/github/kuangcp/mvc/PersonAction.java | 0 .../com/github/kuangcp/mvc/PersonDao.java | 0 .../com/github/kuangcp/mvc/PersonDaoImpl.java | 0 .../com/github/kuangcp/mvc/PersonService.java | 0 .../github/kuangcp/mvc/PersonServiceImpl.java | 0 .../com/github/kuangcp/mvc/PersonTest.java | 0 .../kuangcp/mvc/annotation/PersonAction.java | 0 .../kuangcp/mvc/annotation/PersonDao.java | 0 .../kuangcp/mvc/annotation/PersonDaoImpl.java | 0 .../kuangcp/mvc/annotation/PersonService.java | 0 .../mvc/annotation/PersonServiceImpl.java | 0 .../kuangcp/mvc/annotation/PersonTest.java | 0 .../mvc/annotation/applicationContext.xml | 0 .../github/kuangcp/mvc/applicationContext.xml | 0 .../com/github/kuangcp/proxy/dao/Readme.md | 2 +- .../proxy/dao/cglibproxy/CGlibProxyTest.java | 0 .../proxy/dao/jdkproxy/JDKProxyTest.java | 0 .../kuangcp/proxy/dao/jdkproxy/Readme.md | 0 .../github/kuangcp/proxy/salary/Logger.java | 7 ++-- .../kuangcp/proxy/salary/Privilege.java | 14 +++++++ .../kuangcp/proxy/salary/SalaryManager.java | 24 ++++++++++++ .../kuangcp/proxy/salary/SalaryTest.java | 16 ++++++++ .../github/kuangcp/proxy/salary/Security.java | 7 ++-- .../kuangcp/proxy/salary/aop/xml/Person.java | 0 .../proxy/salary/aop/xml/PersonDao.java | 0 .../proxy/salary/aop/xml/PersonDaoImpl.java | 0 .../proxy/salary/aop/xml/PersonTest.java | 0 .../proxy/salary/aop/xml/Transaction.java | 0 .../salary/aop/xml/applicationContext.xml | 0 ...224\250XML\345\256\236\347\216\260AOP.txt" | 0 .../kuangcp/proxy/salary/jdkproxy/Logger.java | 0 .../proxy/salary/jdkproxy/Privilege.java | 0 .../proxy/salary/jdkproxy/ProxyTest.java | 0 .../salary/jdkproxy/SalaryInterceptor.java | 0 .../proxy/salary/jdkproxy/SalaryManager.java | 0 .../salary/jdkproxy/SalaryManagerImpl.java | 0 .../proxy/salary/jdkproxy/Security.java | 0 .../salary/jdkproxy/aspects/Interceptor.java | 0 .../proxy/salary/jdkproxy/aspects/Logger.java | 0 .../salary/jdkproxy/aspects/Privilege.java | 0 .../salary/jdkproxy/aspects/ProxyTest.java | 0 .../jdkproxy/aspects/SalaryInterceptor.java | 0 .../jdkproxy/aspects/SalaryManager.java | 0 .../jdkproxy/aspects/SalaryManagerImpl.java | 0 .../salary/jdkproxy/aspects/Security.java | 0 .../kuangcp/proxy/salary/proxy/Logger.java | 7 ++-- .../kuangcp/proxy/salary/proxy/Privilege.java | 14 +++++++ .../kuangcp/proxy/salary/proxy/ProxyTest.java | 0 .../kuangcp/proxy/salary/proxy/Readme.md | 0 .../proxy/salary/proxy/SalaryManager.java | 3 +- .../proxy/salary/proxy/SalaryManagerImpl.java | 9 +++++ .../salary/proxy/SalaryManagerProxy.java | 0 .../kuangcp/proxy/salary/proxy/Security.java | 7 ++-- .../com/github/kuangcp/util/SpringHelper.java | 0 {java-test => test}/README.md | 0 {java-test => test}/build.gradle | 0 .../com/github/kuangcp/domain/Person.java | 0 .../src/main/resources/person.yml | 0 .../src/test/java/junit4/Readme.md | 0 .../src/test/java/junit5/Readme.md | 0 .../src/test/java/mockito/Readme.md | 0 .../src/test/java/testng/Readme.md | 0 .../src/test/resources/person.yml | 0 626 files changed, 117 insertions(+), 99 deletions(-) rename {java-algorithms => algorithms}/build.gradle (100%) rename {java-algorithms => algorithms}/src/main/java/com/github/kuangcp/found/BinarySearch.java (100%) rename {java-algorithms => algorithms}/src/main/java/com/github/kuangcp/organizationstructure/Node.java (100%) rename {java-algorithms => algorithms}/src/main/java/com/github/kuangcp/organizationstructure/NodeAction.java (100%) rename {java-algorithms => algorithms}/src/main/java/com/github/kuangcp/organizationstructure/NodeMgr.java (100%) rename {java-algorithms => algorithms}/src/main/java/com/github/kuangcp/organizationstructure/package-info.java (100%) rename {java-algorithms => algorithms}/src/main/java/com/github/kuangcp/recursion/Fibonacci.java (100%) rename {java-algorithms => algorithms}/src/main/java/com/github/kuangcp/sort/Bubble.java (100%) rename {java-algorithms => algorithms}/src/main/java/com/github/kuangcp/sort/Insert.java (100%) rename {java-algorithms => algorithms}/src/main/java/com/github/kuangcp/sort/Quick.java (100%) rename {java-algorithms => algorithms}/src/main/java/com/github/kuangcp/sort/Radix.java (100%) rename {java-algorithms => algorithms}/src/main/java/com/github/kuangcp/sort/Select.java (100%) rename {java-algorithms => algorithms}/src/main/java/com/github/kuangcp/sort/Shell.java (100%) rename {java-algorithms => algorithms}/src/main/java/com/github/kuangcp/sort/SortAlgorithm.java (100%) rename {java-algorithms => algorithms}/src/main/java/com/github/kuangcp/sort/SortHelper.java (100%) rename {java-algorithms => algorithms}/src/main/java/com/github/kuangcp/sort/Tim.java (100%) rename {java-algorithms => algorithms}/src/main/java/com/github/kuangcp/strcuture/stack/MythArrayStack.java (100%) rename {java-algorithms => algorithms}/src/main/java/com/github/kuangcp/strcuture/stack/MythBaseStack.java (100%) rename {java-algorithms => algorithms}/src/main/java/com/github/kuangcp/strcuture/stack/MythLinkedStack.java (100%) rename {java-algorithms => algorithms}/src/main/java/com/github/kuangcp/strcuture/stackapp/AddTwoBigInteger.java (100%) rename {java-algorithms => algorithms}/src/main/java/com/github/kuangcp/strcuture/stackapp/MatchBracket.java (100%) rename {java-algorithms => algorithms}/src/main/java/com/github/kuangcp/strcuture/stackapp/OutOfTheMaze.java (100%) rename {java-algorithms => algorithms}/src/main/java/com/github/kuangcp/strcuture/stackapp/ShowStackPop.java (100%) rename {java-algorithms => algorithms}/src/main/java/com/github/kuangcp/strcuture/stackapp/StackReplaceRecurrence.java (100%) rename {java-algorithms => algorithms}/src/main/java/com/github/kuangcp/strcuture/tree/BiTree.java (100%) rename {java-algorithms => algorithms}/src/main/resources/logback.xml (100%) rename {java-algorithms => algorithms}/src/test/java/com/github/kuangcp/a/A.java (100%) rename {java-algorithms => algorithms}/src/test/java/com/github/kuangcp/a/B.java (100%) rename {java-algorithms => algorithms}/src/test/java/com/github/kuangcp/dynamicprogramming/Readme.md (100%) rename {java-algorithms => algorithms}/src/test/java/com/github/kuangcp/found/BinarySearchTest.java (100%) rename {java-algorithms => algorithms}/src/test/java/com/github/kuangcp/organizationstructure/NodeMgrTest.java (100%) rename {java-algorithms => algorithms}/src/test/java/com/github/kuangcp/recursion/FibonacciTest.java (100%) rename {java-algorithms => algorithms}/src/test/java/com/github/kuangcp/sort/SortTest.java (100%) rename {java-algorithms => algorithms}/src/test/java/com/github/kuangcp/strcuture/stack/MythArrayStackTest.java (100%) rename {java-algorithms => algorithms}/src/test/java/com/github/kuangcp/strcuture/stack/MythLinkedStackTest.java (100%) rename {java-algorithms => algorithms}/src/test/java/com/github/kuangcp/strcuture/stackapp/AddTwoBigIntegerTest.java (100%) rename {java-algorithms => algorithms}/src/test/java/com/github/kuangcp/strcuture/stackapp/MatchBracketTest.java (100%) rename {java-algorithms => algorithms}/src/test/java/com/github/kuangcp/strcuture/stackapp/ShowStackPopTest.java (100%) rename {java-algorithms => algorithms}/src/test/java/com/github/kuangcp/strcuture/stackapp/SimulationQueue.java (100%) rename {java-class => class}/README.md (100%) rename {java-class => class}/build.gradle (100%) rename {java-class => class}/src/main/java/com/github/kuangcp/README.md (100%) rename {java-class => class}/src/main/java/com/github/kuangcp/ValueCacheDemo.java (100%) rename {java-class => class}/src/main/java/com/github/kuangcp/annotation/First.java (100%) rename {java-class => class}/src/main/java/com/github/kuangcp/authorityscope/README.md (100%) rename {java-class => class}/src/main/java/com/github/kuangcp/authorityscope/package1/A.java (100%) rename {java-class => class}/src/main/java/com/github/kuangcp/authorityscope/package1/B.java (100%) rename {java-class => class}/src/main/java/com/github/kuangcp/authorityscope/package1/C.java (100%) rename {java-class => class}/src/main/java/com/github/kuangcp/authorityscope/package2/D.java (100%) rename {java-class => class}/src/main/java/com/github/kuangcp/authorityscope/package2/E.java (100%) rename {java-class => class}/src/main/java/com/github/kuangcp/instantiation/ComplexConstructor.java (100%) rename {java-class => class}/src/main/java/com/github/kuangcp/instantiation/InstantiationAndConstructor.java (100%) rename {java-class => class}/src/main/java/com/github/kuangcp/instantiation/Readme.md (100%) rename {java-class => class}/src/main/java/com/github/kuangcp/instantiation/StaticFieldInit.java (100%) rename {java-class => class}/src/main/java/com/github/kuangcp/read/ClassScanner.java (100%) rename {java-class => class}/src/main/java/com/github/kuangcp/reflects/ReflectTargetObject.java (100%) rename {java-class => class}/src/main/java/com/github/kuangcp/serialize/Person.java (100%) rename {java-class => class}/src/main/java/com/github/kuangcp/serialize/customserialize/Myth.java (100%) rename {java-class => class}/src/main/java/com/github/kuangcp/serialize/customserialize/MythSerialize.java (100%) rename {java-class => class}/src/main/java/com/github/kuangcp/serialize/json/JsonSmileMigrationService.java (100%) rename {java-class => class}/src/main/java/com/github/kuangcp/serialize/json/speed/FastJsonTool.java (100%) rename {java-class => class}/src/main/java/com/github/kuangcp/serialize/json/speed/GsonTool.java (100%) rename {java-class => class}/src/main/java/com/github/kuangcp/serialize/json/speed/JacksonTool.java (100%) rename {java-class => class}/src/main/java/com/github/kuangcp/serialize/json/speed/JsonTool.java (100%) rename {java-class => class}/src/main/java/com/github/kuangcp/serialize/json/speed/README.md (100%) rename {java-class => class}/src/main/java/com/github/kuangcp/util/ShowBinary.java (100%) rename {java-class => class}/src/main/java/jmh/StringBuilderBenchmark.java (100%) rename {java-class => class}/src/main/java/jvm/oom/DirectMemoryOOM.java (100%) rename {java-class => class}/src/main/java/jvm/oom/HeapOOM.java (100%) rename {java-class => class}/src/main/java/jvm/oom/JvmStackOOM.java (100%) rename {java-class => class}/src/main/java/jvm/oom/JvmStackOverFlow.java (100%) rename {java-class => class}/src/main/java/jvm/oom/MethodAreaOOM.java (100%) rename {java-class => class}/src/main/java/jvm/oom/Readme.md (100%) rename {java-class => class}/src/main/java/jvm/oom/RunTimeConstantPoolOOM.java (100%) rename {java-class => class}/src/main/java/syntax/enums/Color.java (100%) rename {java-class => class}/src/main/java/syntax/enums/SingletonDemo.java (100%) rename {java-class => class}/src/main/java/syntax/enums/package-info.java (100%) rename {java-class => class}/src/main/java/syntax/package-info.java (100%) rename {java-class => class}/src/main/resources/logback.groovy.back (100%) rename {java-class => class}/src/main/resources/logback.xml (100%) rename {java-class => class}/src/test/java/com/github/kuangcp/inherit/SameFieldTest.java (100%) rename {java-class => class}/src/test/java/com/github/kuangcp/instantiation/ComplexConstructorTest.java (100%) rename {java-class => class}/src/test/java/com/github/kuangcp/instantiation/InstantiationAndConstructorTest.java (100%) rename {java-class => class}/src/test/java/com/github/kuangcp/instantiation/StaticFieldInitTest.java (100%) rename {java-class => class}/src/test/java/com/github/kuangcp/loader/ClassInitialTest.java (100%) rename {java-class => class}/src/test/java/com/github/kuangcp/loader/InstantiationSortTest.java (100%) rename {java-class => class}/src/test/java/com/github/kuangcp/read/BaseFather.java (100%) rename {java-class => class}/src/test/java/com/github/kuangcp/read/ClassScannerTest.java (100%) rename {java-class => class}/src/test/java/com/github/kuangcp/read/demo/FirstBase.java (100%) rename {java-class => class}/src/test/java/com/github/kuangcp/read/demo/SeconedBase.java (100%) rename {java-class => class}/src/test/java/com/github/kuangcp/read/demo/son/ThirdBase.java (100%) rename {java-class => class}/src/test/java/com/github/kuangcp/reflects/InvokeWithInheritParamTest.java (100%) rename {java-class => class}/src/test/java/com/github/kuangcp/reflects/ReflectPerformanceTest.java (100%) rename {java-class => class}/src/test/java/com/github/kuangcp/reflects/ReflectTargetObjectTest.java (100%) rename {java-class => class}/src/test/java/com/github/kuangcp/serialize/SerializeTest.java (100%) rename {java-class => class}/src/test/java/com/github/kuangcp/serialize/customserialize/MythSerializeTest.java (100%) rename {java-class => class}/src/test/java/com/github/kuangcp/serialize/json/GsonTest.java (100%) rename {java-class => class}/src/test/java/com/github/kuangcp/serialize/json/SpeedTest.java (100%) rename {java-class => class}/src/test/java/jmh/StringBuilderBenchmarkTest.java (100%) rename {java-class => class}/src/test/java/jvm/oom/HeapOOMTest.java (100%) rename {java-class => class}/src/test/java/log/CorrectLogTest.java (100%) rename {java-class => class}/src/test/java/math/RandomTest.java (100%) rename {java-class => class}/src/test/java/syntax/InstanceofTest.java (100%) rename {java-class => class}/src/test/java/syntax/base/method/EqualsAndHashCodeTest.java (100%) rename {java-class => class}/src/test/java/syntax/base/method/ParamTransferTest.java (100%) rename {java-class => class}/src/test/java/syntax/basetype/BaseTypeDefaultValueTest.java (100%) rename {java-class => class}/src/test/java/syntax/bit/BitOperatorsTest.java (100%) rename {java-class => class}/src/test/java/syntax/bit/StringPerformanceTest.java (100%) rename {java-class => class}/src/test/java/syntax/bytes/OperatorTest.java (100%) rename {java-class => class}/src/test/java/syntax/doubles/DoubleConstTest.java (100%) rename {java-class => class}/src/test/java/syntax/expression/ShortCircuitTest.java (100%) rename {java-class => class}/src/test/java/syntax/floats/AccuracyLoseTest.java (100%) rename {java-class => class}/src/test/java/syntax/innerclass/ServerConfig.java (100%) rename {java-class => class}/src/test/java/syntax/integer/IntegerCacheTest.java (100%) rename {java-class => class}/src/test/java/syntax/longs/LongTest.java (100%) rename {java-class => class}/src/test/java/syntax/package-info.java (100%) rename {java-class => class}/src/test/java/syntax/string/SplitDemoTest.java (100%) rename {java-class => class}/src/test/java/syntax/string/StringTest.java (100%) rename {java-class => class}/src/test/java/syntax/tryblock/FinallyWithReturn.java (100%) rename {java-class => class}/src/test/java/syntax/tryblock/TWR.java (100%) rename {java-class => class}/src/test/java/time/InstantTest.java (100%) rename {java-class => class}/src/test/java/time/LocalDateTest.java (100%) rename {java-class => class}/src/test/java/time/LocalDateTimeTest.java (100%) rename {java-class => class}/src/test/java/time/OffsetTest.java (100%) rename {java-class => class}/src/test/java/time/Readme.md (100%) rename {java-collection => collection}/build.gradle (100%) rename {java-collection => collection}/src/test/java/README.md (100%) rename {java-collection => collection}/src/test/java/com/github/kuangcp/list/ConcurrentModificationTest.java (100%) rename {java-collection => collection}/src/test/java/com/github/kuangcp/list/ListWithArrayTest.java (100%) rename {java-collection => collection}/src/test/java/com/github/kuangcp/list/OperationTest.java (100%) rename {java-collection => collection}/src/test/java/com/github/kuangcp/map/CurrentModificationTest.java (100%) rename {java-collection => collection}/src/test/java/com/github/kuangcp/map/HashMapTest.java (100%) rename {java-collection => collection}/src/test/java/com/github/kuangcp/map/IterateMapTest.java (100%) rename {java-collection => collection}/src/test/java/com/github/kuangcp/map/README.md (100%) rename {java-collection => collection}/src/test/java/com/github/kuangcp/map/RandomGetValueTest.java (100%) rename {java-collection => collection}/src/test/java/com/github/kuangcp/set/HashSetTest.java (100%) rename {java-collection => collection}/src/test/java/com/github/kuangcp/set/TreeSetTest.java (100%) rename {java-concurrency => concurrency}/Readme.md (100%) rename {java-concurrency => concurrency}/build.gradle (100%) rename {java-concurrency => concurrency}/src/main/java/com/github/kuangcp/README.md (100%) rename {java-concurrency => concurrency}/src/main/java/com/github/kuangcp/countlatch/CountLatchDemo.java (100%) rename {java-concurrency => concurrency}/src/main/java/com/github/kuangcp/countlatch/ProcessingThread.java (100%) rename {java-concurrency => concurrency}/src/main/java/com/github/kuangcp/forkjoin/Counter.groovy (100%) rename {java-concurrency => concurrency}/src/main/java/com/github/kuangcp/forkjoin/Element.groovy (100%) rename {java-concurrency => concurrency}/src/main/java/com/github/kuangcp/forkjoin/ElementSorter.groovy (100%) rename {java-concurrency => concurrency}/src/main/java/com/github/kuangcp/forkjoin/ForkJoinEasyDemo.groovy (100%) rename {java-concurrency => concurrency}/src/main/java/com/github/kuangcp/forkjoin/Readme.md (100%) rename {java-concurrency => concurrency}/src/main/java/com/github/kuangcp/forkjoin/SynchronizedDemo.groovy (100%) rename {java-concurrency => concurrency}/src/main/java/com/github/kuangcp/list/ArrayListDemo.java (100%) rename {java-concurrency => concurrency}/src/main/java/com/github/kuangcp/list/CopyOnWriteDemo.java (100%) rename {java-concurrency => concurrency}/src/main/java/com/github/kuangcp/list/Element.java (100%) rename {java-concurrency => concurrency}/src/main/java/com/github/kuangcp/list/ElementArrayList.java (100%) rename {java-concurrency => concurrency}/src/main/java/com/github/kuangcp/list/ElementList.java (100%) rename {java-concurrency => concurrency}/src/main/java/com/github/kuangcp/list/ShowCopyOnWrite.java (100%) rename {java-concurrency => concurrency}/src/main/java/com/github/kuangcp/lock/dead/DeadLock.java (100%) rename {java-concurrency => concurrency}/src/main/java/com/github/kuangcp/lock/dead/DeadLockDemo.java (100%) rename {java-concurrency => concurrency}/src/main/java/com/github/kuangcp/lock/dead/DeadLockHandler.java (100%) rename {java-concurrency => concurrency}/src/main/java/com/github/kuangcp/lock/dead/DeadLockHandlerDemo.java (100%) rename {java-concurrency => concurrency}/src/main/java/com/github/kuangcp/lock/dead/DeadLockRewrite.java (100%) rename {java-concurrency => concurrency}/src/main/java/com/github/kuangcp/lock/dead/DeadLockRewriteDemo.java (100%) rename {java-concurrency => concurrency}/src/main/java/com/github/kuangcp/lock/pvp/Player.java (100%) rename {java-concurrency => concurrency}/src/main/java/com/github/kuangcp/lock/sync/Action.java (100%) rename {java-concurrency => concurrency}/src/main/java/com/github/kuangcp/lock/sync/Readme.md (100%) rename {java-concurrency => concurrency}/src/main/java/com/github/kuangcp/lock/sync/SleepDemo.java (100%) rename {java-concurrency => concurrency}/src/main/java/com/github/kuangcp/old/BuildFactory.java (100%) rename {java-concurrency => concurrency}/src/main/java/com/github/kuangcp/old/CreateObjByBuilder.groovy (100%) rename {java-concurrency => concurrency}/src/main/java/com/github/kuangcp/old/DeadLock.java (100%) rename {java-concurrency => concurrency}/src/main/java/com/github/kuangcp/old/DeadLockShow.java (100%) rename {java-concurrency => concurrency}/src/main/java/com/github/kuangcp/old/Food.java (100%) rename {java-concurrency => concurrency}/src/main/java/com/github/kuangcp/old/MainThread.java (100%) rename {java-concurrency => concurrency}/src/main/java/com/github/kuangcp/old/ObjBuilder.java (100%) rename {java-concurrency => concurrency}/src/main/java/com/github/kuangcp/old/PrefectSynchronized.java (100%) rename {java-concurrency => concurrency}/src/main/java/com/github/kuangcp/queue/ShowMicro.groovy (100%) rename {java-concurrency => concurrency}/src/main/java/com/github/kuangcp/queue/use/blocking/Appointment.java (100%) rename {java-concurrency => concurrency}/src/main/java/com/github/kuangcp/queue/use/blocking/Cat.java (100%) rename {java-concurrency => concurrency}/src/main/java/com/github/kuangcp/queue/use/blocking/Dog.java (100%) rename {java-concurrency => concurrency}/src/main/java/com/github/kuangcp/queue/use/blocking/Pet.java (100%) rename {java-concurrency => concurrency}/src/main/java/com/github/kuangcp/queue/use/blocking/Veterinarian.java (100%) rename {java-concurrency => concurrency}/src/main/java/com/github/kuangcp/schedule/CreateModel.groovy (100%) rename {java-concurrency => concurrency}/src/main/java/com/github/kuangcp/schedule/ShowSTPE.groovy (100%) rename {java-concurrency => concurrency}/src/main/java/com/github/kuangcp/sync/Cache.groovy (100%) rename {java-concurrency => concurrency}/src/main/java/com/github/kuangcp/sync/SyncField.groovy (100%) rename {java-concurrency => concurrency}/src/main/java/com/github/kuangcp/sync/package-info.java (100%) rename {java-concurrency => concurrency}/src/main/java/com/github/kuangcp/volatiles/NeverStopThread.java (100%) rename {java-concurrency => concurrency}/src/main/java/redis/migration/Main.java (100%) rename {java-concurrency => concurrency}/src/main/java/redis/migration/Readme.md (100%) rename {java-concurrency => concurrency}/src/main/java/redis/migration/RedisDataType.java (100%) rename {java-concurrency => concurrency}/src/main/java/redis/migration/RedisPoolProperty.java (100%) rename {java-concurrency => concurrency}/src/main/java/redis/migration/RedisPools.java (100%) rename {java-concurrency => concurrency}/src/main/java/redis/package-info.java (100%) rename {java-concurrency => concurrency}/src/main/java/thread/HowToCreateThread.java (100%) rename {java-concurrency => concurrency}/src/main/java/thread/README.md (100%) rename {java-concurrency => concurrency}/src/main/java/thread/ShowCreateThreadForSimpleMain.java (100%) rename {java-concurrency => concurrency}/src/main/java/thread/ThreadInterruptedDemo.java (100%) rename {java-concurrency => concurrency}/src/main/java/thread/ThreadJoinDemo.java (100%) rename {java-concurrency => concurrency}/src/main/java/thread/ThreadStatusTransfer.java (100%) rename {java-concurrency => concurrency}/src/main/java/thread/UseThreadPool.java (100%) rename {java-concurrency => concurrency}/src/main/java/thread/order/README.md (100%) rename {java-concurrency => concurrency}/src/main/java/thread/order/Task.java (100%) rename {java-concurrency => concurrency}/src/main/java/thread/order/TaskWithVolatile.java (100%) rename {java-concurrency => concurrency}/src/main/java/thread/pcstatus/Consumer.java (100%) rename {java-concurrency => concurrency}/src/main/java/thread/pcstatus/Main.java (100%) rename {java-concurrency => concurrency}/src/main/java/thread/pcstatus/Producer.java (100%) rename {java-concurrency => concurrency}/src/main/java/thread/pcstatus/README.md (100%) rename {java-concurrency => concurrency}/src/main/java/thread/pcstatus/Share.java (100%) rename {java-concurrency => concurrency}/src/test/java/com/github/kuangcp/lock/pvp/PlayerTest.java (100%) rename {java-concurrency => concurrency}/src/test/java/com/github/kuangcp/lock/sync/ActionTest.java (100%) rename {java-concurrency => concurrency}/src/test/java/com/github/kuangcp/queue/use/blocking/VeterinarianTest.java (100%) rename {java-concurrency => concurrency}/src/test/java/com/github/kuangcp/volatiles/NeverStopThreadTest.java (100%) rename {java-concurrency => concurrency}/src/test/java/redis/migration/MainTest.java (100%) rename {java-concurrency => concurrency}/src/test/java/thread/ThreadStatusTransferTest.java (100%) rename {java-concurrency => concurrency}/src/test/java/thread/order/OrderedThreadTest.java (100%) rename {java-concurrency => concurrency}/src/test/java/thread/schdule/TimeTaskTest.java (100%) rename {java-concurrency => concurrency}/src/test/resources/logback.xml (100%) rename {java-guava => flink}/build.gradle (100%) rename {java-generic => generic}/build.gradle (100%) rename {java-generic => generic}/src/main/java/com/github/kuangcp/ArrayUtils.java (100%) rename {java-generic => generic}/src/main/java/com/github/kuangcp/README.md (100%) rename {java-generic => generic}/src/main/java/com/github/kuangcp/common/Human.java (100%) rename {java-generic => generic}/src/main/java/com/github/kuangcp/common/Junior.java (100%) rename {java-generic => generic}/src/main/java/com/github/kuangcp/common/Student.java (100%) rename {java-generic => generic}/src/main/java/com/github/kuangcp/distribution/SampleAble.java (100%) rename {java-generic => generic}/src/main/java/com/github/kuangcp/distribution/SampleUtil.java (100%) rename {java-generic => generic}/src/main/java/com/github/kuangcp/inherit/Container.java (100%) rename {java-generic => generic}/src/main/java/com/github/kuangcp/nesting/AbstractLoader.java (100%) rename {java-generic => generic}/src/main/java/com/github/kuangcp/nesting/HumanLoader.java (100%) rename {java-generic => generic}/src/main/java/com/github/kuangcp/nesting/HumanVO.java (100%) rename {java-generic => generic}/src/main/java/com/github/kuangcp/nesting/JsonVO.java (100%) rename {java-generic => generic}/src/main/java/com/github/kuangcp/nesting/Readme.md (100%) rename {java-generic => generic}/src/main/java/com/github/kuangcp/simple/DateInterval.java (100%) rename {java-generic => generic}/src/main/java/com/github/kuangcp/simple/Pair.java (100%) rename {java-generic => generic}/src/main/java/com/github/kuangcp/simple/SimpleGenericMethod.java (100%) rename {java-generic => generic}/src/main/resources/logback.xml (100%) rename {java-generic => generic}/src/test/java/com/github/kuangcp/ArrayUtilsTest.java (100%) rename {java-generic => generic}/src/test/java/com/github/kuangcp/constraint/GenericArrayTest.java (100%) rename {java-generic => generic}/src/test/java/com/github/kuangcp/distribution/DogRef.java (100%) rename {java-generic => generic}/src/test/java/com/github/kuangcp/distribution/SampleUtilTest.java (100%) rename {java-generic => generic}/src/test/java/com/github/kuangcp/inherit/InheritTest.java (100%) rename {java-generic => generic}/src/test/java/com/github/kuangcp/nesting/HumanLoaderTest.java (100%) rename {java-generic => generic}/src/test/java/com/github/kuangcp/simple/PairTest.java (100%) rename {java-generic => generic}/src/test/java/com/github/kuangcp/simple/Score.java (100%) rename {java-generic => generic}/src/test/java/com/github/kuangcp/simple/SimpleGenericMethodTest.java (100%) create mode 100644 guava/build.gradle rename {java-guava => guava}/src/test/java/guava/base/AvoidNull.java (100%) rename {java-guava => guava}/src/test/java/guava/package-info.java (100%) rename {java-guava => guava}/src/test/java/guava/service/package-info.java (100%) rename {java-gui => gui}/build.gradle (100%) rename {java-gui => gui}/src/main/java/com/github/kuangcp/README.md (100%) rename {java-gui => gui}/src/main/java/com/github/kuangcp/caculator/Calculate_btnBegin_actionAdapter.java (100%) rename {java-gui => gui}/src/main/java/com/github/kuangcp/caculator/Calculate_btnCancel_actionAdapter.java (100%) rename {java-gui => gui}/src/main/java/com/github/kuangcp/caculator/Calculate_btnEqual_actionAdapter.java (100%) rename {java-gui => gui}/src/main/java/com/github/kuangcp/caculator/Calculate_btnIncrease_actionAdapter.java (100%) rename {java-gui => gui}/src/main/java/com/github/kuangcp/caculator/Calculate_btnMinus_actionAdapter.java (100%) rename {java-gui => gui}/src/main/java/com/github/kuangcp/caculator/Calculate_btnPoint_actionAdapter.java (100%) rename {java-gui => gui}/src/main/java/com/github/kuangcp/caculator/Calculate_btnZero_actionAdapter.java (100%) rename {java-gui => gui}/src/main/java/com/github/kuangcp/caculator/Calculator.java (100%) rename {java-gui => gui}/src/main/java/com/github/kuangcp/jigsaw/ButtonListener.java (100%) rename {java-gui => gui}/src/main/java/com/github/kuangcp/jigsaw/ImageBlock.java (100%) rename {java-gui => gui}/src/main/java/com/github/kuangcp/jigsaw/ImageBlockMgr.java (100%) rename {java-gui => gui}/src/main/java/com/github/kuangcp/jigsaw/MainFrame.java (100%) rename {java-gui => gui}/src/main/java/com/github/kuangcp/jigsaw/MainPanel.java (100%) rename {java-gui => gui}/src/main/java/com/github/kuangcp/jigsaw/Move.java (100%) rename {java-gui => gui}/src/main/java/com/github/kuangcp/notepad/ICommand.java (100%) rename {java-gui => gui}/src/main/java/com/github/kuangcp/notepad/Note.java (100%) rename {java-gui => gui}/src/main/resources/jigsaw/0.jpg (100%) rename {java-gui => gui}/src/main/resources/jigsaw/1.jpg (100%) rename {java-gui => gui}/src/main/resources/jigsaw/2.jpg (100%) rename {java-gui => gui}/src/main/resources/jigsaw/3.jpg (100%) rename {java-gui => gui}/src/main/resources/jigsaw/4.jpg (100%) rename {java-gui => gui}/src/main/resources/jigsaw/5.jpg (100%) rename {java-gui => gui}/src/main/resources/jigsaw/6.jpg (100%) rename {java-gui => gui}/src/main/resources/jigsaw/7.jpg (100%) rename {java-gui => gui}/src/main/resources/jigsaw/8.jpg (100%) rename {java-gui => gui}/src/main/resources/logback.xml (100%) rename {java-hadoop => hadoop}/build.gradle (100%) rename {java-hadoop => hadoop}/src/main/java/com/github/kuangcp/hdfs/hi/GeneralFileActionDemo.java (100%) rename {java-hadoop => hadoop}/src/main/resources/logback.xml (100%) rename {java-hadoop => hadoop}/src/test/java/com/github/kuangcp/hdfs/hi/GeneralFileActionDemoTest.java (100%) rename {java-io => io}/README.md (100%) rename {java-io => io}/build.gradle (100%) rename {java-io => io}/src/main/java/com/github/kuangcp/file/CopyFile.java (100%) rename {java-io => io}/src/main/java/com/github/kuangcp/standard/Readme.md (100%) rename {java-io => io}/src/main/java/com/github/kuangcp/zip/package-info.java (100%) rename {java-io => io}/src/test/java/buffer/BufferTest.java (100%) rename {java-io => io}/src/test/java/com/github/kuangcp/PropertiesTest.java (100%) rename {java-io => io}/src/test/java/com/github/kuangcp/file/CopyFileTest.java (100%) rename {java-io => io}/src/test/resources/properties/main.properties (100%) delete mode 100644 java-spring/src/test/java/com/github/kuangcp/proxy/salary/Privilege.java delete mode 100644 java-spring/src/test/java/com/github/kuangcp/proxy/salary/SalaryManager.java delete mode 100644 java-spring/src/test/java/com/github/kuangcp/proxy/salary/SalaryTest.java delete mode 100644 java-spring/src/test/java/com/github/kuangcp/proxy/salary/proxy/Privilege.java delete mode 100644 java-spring/src/test/java/com/github/kuangcp/proxy/salary/proxy/SalaryManagerImpl.java rename {java-8 => java8}/Readme.md (100%) rename {java-8 => java8}/build.gradle (100%) rename {java-8 => java8}/src/test/java/com/github/kuangcp/function/sort/BaseTypeSortTest.java (100%) rename {java-8 => java8}/src/test/java/com/github/kuangcp/function/sort/CustomSortTest.java (100%) rename {java-8 => java8}/src/test/java/com/github/kuangcp/function/sort/DateSortTest.java (100%) rename {java-8 => java8}/src/test/java/com/github/kuangcp/lambda/filter/FilterTest.java (100%) rename {java-8 => java8}/src/test/java/com/github/kuangcp/lambda/firstdemo/MainTest.java (100%) rename {java-8 => java8}/src/test/java/com/github/kuangcp/lambda/firstdemo/PointAction.java (100%) rename {java-8 => java8}/src/test/java/com/github/kuangcp/lambda/firstdemo/PointArrayList.java (100%) rename {java-8 => java8}/src/test/java/com/github/kuangcp/lambda/firstdemo/TranslateByOne.java (100%) rename {java-8 => java8}/src/test/java/com/github/kuangcp/lambda/firstdemo/TranslateByOne8.java (100%) rename {java-8 => java8}/src/test/java/com/github/kuangcp/optional/OptionalTest.java (100%) rename {java-8 => java8}/src/test/java/com/github/kuangcp/stream/Readme.md (100%) rename {java-8 => java8}/src/test/java/com/github/kuangcp/stream/debug/UsePluginWithDebugTest.java (100%) rename {java-8 => java8}/src/test/java/com/github/kuangcp/stream/list/HandleExceptionTest.java (100%) rename {java-8 => java8}/src/test/java/com/github/kuangcp/stream/map/ToMapTest.java (100%) rename {java-8 => java8}/src/test/java/com/github/kuangcp/time/CalendarTest.java (100%) rename {java-8 => java8}/src/test/java/com/github/kuangcp/time/InstantTest.java (100%) rename {java-8 => java8}/src/test/java/com/github/kuangcp/time/LocalDateTimeTest.java (100%) rename {java-kafka => kafka}/build.gradle (100%) rename {java-kafka => kafka}/src/main/java/com/github/kuangcp/hi/Constants.java (100%) rename {java-kafka => kafka}/src/main/java/com/github/kuangcp/hi/ConsumerDemo.java (100%) rename {java-kafka => kafka}/src/main/java/com/github/kuangcp/hi/ProducerDemo.java (100%) rename {java-kafka => kafka}/src/main/java/com/github/kuangcp/hi/dto/ProductStatisticJobCommand.java (100%) rename {java-kafka => kafka}/src/main/java/com/github/kuangcp/hi/dto/ProductStatisticSpan.java (100%) rename {java-kafka => kafka}/src/main/java/com/github/kuangcp/hi/dto/StartCommand.java (100%) rename {java-kafka => kafka}/src/main/java/com/github/kuangcp/hi/dto/StatisticSpan.java (100%) rename {java-kafka => kafka}/src/main/resources/logback.xml (100%) rename {java-kafka => kafka}/src/test/java/com/github/kuangcp/hi/ConsumerDemoTest.java (100%) rename {java-kafka => kafka}/src/test/java/com/github/kuangcp/hi/ProducerDemoTest.java (100%) rename {java-netty => netty}/build.gradle (100%) rename {java-netty => netty}/src/main/java/netty/package-info.java (100%) rename {java-netty => netty}/src/main/java/netty/timeServer/Command.java (100%) rename {java-netty => netty}/src/main/java/netty/timeServer/README.md (100%) rename {java-netty => netty}/src/main/java/netty/timeServer/TimeClient.java (100%) rename {java-netty => netty}/src/main/java/netty/timeServer/TimeClientHandler.java (100%) rename {java-netty => netty}/src/main/java/netty/timeServer/TimeServer.java (100%) rename {java-netty => netty}/src/main/java/netty/timeServer/TimeServerHandler.java (100%) rename {java-netty => netty}/src/main/resources/logback.xml (100%) rename {java-netty => netty}/src/test/java/netty/timeServer/TimeServerTest.java (100%) rename {java-network => network}/README.md (100%) rename {java-network => network}/build.gradle (100%) rename {java-network => network}/src/main/java/com/github/kuangcp/bio/Readme.md (100%) rename {java-network => network}/src/main/java/com/github/kuangcp/bio/chattingroom/ClientThread.java (100%) rename {java-network => network}/src/main/java/com/github/kuangcp/bio/chattingroom/README.md (100%) rename {java-network => network}/src/main/java/com/github/kuangcp/bio/chattingroom/ServerThread.java (100%) rename {java-network => network}/src/main/java/com/github/kuangcp/bio/chattingroom/SocketClient.java (100%) rename {java-network => network}/src/main/java/com/github/kuangcp/bio/chattingroom/SocketServer.java (100%) rename {java-network => network}/src/main/java/com/github/kuangcp/bio/onechatone/ChatMaps.java (100%) rename {java-network => network}/src/main/java/com/github/kuangcp/bio/onechatone/ChatProtocol.java (100%) rename {java-network => network}/src/main/java/com/github/kuangcp/bio/onechatone/Client.java (100%) rename {java-network => network}/src/main/java/com/github/kuangcp/bio/onechatone/ClientThread.java (100%) rename {java-network => network}/src/main/java/com/github/kuangcp/bio/onechatone/README.md (100%) rename {java-network => network}/src/main/java/com/github/kuangcp/bio/onechatone/Server.java (100%) rename {java-network => network}/src/main/java/com/github/kuangcp/bio/onechatone/ServerThread.java (100%) rename {java-network => network}/src/main/java/com/github/kuangcp/nio/EchoNIOServer.java (100%) rename {java-network => network}/src/main/java/com/github/kuangcp/nio/NIOClient.java (100%) rename {java-network => network}/src/main/java/com/github/kuangcp/nio/NIOServer.java (100%) rename {java-network => network}/src/main/java/com/github/kuangcp/port/LogicThread.java (100%) rename {java-network => network}/src/main/java/com/github/kuangcp/port/LogicThreadTest.java (100%) rename {java-network => network}/src/main/java/com/github/kuangcp/port/MulSocketServer.java (100%) rename {java-network => network}/src/main/java/com/github/kuangcp/runable/GreetingClient.java (100%) rename {java-network => network}/src/main/java/com/github/kuangcp/runable/GreetingServer.java (100%) rename {java-network => network}/src/main/java/com/github/kuangcp/selfclose/SocketSelfClose.java (100%) rename {java-network => network}/src/main/resources/logback.xml (100%) rename {java-network => network}/src/test/java/com/github/kuangcp/port/DomainQueryTest.java (100%) rename {java-network => network}/src/test/java/com/github/kuangcp/runable/TuLingRobotTest.java (100%) rename {java-pattern => pattern}/Readme.md (100%) rename {java-pattern => pattern}/build.gradle (100%) rename {java-pattern => pattern}/src/main/java/com/github/kuangcp/abstractfactory/AbstractFactory.java (100%) rename {java-pattern => pattern}/src/main/java/com/github/kuangcp/abstractfactory/ColorFactory.java (100%) rename {java-pattern => pattern}/src/main/java/com/github/kuangcp/abstractfactory/FactoryProducer.java (100%) rename {java-pattern => pattern}/src/main/java/com/github/kuangcp/abstractfactory/ShapeFactory.java (100%) rename {java-pattern => pattern}/src/main/java/com/github/kuangcp/abstractfactory/base/Color.java (100%) rename {java-pattern => pattern}/src/main/java/com/github/kuangcp/abstractfactory/base/Shape.java (100%) rename {java-pattern => pattern}/src/main/java/com/github/kuangcp/abstractfactory/domain/Green.java (100%) rename {java-pattern => pattern}/src/main/java/com/github/kuangcp/abstractfactory/domain/Rectangle.java (100%) rename {java-pattern => pattern}/src/main/java/com/github/kuangcp/abstractfactory/domain/Red.java (100%) rename {java-pattern => pattern}/src/main/java/com/github/kuangcp/abstractfactory/domain/Square.java (100%) rename {java-pattern => pattern}/src/main/java/com/github/kuangcp/decorator/Decorator.java (100%) rename {java-pattern => pattern}/src/main/java/com/github/kuangcp/decorator/FootDecorator.java (100%) rename {java-pattern => pattern}/src/main/java/com/github/kuangcp/decorator/HeadDecorator.java (100%) rename {java-pattern => pattern}/src/main/java/com/github/kuangcp/decorator/Invoice.java (100%) rename {java-pattern => pattern}/src/main/java/com/github/kuangcp/decorator/Readme.md (100%) rename {java-pattern => pattern}/src/main/java/com/github/kuangcp/singleton/DoubleCheck.java (100%) rename {java-pattern => pattern}/src/main/java/com/github/kuangcp/singleton/DoubleCheckWithVolatile.java (100%) rename {java-pattern => pattern}/src/main/java/com/github/kuangcp/singleton/Enum.java (100%) rename {java-pattern => pattern}/src/main/java/com/github/kuangcp/singleton/Readme.md (100%) rename {java-pattern => pattern}/src/main/java/com/github/kuangcp/singleton/StaticBlock.java (100%) rename {java-pattern => pattern}/src/main/java/com/github/kuangcp/singleton/StaticFinal.java (100%) rename {java-pattern => pattern}/src/main/java/com/github/kuangcp/singleton/StaticInit.java (100%) rename {java-pattern => pattern}/src/main/java/com/github/kuangcp/singleton/StaticInnerClass.java (100%) rename {java-pattern => pattern}/src/main/java/com/github/kuangcp/singleton/StaticLazyWithSyncBlock.java (100%) rename {java-pattern => pattern}/src/main/java/com/github/kuangcp/singleton/StaticLazyWithSyncMethod.java (100%) rename {java-pattern => pattern}/src/main/resources/logback.xml (100%) rename {java-pattern => pattern}/src/test/java/com/github/kuangcp/abstractfactory/FactoryProducerTest.java (100%) rename {java-pattern => pattern}/src/test/java/com/github/kuangcp/decorator/DecoratorTest.java (100%) rename {java-question => question}/README.md (100%) rename {java-question => question}/build.gradle (100%) rename {java-question => question}/src/main/java/com/github/kuangcp/caculator/ListDengShi2.java (100%) rename {java-question => question}/src/main/java/com/github/kuangcp/caculator/ListExpressionDemo.java (100%) rename {java-question => question}/src/main/java/com/github/kuangcp/key/CreateKey.java (100%) rename {java-question => question}/src/main/java/com/github/kuangcp/key/RSAEncoder.java (100%) rename {java-question => question}/src/main/java/com/github/kuangcp/simpleMethod/README.md (100%) rename {java-question => question}/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethod/Equality.java (100%) rename {java-question => question}/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethod/SimplexMethod.java (100%) rename {java-question => question}/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethod/Table.java (100%) rename {java-question => question}/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethodQuarter/Equality.java (100%) rename {java-question => question}/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethodQuarter/SimplexMethod.java (100%) rename {java-question => question}/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethodQuarter/Table.java (100%) rename {java-question => question}/src/main/java/com/github/kuangcp/simpleMethod/number/ReadProperties.java (100%) rename {java-question => question}/src/main/resources/math/SimplexMethod.properties (100%) rename {java-question => question}/src/main/resources/math/log4j.xml (100%) rename {java-question => question}/src/test/java/com/github/kuangcp/key/CreateKeyTest.java (100%) rename {java-question => question}/src/test/java/com/github/kuangcp/math/AbsTest.java (100%) rename {java-question => question}/src/test/java/com/github/kuangcp/simpleMethod/SimplexMethodQuarter/MethodTest.java (100%) rename {java-question => question}/src/test/java/scattered/SmoothingTheData.java (100%) rename {java-spring => spring}/.gitignore (100%) rename {java-spring => spring}/Readme.md (100%) rename {java-spring => spring}/build.gradle (100%) rename {java-spring => spring}/src/main/java/com/github/kuangcp/proxy/dao/base/CustomInterceptor.java (100%) rename {java-spring => spring}/src/main/java/com/github/kuangcp/proxy/dao/base/Person.java (100%) rename {java-spring => spring}/src/main/java/com/github/kuangcp/proxy/dao/base/Transaction.java (100%) rename {java-spring => spring}/src/main/java/com/github/kuangcp/proxy/dao/cglibproxy/PersonDaoImpl.java (100%) rename {java-spring => spring}/src/main/java/com/github/kuangcp/proxy/dao/cglibproxy/PersonDaoInterceptor.java (100%) rename {java-spring => spring}/src/main/java/com/github/kuangcp/proxy/dao/jdkproxy/PersonDao.java (100%) rename {java-spring => spring}/src/main/java/com/github/kuangcp/proxy/dao/jdkproxy/PersonDaoImpl.java (100%) rename {java-spring => spring}/src/main/java/com/github/kuangcp/proxy/dao/jdkproxy/PersonDaoInterceptor.java (100%) rename {java-spring => spring}/src/main/resources/logback.xml (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/aop/ExceptionTest.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/aop/annotation/Person.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/aop/annotation/PersonDao.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/aop/annotation/PersonDaoImpl.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/aop/annotation/PersonTest.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/aop/annotation/Transaction.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/aop/annotation/applicationContext.xml (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/aop/applicationContext-exception.xml (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/aop/exception/MyException.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/aop/exception/Readme.md (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/aop/exception/action/PersonAction.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/aop/exception/dao/PersonDao.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/aop/exception/dao/PersonDaoImpl.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/aop/exception/service/PersonService.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/aop/exception/service/PersonServiceImpl.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/aop/xml/Person.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/aop/xml/PersonDao.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/aop/xml/PersonDaoImpl.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/aop/xml/PersonTest.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/aop/xml/Readme.md (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/aop/xml/Transaction.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/aop/xml/applicationContext.xml (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/aop/xml/salary/Logger.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/aop/xml/salary/Privilege.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/aop/xml/salary/Readmd.md (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/aop/xml/salary/SalaryManager.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/aop/xml/salary/SalaryTest.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/aop/xml/salary/Security.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/aop/xml/salary/applicationContext.xml (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/di/annotation/AnnotationParse.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/di/annotation/Description.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/di/annotation/ITCAST.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/di/annotation/Name.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/di/di/annotation/Person.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/di/di/annotation/PersonTest.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/di/di/annotation/Student.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/di/di/annotation/applicationContext.xml (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/di/di/xml/constructor/Person.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/di/di/xml/constructor/PersonTest.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/di/di/xml/constructor/Student.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/di/di/xml/constructor/applicationContext.xml (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/di/di/xml/set/Person.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/di/di/xml/set/PersonTest.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/di/di/xml/set/Student.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/di/di/xml/set/applicationContext.xml (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/di/scan/Person.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/di/scan/PersonTest.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/di/scan/Student.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/di/scan/applicationContext.xml (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/document/Document.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/document/DocumentManager.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/document/DocumentTest.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/document/ExcelDocument.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/document/PDFDocument.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/document/WordDocument.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/document/spring/Document.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/document/spring/DocumentManager.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/document/spring/DocumentTest.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/document/spring/ExcelDocument.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/document/spring/PDFDocument.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/document/spring/WordDocument.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/document/spring/applicationContext.xml (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/exception/ExceptionTest.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/exception/Readme.md (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/exception/ServiceInvocation.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/exception/ServiceMapping.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/exception/service/PersonServiceImpl.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/exception/service/Service.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/exception/service/StudentServiceImpl.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/extend/Person.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/extend/PersonTest.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/extend/Student.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/extend/applicationContext.xml (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/extend/log4j.xml (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/hibernate/annotation/Person.hbm.xml (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/hibernate/annotation/Person.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/hibernate/annotation/PersonDao.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/hibernate/annotation/PersonDaoImpl.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/hibernate/annotation/PersonService.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/hibernate/annotation/PersonServiceImpl.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/hibernate/annotation/PersonTest.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/hibernate/annotation/applicationContext.xml (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/hibernate/hibernate.cfg.xml (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/hibernate/xml/Person.hbm.xml (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/hibernate/xml/Person.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/hibernate/xml/PersonDao.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/hibernate/xml/PersonDaoImpl.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/hibernate/xml/PersonService.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/hibernate/xml/PersonServiceImpl.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/hibernate/xml/PersonTest.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/hibernate/xml/Readme.md (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/hibernate/xml/applicationContext.xml (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/ioc/alias/AliasTest.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/ioc/alias/HelloWorld.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/ioc/alias/applicationContext.xml (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/ioc/createobject/CreateObjectTest.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/ioc/createobject/HelloWorld.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/ioc/createobject/HelloWorldFactory.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/ioc/createobject/applicationContext.xml (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/ioc/createobject/when/HelloWorld.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/ioc/createobject/when/HelloWorldFactory.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/ioc/createobject/when/Person.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/ioc/createobject/when/WhenTest.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/ioc/createobject/when/applicationContext.xml (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/ioc/helloworld/HelloWorld.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/ioc/helloworld/HelloWorldTest.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/ioc/helloworld/applicationContext.xml (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/ioc/initdestroy/HelloWorld.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/ioc/initdestroy/InitDestroyTest.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/ioc/initdestroy/applicationContext.xml (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/ioc/scope/HelloWorld.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/ioc/scope/ScopeTest.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/ioc/scope/applicationContext.xml (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/jdbc/jdbc.properties (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/jdbc/jdbc/DataSourceTest.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/jdbc/jdbc/Person.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/jdbc/jdbc/PersonDao.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/jdbc/jdbc/PersonDaoImpl.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/jdbc/jdbc/PersonDaoImpl2.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/jdbc/jdbc/PersonDaoImpl3.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/jdbc/jdbc/PersonRowMapper.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/jdbc/jdbc/PersonTest.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/jdbc/jdbc/applicationContext.xml (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/PersonDao.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/PersonDaoImpl.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/PersonService.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/PersonServiceImpl.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/PersonTest.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/annotation/PersonDao.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/annotation/PersonDaoImpl.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/annotation/PersonService.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/annotation/PersonServiceImpl.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/annotation/PersonTest.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/annotation/applicationContext.xml (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/applicationContext.xml (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/jdbc/transaction/PersonDao.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/jdbc/transaction/PersonDaoImpl.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/jdbc/transaction/PersonService.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/jdbc/transaction/PersonServiceImpl.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/jdbc/transaction/PersonTest.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/jdbc/transaction/applicationContext.xml (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/mvc/PersonAction.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/mvc/PersonDao.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/mvc/PersonDaoImpl.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/mvc/PersonService.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/mvc/PersonServiceImpl.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/mvc/PersonTest.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/mvc/annotation/PersonAction.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/mvc/annotation/PersonDao.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/mvc/annotation/PersonDaoImpl.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/mvc/annotation/PersonService.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/mvc/annotation/PersonServiceImpl.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/mvc/annotation/PersonTest.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/mvc/annotation/applicationContext.xml (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/mvc/applicationContext.xml (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/proxy/dao/Readme.md (69%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/proxy/dao/cglibproxy/CGlibProxyTest.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/proxy/dao/jdkproxy/JDKProxyTest.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/proxy/dao/jdkproxy/Readme.md (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/proxy/salary/Logger.java (50%) create mode 100644 spring/src/test/java/com/github/kuangcp/proxy/salary/Privilege.java create mode 100644 spring/src/test/java/com/github/kuangcp/proxy/salary/SalaryManager.java create mode 100644 spring/src/test/java/com/github/kuangcp/proxy/salary/SalaryTest.java rename {java-spring => spring}/src/test/java/com/github/kuangcp/proxy/salary/Security.java (50%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/proxy/salary/aop/xml/Person.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/proxy/salary/aop/xml/PersonDao.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/proxy/salary/aop/xml/PersonDaoImpl.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/proxy/salary/aop/xml/PersonTest.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/proxy/salary/aop/xml/Transaction.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/proxy/salary/aop/xml/applicationContext.xml (100%) rename "java-spring/src/test/java/com/github/kuangcp/proxy/salary/aop/xml/\344\275\277\347\224\250XML\345\256\236\347\216\260AOP.txt" => "spring/src/test/java/com/github/kuangcp/proxy/salary/aop/xml/\344\275\277\347\224\250XML\345\256\236\347\216\260AOP.txt" (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/Logger.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/Privilege.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/ProxyTest.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/SalaryInterceptor.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/SalaryManager.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/SalaryManagerImpl.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/Security.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/aspects/Interceptor.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/aspects/Logger.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/aspects/Privilege.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/aspects/ProxyTest.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/aspects/SalaryInterceptor.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/aspects/SalaryManager.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/aspects/SalaryManagerImpl.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/aspects/Security.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/proxy/salary/proxy/Logger.java (52%) create mode 100644 spring/src/test/java/com/github/kuangcp/proxy/salary/proxy/Privilege.java rename {java-spring => spring}/src/test/java/com/github/kuangcp/proxy/salary/proxy/ProxyTest.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/proxy/salary/proxy/Readme.md (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/proxy/salary/proxy/SalaryManager.java (75%) create mode 100644 spring/src/test/java/com/github/kuangcp/proxy/salary/proxy/SalaryManagerImpl.java rename {java-spring => spring}/src/test/java/com/github/kuangcp/proxy/salary/proxy/SalaryManagerProxy.java (100%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/proxy/salary/proxy/Security.java (52%) rename {java-spring => spring}/src/test/java/com/github/kuangcp/util/SpringHelper.java (100%) rename {java-test => test}/README.md (100%) rename {java-test => test}/build.gradle (100%) rename {java-test => test}/src/main/java/com/github/kuangcp/domain/Person.java (100%) rename {java-test => test}/src/main/resources/person.yml (100%) rename {java-test => test}/src/test/java/junit4/Readme.md (100%) rename {java-test => test}/src/test/java/junit5/Readme.md (100%) rename {java-test => test}/src/test/java/mockito/Readme.md (100%) rename {java-test => test}/src/test/java/testng/Readme.md (100%) rename {java-test => test}/src/test/resources/person.yml (100%) diff --git a/java-algorithms/build.gradle b/algorithms/build.gradle similarity index 100% rename from java-algorithms/build.gradle rename to algorithms/build.gradle diff --git a/java-algorithms/src/main/java/com/github/kuangcp/found/BinarySearch.java b/algorithms/src/main/java/com/github/kuangcp/found/BinarySearch.java similarity index 100% rename from java-algorithms/src/main/java/com/github/kuangcp/found/BinarySearch.java rename to algorithms/src/main/java/com/github/kuangcp/found/BinarySearch.java diff --git a/java-algorithms/src/main/java/com/github/kuangcp/organizationstructure/Node.java b/algorithms/src/main/java/com/github/kuangcp/organizationstructure/Node.java similarity index 100% rename from java-algorithms/src/main/java/com/github/kuangcp/organizationstructure/Node.java rename to algorithms/src/main/java/com/github/kuangcp/organizationstructure/Node.java diff --git a/java-algorithms/src/main/java/com/github/kuangcp/organizationstructure/NodeAction.java b/algorithms/src/main/java/com/github/kuangcp/organizationstructure/NodeAction.java similarity index 100% rename from java-algorithms/src/main/java/com/github/kuangcp/organizationstructure/NodeAction.java rename to algorithms/src/main/java/com/github/kuangcp/organizationstructure/NodeAction.java diff --git a/java-algorithms/src/main/java/com/github/kuangcp/organizationstructure/NodeMgr.java b/algorithms/src/main/java/com/github/kuangcp/organizationstructure/NodeMgr.java similarity index 100% rename from java-algorithms/src/main/java/com/github/kuangcp/organizationstructure/NodeMgr.java rename to algorithms/src/main/java/com/github/kuangcp/organizationstructure/NodeMgr.java diff --git a/java-algorithms/src/main/java/com/github/kuangcp/organizationstructure/package-info.java b/algorithms/src/main/java/com/github/kuangcp/organizationstructure/package-info.java similarity index 100% rename from java-algorithms/src/main/java/com/github/kuangcp/organizationstructure/package-info.java rename to algorithms/src/main/java/com/github/kuangcp/organizationstructure/package-info.java diff --git a/java-algorithms/src/main/java/com/github/kuangcp/recursion/Fibonacci.java b/algorithms/src/main/java/com/github/kuangcp/recursion/Fibonacci.java similarity index 100% rename from java-algorithms/src/main/java/com/github/kuangcp/recursion/Fibonacci.java rename to algorithms/src/main/java/com/github/kuangcp/recursion/Fibonacci.java diff --git a/java-algorithms/src/main/java/com/github/kuangcp/sort/Bubble.java b/algorithms/src/main/java/com/github/kuangcp/sort/Bubble.java similarity index 100% rename from java-algorithms/src/main/java/com/github/kuangcp/sort/Bubble.java rename to algorithms/src/main/java/com/github/kuangcp/sort/Bubble.java diff --git a/java-algorithms/src/main/java/com/github/kuangcp/sort/Insert.java b/algorithms/src/main/java/com/github/kuangcp/sort/Insert.java similarity index 100% rename from java-algorithms/src/main/java/com/github/kuangcp/sort/Insert.java rename to algorithms/src/main/java/com/github/kuangcp/sort/Insert.java diff --git a/java-algorithms/src/main/java/com/github/kuangcp/sort/Quick.java b/algorithms/src/main/java/com/github/kuangcp/sort/Quick.java similarity index 100% rename from java-algorithms/src/main/java/com/github/kuangcp/sort/Quick.java rename to algorithms/src/main/java/com/github/kuangcp/sort/Quick.java diff --git a/java-algorithms/src/main/java/com/github/kuangcp/sort/Radix.java b/algorithms/src/main/java/com/github/kuangcp/sort/Radix.java similarity index 100% rename from java-algorithms/src/main/java/com/github/kuangcp/sort/Radix.java rename to algorithms/src/main/java/com/github/kuangcp/sort/Radix.java diff --git a/java-algorithms/src/main/java/com/github/kuangcp/sort/Select.java b/algorithms/src/main/java/com/github/kuangcp/sort/Select.java similarity index 100% rename from java-algorithms/src/main/java/com/github/kuangcp/sort/Select.java rename to algorithms/src/main/java/com/github/kuangcp/sort/Select.java diff --git a/java-algorithms/src/main/java/com/github/kuangcp/sort/Shell.java b/algorithms/src/main/java/com/github/kuangcp/sort/Shell.java similarity index 100% rename from java-algorithms/src/main/java/com/github/kuangcp/sort/Shell.java rename to algorithms/src/main/java/com/github/kuangcp/sort/Shell.java diff --git a/java-algorithms/src/main/java/com/github/kuangcp/sort/SortAlgorithm.java b/algorithms/src/main/java/com/github/kuangcp/sort/SortAlgorithm.java similarity index 100% rename from java-algorithms/src/main/java/com/github/kuangcp/sort/SortAlgorithm.java rename to algorithms/src/main/java/com/github/kuangcp/sort/SortAlgorithm.java diff --git a/java-algorithms/src/main/java/com/github/kuangcp/sort/SortHelper.java b/algorithms/src/main/java/com/github/kuangcp/sort/SortHelper.java similarity index 100% rename from java-algorithms/src/main/java/com/github/kuangcp/sort/SortHelper.java rename to algorithms/src/main/java/com/github/kuangcp/sort/SortHelper.java diff --git a/java-algorithms/src/main/java/com/github/kuangcp/sort/Tim.java b/algorithms/src/main/java/com/github/kuangcp/sort/Tim.java similarity index 100% rename from java-algorithms/src/main/java/com/github/kuangcp/sort/Tim.java rename to algorithms/src/main/java/com/github/kuangcp/sort/Tim.java diff --git a/java-algorithms/src/main/java/com/github/kuangcp/strcuture/stack/MythArrayStack.java b/algorithms/src/main/java/com/github/kuangcp/strcuture/stack/MythArrayStack.java similarity index 100% rename from java-algorithms/src/main/java/com/github/kuangcp/strcuture/stack/MythArrayStack.java rename to algorithms/src/main/java/com/github/kuangcp/strcuture/stack/MythArrayStack.java diff --git a/java-algorithms/src/main/java/com/github/kuangcp/strcuture/stack/MythBaseStack.java b/algorithms/src/main/java/com/github/kuangcp/strcuture/stack/MythBaseStack.java similarity index 100% rename from java-algorithms/src/main/java/com/github/kuangcp/strcuture/stack/MythBaseStack.java rename to algorithms/src/main/java/com/github/kuangcp/strcuture/stack/MythBaseStack.java diff --git a/java-algorithms/src/main/java/com/github/kuangcp/strcuture/stack/MythLinkedStack.java b/algorithms/src/main/java/com/github/kuangcp/strcuture/stack/MythLinkedStack.java similarity index 100% rename from java-algorithms/src/main/java/com/github/kuangcp/strcuture/stack/MythLinkedStack.java rename to algorithms/src/main/java/com/github/kuangcp/strcuture/stack/MythLinkedStack.java diff --git a/java-algorithms/src/main/java/com/github/kuangcp/strcuture/stackapp/AddTwoBigInteger.java b/algorithms/src/main/java/com/github/kuangcp/strcuture/stackapp/AddTwoBigInteger.java similarity index 100% rename from java-algorithms/src/main/java/com/github/kuangcp/strcuture/stackapp/AddTwoBigInteger.java rename to algorithms/src/main/java/com/github/kuangcp/strcuture/stackapp/AddTwoBigInteger.java diff --git a/java-algorithms/src/main/java/com/github/kuangcp/strcuture/stackapp/MatchBracket.java b/algorithms/src/main/java/com/github/kuangcp/strcuture/stackapp/MatchBracket.java similarity index 100% rename from java-algorithms/src/main/java/com/github/kuangcp/strcuture/stackapp/MatchBracket.java rename to algorithms/src/main/java/com/github/kuangcp/strcuture/stackapp/MatchBracket.java diff --git a/java-algorithms/src/main/java/com/github/kuangcp/strcuture/stackapp/OutOfTheMaze.java b/algorithms/src/main/java/com/github/kuangcp/strcuture/stackapp/OutOfTheMaze.java similarity index 100% rename from java-algorithms/src/main/java/com/github/kuangcp/strcuture/stackapp/OutOfTheMaze.java rename to algorithms/src/main/java/com/github/kuangcp/strcuture/stackapp/OutOfTheMaze.java diff --git a/java-algorithms/src/main/java/com/github/kuangcp/strcuture/stackapp/ShowStackPop.java b/algorithms/src/main/java/com/github/kuangcp/strcuture/stackapp/ShowStackPop.java similarity index 100% rename from java-algorithms/src/main/java/com/github/kuangcp/strcuture/stackapp/ShowStackPop.java rename to algorithms/src/main/java/com/github/kuangcp/strcuture/stackapp/ShowStackPop.java diff --git a/java-algorithms/src/main/java/com/github/kuangcp/strcuture/stackapp/StackReplaceRecurrence.java b/algorithms/src/main/java/com/github/kuangcp/strcuture/stackapp/StackReplaceRecurrence.java similarity index 100% rename from java-algorithms/src/main/java/com/github/kuangcp/strcuture/stackapp/StackReplaceRecurrence.java rename to algorithms/src/main/java/com/github/kuangcp/strcuture/stackapp/StackReplaceRecurrence.java diff --git a/java-algorithms/src/main/java/com/github/kuangcp/strcuture/tree/BiTree.java b/algorithms/src/main/java/com/github/kuangcp/strcuture/tree/BiTree.java similarity index 100% rename from java-algorithms/src/main/java/com/github/kuangcp/strcuture/tree/BiTree.java rename to algorithms/src/main/java/com/github/kuangcp/strcuture/tree/BiTree.java diff --git a/java-algorithms/src/main/resources/logback.xml b/algorithms/src/main/resources/logback.xml similarity index 100% rename from java-algorithms/src/main/resources/logback.xml rename to algorithms/src/main/resources/logback.xml diff --git a/java-algorithms/src/test/java/com/github/kuangcp/a/A.java b/algorithms/src/test/java/com/github/kuangcp/a/A.java similarity index 100% rename from java-algorithms/src/test/java/com/github/kuangcp/a/A.java rename to algorithms/src/test/java/com/github/kuangcp/a/A.java diff --git a/java-algorithms/src/test/java/com/github/kuangcp/a/B.java b/algorithms/src/test/java/com/github/kuangcp/a/B.java similarity index 100% rename from java-algorithms/src/test/java/com/github/kuangcp/a/B.java rename to algorithms/src/test/java/com/github/kuangcp/a/B.java diff --git a/java-algorithms/src/test/java/com/github/kuangcp/dynamicprogramming/Readme.md b/algorithms/src/test/java/com/github/kuangcp/dynamicprogramming/Readme.md similarity index 100% rename from java-algorithms/src/test/java/com/github/kuangcp/dynamicprogramming/Readme.md rename to algorithms/src/test/java/com/github/kuangcp/dynamicprogramming/Readme.md diff --git a/java-algorithms/src/test/java/com/github/kuangcp/found/BinarySearchTest.java b/algorithms/src/test/java/com/github/kuangcp/found/BinarySearchTest.java similarity index 100% rename from java-algorithms/src/test/java/com/github/kuangcp/found/BinarySearchTest.java rename to algorithms/src/test/java/com/github/kuangcp/found/BinarySearchTest.java diff --git a/java-algorithms/src/test/java/com/github/kuangcp/organizationstructure/NodeMgrTest.java b/algorithms/src/test/java/com/github/kuangcp/organizationstructure/NodeMgrTest.java similarity index 100% rename from java-algorithms/src/test/java/com/github/kuangcp/organizationstructure/NodeMgrTest.java rename to algorithms/src/test/java/com/github/kuangcp/organizationstructure/NodeMgrTest.java diff --git a/java-algorithms/src/test/java/com/github/kuangcp/recursion/FibonacciTest.java b/algorithms/src/test/java/com/github/kuangcp/recursion/FibonacciTest.java similarity index 100% rename from java-algorithms/src/test/java/com/github/kuangcp/recursion/FibonacciTest.java rename to algorithms/src/test/java/com/github/kuangcp/recursion/FibonacciTest.java diff --git a/java-algorithms/src/test/java/com/github/kuangcp/sort/SortTest.java b/algorithms/src/test/java/com/github/kuangcp/sort/SortTest.java similarity index 100% rename from java-algorithms/src/test/java/com/github/kuangcp/sort/SortTest.java rename to algorithms/src/test/java/com/github/kuangcp/sort/SortTest.java diff --git a/java-algorithms/src/test/java/com/github/kuangcp/strcuture/stack/MythArrayStackTest.java b/algorithms/src/test/java/com/github/kuangcp/strcuture/stack/MythArrayStackTest.java similarity index 100% rename from java-algorithms/src/test/java/com/github/kuangcp/strcuture/stack/MythArrayStackTest.java rename to algorithms/src/test/java/com/github/kuangcp/strcuture/stack/MythArrayStackTest.java diff --git a/java-algorithms/src/test/java/com/github/kuangcp/strcuture/stack/MythLinkedStackTest.java b/algorithms/src/test/java/com/github/kuangcp/strcuture/stack/MythLinkedStackTest.java similarity index 100% rename from java-algorithms/src/test/java/com/github/kuangcp/strcuture/stack/MythLinkedStackTest.java rename to algorithms/src/test/java/com/github/kuangcp/strcuture/stack/MythLinkedStackTest.java diff --git a/java-algorithms/src/test/java/com/github/kuangcp/strcuture/stackapp/AddTwoBigIntegerTest.java b/algorithms/src/test/java/com/github/kuangcp/strcuture/stackapp/AddTwoBigIntegerTest.java similarity index 100% rename from java-algorithms/src/test/java/com/github/kuangcp/strcuture/stackapp/AddTwoBigIntegerTest.java rename to algorithms/src/test/java/com/github/kuangcp/strcuture/stackapp/AddTwoBigIntegerTest.java diff --git a/java-algorithms/src/test/java/com/github/kuangcp/strcuture/stackapp/MatchBracketTest.java b/algorithms/src/test/java/com/github/kuangcp/strcuture/stackapp/MatchBracketTest.java similarity index 100% rename from java-algorithms/src/test/java/com/github/kuangcp/strcuture/stackapp/MatchBracketTest.java rename to algorithms/src/test/java/com/github/kuangcp/strcuture/stackapp/MatchBracketTest.java diff --git a/java-algorithms/src/test/java/com/github/kuangcp/strcuture/stackapp/ShowStackPopTest.java b/algorithms/src/test/java/com/github/kuangcp/strcuture/stackapp/ShowStackPopTest.java similarity index 100% rename from java-algorithms/src/test/java/com/github/kuangcp/strcuture/stackapp/ShowStackPopTest.java rename to algorithms/src/test/java/com/github/kuangcp/strcuture/stackapp/ShowStackPopTest.java diff --git a/java-algorithms/src/test/java/com/github/kuangcp/strcuture/stackapp/SimulationQueue.java b/algorithms/src/test/java/com/github/kuangcp/strcuture/stackapp/SimulationQueue.java similarity index 100% rename from java-algorithms/src/test/java/com/github/kuangcp/strcuture/stackapp/SimulationQueue.java rename to algorithms/src/test/java/com/github/kuangcp/strcuture/stackapp/SimulationQueue.java diff --git a/java-class/README.md b/class/README.md similarity index 100% rename from java-class/README.md rename to class/README.md diff --git a/java-class/build.gradle b/class/build.gradle similarity index 100% rename from java-class/build.gradle rename to class/build.gradle diff --git a/java-class/src/main/java/com/github/kuangcp/README.md b/class/src/main/java/com/github/kuangcp/README.md similarity index 100% rename from java-class/src/main/java/com/github/kuangcp/README.md rename to class/src/main/java/com/github/kuangcp/README.md diff --git a/java-class/src/main/java/com/github/kuangcp/ValueCacheDemo.java b/class/src/main/java/com/github/kuangcp/ValueCacheDemo.java similarity index 100% rename from java-class/src/main/java/com/github/kuangcp/ValueCacheDemo.java rename to class/src/main/java/com/github/kuangcp/ValueCacheDemo.java diff --git a/java-class/src/main/java/com/github/kuangcp/annotation/First.java b/class/src/main/java/com/github/kuangcp/annotation/First.java similarity index 100% rename from java-class/src/main/java/com/github/kuangcp/annotation/First.java rename to class/src/main/java/com/github/kuangcp/annotation/First.java diff --git a/java-class/src/main/java/com/github/kuangcp/authorityscope/README.md b/class/src/main/java/com/github/kuangcp/authorityscope/README.md similarity index 100% rename from java-class/src/main/java/com/github/kuangcp/authorityscope/README.md rename to class/src/main/java/com/github/kuangcp/authorityscope/README.md diff --git a/java-class/src/main/java/com/github/kuangcp/authorityscope/package1/A.java b/class/src/main/java/com/github/kuangcp/authorityscope/package1/A.java similarity index 100% rename from java-class/src/main/java/com/github/kuangcp/authorityscope/package1/A.java rename to class/src/main/java/com/github/kuangcp/authorityscope/package1/A.java diff --git a/java-class/src/main/java/com/github/kuangcp/authorityscope/package1/B.java b/class/src/main/java/com/github/kuangcp/authorityscope/package1/B.java similarity index 100% rename from java-class/src/main/java/com/github/kuangcp/authorityscope/package1/B.java rename to class/src/main/java/com/github/kuangcp/authorityscope/package1/B.java diff --git a/java-class/src/main/java/com/github/kuangcp/authorityscope/package1/C.java b/class/src/main/java/com/github/kuangcp/authorityscope/package1/C.java similarity index 100% rename from java-class/src/main/java/com/github/kuangcp/authorityscope/package1/C.java rename to class/src/main/java/com/github/kuangcp/authorityscope/package1/C.java diff --git a/java-class/src/main/java/com/github/kuangcp/authorityscope/package2/D.java b/class/src/main/java/com/github/kuangcp/authorityscope/package2/D.java similarity index 100% rename from java-class/src/main/java/com/github/kuangcp/authorityscope/package2/D.java rename to class/src/main/java/com/github/kuangcp/authorityscope/package2/D.java diff --git a/java-class/src/main/java/com/github/kuangcp/authorityscope/package2/E.java b/class/src/main/java/com/github/kuangcp/authorityscope/package2/E.java similarity index 100% rename from java-class/src/main/java/com/github/kuangcp/authorityscope/package2/E.java rename to class/src/main/java/com/github/kuangcp/authorityscope/package2/E.java diff --git a/java-class/src/main/java/com/github/kuangcp/instantiation/ComplexConstructor.java b/class/src/main/java/com/github/kuangcp/instantiation/ComplexConstructor.java similarity index 100% rename from java-class/src/main/java/com/github/kuangcp/instantiation/ComplexConstructor.java rename to class/src/main/java/com/github/kuangcp/instantiation/ComplexConstructor.java diff --git a/java-class/src/main/java/com/github/kuangcp/instantiation/InstantiationAndConstructor.java b/class/src/main/java/com/github/kuangcp/instantiation/InstantiationAndConstructor.java similarity index 100% rename from java-class/src/main/java/com/github/kuangcp/instantiation/InstantiationAndConstructor.java rename to class/src/main/java/com/github/kuangcp/instantiation/InstantiationAndConstructor.java diff --git a/java-class/src/main/java/com/github/kuangcp/instantiation/Readme.md b/class/src/main/java/com/github/kuangcp/instantiation/Readme.md similarity index 100% rename from java-class/src/main/java/com/github/kuangcp/instantiation/Readme.md rename to class/src/main/java/com/github/kuangcp/instantiation/Readme.md diff --git a/java-class/src/main/java/com/github/kuangcp/instantiation/StaticFieldInit.java b/class/src/main/java/com/github/kuangcp/instantiation/StaticFieldInit.java similarity index 100% rename from java-class/src/main/java/com/github/kuangcp/instantiation/StaticFieldInit.java rename to class/src/main/java/com/github/kuangcp/instantiation/StaticFieldInit.java diff --git a/java-class/src/main/java/com/github/kuangcp/read/ClassScanner.java b/class/src/main/java/com/github/kuangcp/read/ClassScanner.java similarity index 100% rename from java-class/src/main/java/com/github/kuangcp/read/ClassScanner.java rename to class/src/main/java/com/github/kuangcp/read/ClassScanner.java diff --git a/java-class/src/main/java/com/github/kuangcp/reflects/ReflectTargetObject.java b/class/src/main/java/com/github/kuangcp/reflects/ReflectTargetObject.java similarity index 100% rename from java-class/src/main/java/com/github/kuangcp/reflects/ReflectTargetObject.java rename to class/src/main/java/com/github/kuangcp/reflects/ReflectTargetObject.java diff --git a/java-class/src/main/java/com/github/kuangcp/serialize/Person.java b/class/src/main/java/com/github/kuangcp/serialize/Person.java similarity index 100% rename from java-class/src/main/java/com/github/kuangcp/serialize/Person.java rename to class/src/main/java/com/github/kuangcp/serialize/Person.java diff --git a/java-class/src/main/java/com/github/kuangcp/serialize/customserialize/Myth.java b/class/src/main/java/com/github/kuangcp/serialize/customserialize/Myth.java similarity index 100% rename from java-class/src/main/java/com/github/kuangcp/serialize/customserialize/Myth.java rename to class/src/main/java/com/github/kuangcp/serialize/customserialize/Myth.java diff --git a/java-class/src/main/java/com/github/kuangcp/serialize/customserialize/MythSerialize.java b/class/src/main/java/com/github/kuangcp/serialize/customserialize/MythSerialize.java similarity index 100% rename from java-class/src/main/java/com/github/kuangcp/serialize/customserialize/MythSerialize.java rename to class/src/main/java/com/github/kuangcp/serialize/customserialize/MythSerialize.java diff --git a/java-class/src/main/java/com/github/kuangcp/serialize/json/JsonSmileMigrationService.java b/class/src/main/java/com/github/kuangcp/serialize/json/JsonSmileMigrationService.java similarity index 100% rename from java-class/src/main/java/com/github/kuangcp/serialize/json/JsonSmileMigrationService.java rename to class/src/main/java/com/github/kuangcp/serialize/json/JsonSmileMigrationService.java diff --git a/java-class/src/main/java/com/github/kuangcp/serialize/json/speed/FastJsonTool.java b/class/src/main/java/com/github/kuangcp/serialize/json/speed/FastJsonTool.java similarity index 100% rename from java-class/src/main/java/com/github/kuangcp/serialize/json/speed/FastJsonTool.java rename to class/src/main/java/com/github/kuangcp/serialize/json/speed/FastJsonTool.java diff --git a/java-class/src/main/java/com/github/kuangcp/serialize/json/speed/GsonTool.java b/class/src/main/java/com/github/kuangcp/serialize/json/speed/GsonTool.java similarity index 100% rename from java-class/src/main/java/com/github/kuangcp/serialize/json/speed/GsonTool.java rename to class/src/main/java/com/github/kuangcp/serialize/json/speed/GsonTool.java diff --git a/java-class/src/main/java/com/github/kuangcp/serialize/json/speed/JacksonTool.java b/class/src/main/java/com/github/kuangcp/serialize/json/speed/JacksonTool.java similarity index 100% rename from java-class/src/main/java/com/github/kuangcp/serialize/json/speed/JacksonTool.java rename to class/src/main/java/com/github/kuangcp/serialize/json/speed/JacksonTool.java diff --git a/java-class/src/main/java/com/github/kuangcp/serialize/json/speed/JsonTool.java b/class/src/main/java/com/github/kuangcp/serialize/json/speed/JsonTool.java similarity index 100% rename from java-class/src/main/java/com/github/kuangcp/serialize/json/speed/JsonTool.java rename to class/src/main/java/com/github/kuangcp/serialize/json/speed/JsonTool.java diff --git a/java-class/src/main/java/com/github/kuangcp/serialize/json/speed/README.md b/class/src/main/java/com/github/kuangcp/serialize/json/speed/README.md similarity index 100% rename from java-class/src/main/java/com/github/kuangcp/serialize/json/speed/README.md rename to class/src/main/java/com/github/kuangcp/serialize/json/speed/README.md diff --git a/java-class/src/main/java/com/github/kuangcp/util/ShowBinary.java b/class/src/main/java/com/github/kuangcp/util/ShowBinary.java similarity index 100% rename from java-class/src/main/java/com/github/kuangcp/util/ShowBinary.java rename to class/src/main/java/com/github/kuangcp/util/ShowBinary.java diff --git a/java-class/src/main/java/jmh/StringBuilderBenchmark.java b/class/src/main/java/jmh/StringBuilderBenchmark.java similarity index 100% rename from java-class/src/main/java/jmh/StringBuilderBenchmark.java rename to class/src/main/java/jmh/StringBuilderBenchmark.java diff --git a/java-class/src/main/java/jvm/oom/DirectMemoryOOM.java b/class/src/main/java/jvm/oom/DirectMemoryOOM.java similarity index 100% rename from java-class/src/main/java/jvm/oom/DirectMemoryOOM.java rename to class/src/main/java/jvm/oom/DirectMemoryOOM.java diff --git a/java-class/src/main/java/jvm/oom/HeapOOM.java b/class/src/main/java/jvm/oom/HeapOOM.java similarity index 100% rename from java-class/src/main/java/jvm/oom/HeapOOM.java rename to class/src/main/java/jvm/oom/HeapOOM.java diff --git a/java-class/src/main/java/jvm/oom/JvmStackOOM.java b/class/src/main/java/jvm/oom/JvmStackOOM.java similarity index 100% rename from java-class/src/main/java/jvm/oom/JvmStackOOM.java rename to class/src/main/java/jvm/oom/JvmStackOOM.java diff --git a/java-class/src/main/java/jvm/oom/JvmStackOverFlow.java b/class/src/main/java/jvm/oom/JvmStackOverFlow.java similarity index 100% rename from java-class/src/main/java/jvm/oom/JvmStackOverFlow.java rename to class/src/main/java/jvm/oom/JvmStackOverFlow.java diff --git a/java-class/src/main/java/jvm/oom/MethodAreaOOM.java b/class/src/main/java/jvm/oom/MethodAreaOOM.java similarity index 100% rename from java-class/src/main/java/jvm/oom/MethodAreaOOM.java rename to class/src/main/java/jvm/oom/MethodAreaOOM.java diff --git a/java-class/src/main/java/jvm/oom/Readme.md b/class/src/main/java/jvm/oom/Readme.md similarity index 100% rename from java-class/src/main/java/jvm/oom/Readme.md rename to class/src/main/java/jvm/oom/Readme.md diff --git a/java-class/src/main/java/jvm/oom/RunTimeConstantPoolOOM.java b/class/src/main/java/jvm/oom/RunTimeConstantPoolOOM.java similarity index 100% rename from java-class/src/main/java/jvm/oom/RunTimeConstantPoolOOM.java rename to class/src/main/java/jvm/oom/RunTimeConstantPoolOOM.java diff --git a/java-class/src/main/java/syntax/enums/Color.java b/class/src/main/java/syntax/enums/Color.java similarity index 100% rename from java-class/src/main/java/syntax/enums/Color.java rename to class/src/main/java/syntax/enums/Color.java diff --git a/java-class/src/main/java/syntax/enums/SingletonDemo.java b/class/src/main/java/syntax/enums/SingletonDemo.java similarity index 100% rename from java-class/src/main/java/syntax/enums/SingletonDemo.java rename to class/src/main/java/syntax/enums/SingletonDemo.java diff --git a/java-class/src/main/java/syntax/enums/package-info.java b/class/src/main/java/syntax/enums/package-info.java similarity index 100% rename from java-class/src/main/java/syntax/enums/package-info.java rename to class/src/main/java/syntax/enums/package-info.java diff --git a/java-class/src/main/java/syntax/package-info.java b/class/src/main/java/syntax/package-info.java similarity index 100% rename from java-class/src/main/java/syntax/package-info.java rename to class/src/main/java/syntax/package-info.java diff --git a/java-class/src/main/resources/logback.groovy.back b/class/src/main/resources/logback.groovy.back similarity index 100% rename from java-class/src/main/resources/logback.groovy.back rename to class/src/main/resources/logback.groovy.back diff --git a/java-class/src/main/resources/logback.xml b/class/src/main/resources/logback.xml similarity index 100% rename from java-class/src/main/resources/logback.xml rename to class/src/main/resources/logback.xml diff --git a/java-class/src/test/java/com/github/kuangcp/inherit/SameFieldTest.java b/class/src/test/java/com/github/kuangcp/inherit/SameFieldTest.java similarity index 100% rename from java-class/src/test/java/com/github/kuangcp/inherit/SameFieldTest.java rename to class/src/test/java/com/github/kuangcp/inherit/SameFieldTest.java diff --git a/java-class/src/test/java/com/github/kuangcp/instantiation/ComplexConstructorTest.java b/class/src/test/java/com/github/kuangcp/instantiation/ComplexConstructorTest.java similarity index 100% rename from java-class/src/test/java/com/github/kuangcp/instantiation/ComplexConstructorTest.java rename to class/src/test/java/com/github/kuangcp/instantiation/ComplexConstructorTest.java diff --git a/java-class/src/test/java/com/github/kuangcp/instantiation/InstantiationAndConstructorTest.java b/class/src/test/java/com/github/kuangcp/instantiation/InstantiationAndConstructorTest.java similarity index 100% rename from java-class/src/test/java/com/github/kuangcp/instantiation/InstantiationAndConstructorTest.java rename to class/src/test/java/com/github/kuangcp/instantiation/InstantiationAndConstructorTest.java diff --git a/java-class/src/test/java/com/github/kuangcp/instantiation/StaticFieldInitTest.java b/class/src/test/java/com/github/kuangcp/instantiation/StaticFieldInitTest.java similarity index 100% rename from java-class/src/test/java/com/github/kuangcp/instantiation/StaticFieldInitTest.java rename to class/src/test/java/com/github/kuangcp/instantiation/StaticFieldInitTest.java diff --git a/java-class/src/test/java/com/github/kuangcp/loader/ClassInitialTest.java b/class/src/test/java/com/github/kuangcp/loader/ClassInitialTest.java similarity index 100% rename from java-class/src/test/java/com/github/kuangcp/loader/ClassInitialTest.java rename to class/src/test/java/com/github/kuangcp/loader/ClassInitialTest.java diff --git a/java-class/src/test/java/com/github/kuangcp/loader/InstantiationSortTest.java b/class/src/test/java/com/github/kuangcp/loader/InstantiationSortTest.java similarity index 100% rename from java-class/src/test/java/com/github/kuangcp/loader/InstantiationSortTest.java rename to class/src/test/java/com/github/kuangcp/loader/InstantiationSortTest.java diff --git a/java-class/src/test/java/com/github/kuangcp/read/BaseFather.java b/class/src/test/java/com/github/kuangcp/read/BaseFather.java similarity index 100% rename from java-class/src/test/java/com/github/kuangcp/read/BaseFather.java rename to class/src/test/java/com/github/kuangcp/read/BaseFather.java diff --git a/java-class/src/test/java/com/github/kuangcp/read/ClassScannerTest.java b/class/src/test/java/com/github/kuangcp/read/ClassScannerTest.java similarity index 100% rename from java-class/src/test/java/com/github/kuangcp/read/ClassScannerTest.java rename to class/src/test/java/com/github/kuangcp/read/ClassScannerTest.java diff --git a/java-class/src/test/java/com/github/kuangcp/read/demo/FirstBase.java b/class/src/test/java/com/github/kuangcp/read/demo/FirstBase.java similarity index 100% rename from java-class/src/test/java/com/github/kuangcp/read/demo/FirstBase.java rename to class/src/test/java/com/github/kuangcp/read/demo/FirstBase.java diff --git a/java-class/src/test/java/com/github/kuangcp/read/demo/SeconedBase.java b/class/src/test/java/com/github/kuangcp/read/demo/SeconedBase.java similarity index 100% rename from java-class/src/test/java/com/github/kuangcp/read/demo/SeconedBase.java rename to class/src/test/java/com/github/kuangcp/read/demo/SeconedBase.java diff --git a/java-class/src/test/java/com/github/kuangcp/read/demo/son/ThirdBase.java b/class/src/test/java/com/github/kuangcp/read/demo/son/ThirdBase.java similarity index 100% rename from java-class/src/test/java/com/github/kuangcp/read/demo/son/ThirdBase.java rename to class/src/test/java/com/github/kuangcp/read/demo/son/ThirdBase.java diff --git a/java-class/src/test/java/com/github/kuangcp/reflects/InvokeWithInheritParamTest.java b/class/src/test/java/com/github/kuangcp/reflects/InvokeWithInheritParamTest.java similarity index 100% rename from java-class/src/test/java/com/github/kuangcp/reflects/InvokeWithInheritParamTest.java rename to class/src/test/java/com/github/kuangcp/reflects/InvokeWithInheritParamTest.java diff --git a/java-class/src/test/java/com/github/kuangcp/reflects/ReflectPerformanceTest.java b/class/src/test/java/com/github/kuangcp/reflects/ReflectPerformanceTest.java similarity index 100% rename from java-class/src/test/java/com/github/kuangcp/reflects/ReflectPerformanceTest.java rename to class/src/test/java/com/github/kuangcp/reflects/ReflectPerformanceTest.java diff --git a/java-class/src/test/java/com/github/kuangcp/reflects/ReflectTargetObjectTest.java b/class/src/test/java/com/github/kuangcp/reflects/ReflectTargetObjectTest.java similarity index 100% rename from java-class/src/test/java/com/github/kuangcp/reflects/ReflectTargetObjectTest.java rename to class/src/test/java/com/github/kuangcp/reflects/ReflectTargetObjectTest.java diff --git a/java-class/src/test/java/com/github/kuangcp/serialize/SerializeTest.java b/class/src/test/java/com/github/kuangcp/serialize/SerializeTest.java similarity index 100% rename from java-class/src/test/java/com/github/kuangcp/serialize/SerializeTest.java rename to class/src/test/java/com/github/kuangcp/serialize/SerializeTest.java diff --git a/java-class/src/test/java/com/github/kuangcp/serialize/customserialize/MythSerializeTest.java b/class/src/test/java/com/github/kuangcp/serialize/customserialize/MythSerializeTest.java similarity index 100% rename from java-class/src/test/java/com/github/kuangcp/serialize/customserialize/MythSerializeTest.java rename to class/src/test/java/com/github/kuangcp/serialize/customserialize/MythSerializeTest.java diff --git a/java-class/src/test/java/com/github/kuangcp/serialize/json/GsonTest.java b/class/src/test/java/com/github/kuangcp/serialize/json/GsonTest.java similarity index 100% rename from java-class/src/test/java/com/github/kuangcp/serialize/json/GsonTest.java rename to class/src/test/java/com/github/kuangcp/serialize/json/GsonTest.java diff --git a/java-class/src/test/java/com/github/kuangcp/serialize/json/SpeedTest.java b/class/src/test/java/com/github/kuangcp/serialize/json/SpeedTest.java similarity index 100% rename from java-class/src/test/java/com/github/kuangcp/serialize/json/SpeedTest.java rename to class/src/test/java/com/github/kuangcp/serialize/json/SpeedTest.java diff --git a/java-class/src/test/java/jmh/StringBuilderBenchmarkTest.java b/class/src/test/java/jmh/StringBuilderBenchmarkTest.java similarity index 100% rename from java-class/src/test/java/jmh/StringBuilderBenchmarkTest.java rename to class/src/test/java/jmh/StringBuilderBenchmarkTest.java diff --git a/java-class/src/test/java/jvm/oom/HeapOOMTest.java b/class/src/test/java/jvm/oom/HeapOOMTest.java similarity index 100% rename from java-class/src/test/java/jvm/oom/HeapOOMTest.java rename to class/src/test/java/jvm/oom/HeapOOMTest.java diff --git a/java-class/src/test/java/log/CorrectLogTest.java b/class/src/test/java/log/CorrectLogTest.java similarity index 100% rename from java-class/src/test/java/log/CorrectLogTest.java rename to class/src/test/java/log/CorrectLogTest.java diff --git a/java-class/src/test/java/math/RandomTest.java b/class/src/test/java/math/RandomTest.java similarity index 100% rename from java-class/src/test/java/math/RandomTest.java rename to class/src/test/java/math/RandomTest.java diff --git a/java-class/src/test/java/syntax/InstanceofTest.java b/class/src/test/java/syntax/InstanceofTest.java similarity index 100% rename from java-class/src/test/java/syntax/InstanceofTest.java rename to class/src/test/java/syntax/InstanceofTest.java diff --git a/java-class/src/test/java/syntax/base/method/EqualsAndHashCodeTest.java b/class/src/test/java/syntax/base/method/EqualsAndHashCodeTest.java similarity index 100% rename from java-class/src/test/java/syntax/base/method/EqualsAndHashCodeTest.java rename to class/src/test/java/syntax/base/method/EqualsAndHashCodeTest.java diff --git a/java-class/src/test/java/syntax/base/method/ParamTransferTest.java b/class/src/test/java/syntax/base/method/ParamTransferTest.java similarity index 100% rename from java-class/src/test/java/syntax/base/method/ParamTransferTest.java rename to class/src/test/java/syntax/base/method/ParamTransferTest.java diff --git a/java-class/src/test/java/syntax/basetype/BaseTypeDefaultValueTest.java b/class/src/test/java/syntax/basetype/BaseTypeDefaultValueTest.java similarity index 100% rename from java-class/src/test/java/syntax/basetype/BaseTypeDefaultValueTest.java rename to class/src/test/java/syntax/basetype/BaseTypeDefaultValueTest.java diff --git a/java-class/src/test/java/syntax/bit/BitOperatorsTest.java b/class/src/test/java/syntax/bit/BitOperatorsTest.java similarity index 100% rename from java-class/src/test/java/syntax/bit/BitOperatorsTest.java rename to class/src/test/java/syntax/bit/BitOperatorsTest.java diff --git a/java-class/src/test/java/syntax/bit/StringPerformanceTest.java b/class/src/test/java/syntax/bit/StringPerformanceTest.java similarity index 100% rename from java-class/src/test/java/syntax/bit/StringPerformanceTest.java rename to class/src/test/java/syntax/bit/StringPerformanceTest.java diff --git a/java-class/src/test/java/syntax/bytes/OperatorTest.java b/class/src/test/java/syntax/bytes/OperatorTest.java similarity index 100% rename from java-class/src/test/java/syntax/bytes/OperatorTest.java rename to class/src/test/java/syntax/bytes/OperatorTest.java diff --git a/java-class/src/test/java/syntax/doubles/DoubleConstTest.java b/class/src/test/java/syntax/doubles/DoubleConstTest.java similarity index 100% rename from java-class/src/test/java/syntax/doubles/DoubleConstTest.java rename to class/src/test/java/syntax/doubles/DoubleConstTest.java diff --git a/java-class/src/test/java/syntax/expression/ShortCircuitTest.java b/class/src/test/java/syntax/expression/ShortCircuitTest.java similarity index 100% rename from java-class/src/test/java/syntax/expression/ShortCircuitTest.java rename to class/src/test/java/syntax/expression/ShortCircuitTest.java diff --git a/java-class/src/test/java/syntax/floats/AccuracyLoseTest.java b/class/src/test/java/syntax/floats/AccuracyLoseTest.java similarity index 100% rename from java-class/src/test/java/syntax/floats/AccuracyLoseTest.java rename to class/src/test/java/syntax/floats/AccuracyLoseTest.java diff --git a/java-class/src/test/java/syntax/innerclass/ServerConfig.java b/class/src/test/java/syntax/innerclass/ServerConfig.java similarity index 100% rename from java-class/src/test/java/syntax/innerclass/ServerConfig.java rename to class/src/test/java/syntax/innerclass/ServerConfig.java diff --git a/java-class/src/test/java/syntax/integer/IntegerCacheTest.java b/class/src/test/java/syntax/integer/IntegerCacheTest.java similarity index 100% rename from java-class/src/test/java/syntax/integer/IntegerCacheTest.java rename to class/src/test/java/syntax/integer/IntegerCacheTest.java diff --git a/java-class/src/test/java/syntax/longs/LongTest.java b/class/src/test/java/syntax/longs/LongTest.java similarity index 100% rename from java-class/src/test/java/syntax/longs/LongTest.java rename to class/src/test/java/syntax/longs/LongTest.java diff --git a/java-class/src/test/java/syntax/package-info.java b/class/src/test/java/syntax/package-info.java similarity index 100% rename from java-class/src/test/java/syntax/package-info.java rename to class/src/test/java/syntax/package-info.java diff --git a/java-class/src/test/java/syntax/string/SplitDemoTest.java b/class/src/test/java/syntax/string/SplitDemoTest.java similarity index 100% rename from java-class/src/test/java/syntax/string/SplitDemoTest.java rename to class/src/test/java/syntax/string/SplitDemoTest.java diff --git a/java-class/src/test/java/syntax/string/StringTest.java b/class/src/test/java/syntax/string/StringTest.java similarity index 100% rename from java-class/src/test/java/syntax/string/StringTest.java rename to class/src/test/java/syntax/string/StringTest.java diff --git a/java-class/src/test/java/syntax/tryblock/FinallyWithReturn.java b/class/src/test/java/syntax/tryblock/FinallyWithReturn.java similarity index 100% rename from java-class/src/test/java/syntax/tryblock/FinallyWithReturn.java rename to class/src/test/java/syntax/tryblock/FinallyWithReturn.java diff --git a/java-class/src/test/java/syntax/tryblock/TWR.java b/class/src/test/java/syntax/tryblock/TWR.java similarity index 100% rename from java-class/src/test/java/syntax/tryblock/TWR.java rename to class/src/test/java/syntax/tryblock/TWR.java diff --git a/java-class/src/test/java/time/InstantTest.java b/class/src/test/java/time/InstantTest.java similarity index 100% rename from java-class/src/test/java/time/InstantTest.java rename to class/src/test/java/time/InstantTest.java diff --git a/java-class/src/test/java/time/LocalDateTest.java b/class/src/test/java/time/LocalDateTest.java similarity index 100% rename from java-class/src/test/java/time/LocalDateTest.java rename to class/src/test/java/time/LocalDateTest.java diff --git a/java-class/src/test/java/time/LocalDateTimeTest.java b/class/src/test/java/time/LocalDateTimeTest.java similarity index 100% rename from java-class/src/test/java/time/LocalDateTimeTest.java rename to class/src/test/java/time/LocalDateTimeTest.java diff --git a/java-class/src/test/java/time/OffsetTest.java b/class/src/test/java/time/OffsetTest.java similarity index 100% rename from java-class/src/test/java/time/OffsetTest.java rename to class/src/test/java/time/OffsetTest.java diff --git a/java-class/src/test/java/time/Readme.md b/class/src/test/java/time/Readme.md similarity index 100% rename from java-class/src/test/java/time/Readme.md rename to class/src/test/java/time/Readme.md diff --git a/java-collection/build.gradle b/collection/build.gradle similarity index 100% rename from java-collection/build.gradle rename to collection/build.gradle diff --git a/java-collection/src/test/java/README.md b/collection/src/test/java/README.md similarity index 100% rename from java-collection/src/test/java/README.md rename to collection/src/test/java/README.md diff --git a/java-collection/src/test/java/com/github/kuangcp/list/ConcurrentModificationTest.java b/collection/src/test/java/com/github/kuangcp/list/ConcurrentModificationTest.java similarity index 100% rename from java-collection/src/test/java/com/github/kuangcp/list/ConcurrentModificationTest.java rename to collection/src/test/java/com/github/kuangcp/list/ConcurrentModificationTest.java diff --git a/java-collection/src/test/java/com/github/kuangcp/list/ListWithArrayTest.java b/collection/src/test/java/com/github/kuangcp/list/ListWithArrayTest.java similarity index 100% rename from java-collection/src/test/java/com/github/kuangcp/list/ListWithArrayTest.java rename to collection/src/test/java/com/github/kuangcp/list/ListWithArrayTest.java diff --git a/java-collection/src/test/java/com/github/kuangcp/list/OperationTest.java b/collection/src/test/java/com/github/kuangcp/list/OperationTest.java similarity index 100% rename from java-collection/src/test/java/com/github/kuangcp/list/OperationTest.java rename to collection/src/test/java/com/github/kuangcp/list/OperationTest.java diff --git a/java-collection/src/test/java/com/github/kuangcp/map/CurrentModificationTest.java b/collection/src/test/java/com/github/kuangcp/map/CurrentModificationTest.java similarity index 100% rename from java-collection/src/test/java/com/github/kuangcp/map/CurrentModificationTest.java rename to collection/src/test/java/com/github/kuangcp/map/CurrentModificationTest.java diff --git a/java-collection/src/test/java/com/github/kuangcp/map/HashMapTest.java b/collection/src/test/java/com/github/kuangcp/map/HashMapTest.java similarity index 100% rename from java-collection/src/test/java/com/github/kuangcp/map/HashMapTest.java rename to collection/src/test/java/com/github/kuangcp/map/HashMapTest.java diff --git a/java-collection/src/test/java/com/github/kuangcp/map/IterateMapTest.java b/collection/src/test/java/com/github/kuangcp/map/IterateMapTest.java similarity index 100% rename from java-collection/src/test/java/com/github/kuangcp/map/IterateMapTest.java rename to collection/src/test/java/com/github/kuangcp/map/IterateMapTest.java diff --git a/java-collection/src/test/java/com/github/kuangcp/map/README.md b/collection/src/test/java/com/github/kuangcp/map/README.md similarity index 100% rename from java-collection/src/test/java/com/github/kuangcp/map/README.md rename to collection/src/test/java/com/github/kuangcp/map/README.md diff --git a/java-collection/src/test/java/com/github/kuangcp/map/RandomGetValueTest.java b/collection/src/test/java/com/github/kuangcp/map/RandomGetValueTest.java similarity index 100% rename from java-collection/src/test/java/com/github/kuangcp/map/RandomGetValueTest.java rename to collection/src/test/java/com/github/kuangcp/map/RandomGetValueTest.java diff --git a/java-collection/src/test/java/com/github/kuangcp/set/HashSetTest.java b/collection/src/test/java/com/github/kuangcp/set/HashSetTest.java similarity index 100% rename from java-collection/src/test/java/com/github/kuangcp/set/HashSetTest.java rename to collection/src/test/java/com/github/kuangcp/set/HashSetTest.java diff --git a/java-collection/src/test/java/com/github/kuangcp/set/TreeSetTest.java b/collection/src/test/java/com/github/kuangcp/set/TreeSetTest.java similarity index 100% rename from java-collection/src/test/java/com/github/kuangcp/set/TreeSetTest.java rename to collection/src/test/java/com/github/kuangcp/set/TreeSetTest.java diff --git a/java-concurrency/Readme.md b/concurrency/Readme.md similarity index 100% rename from java-concurrency/Readme.md rename to concurrency/Readme.md diff --git a/java-concurrency/build.gradle b/concurrency/build.gradle similarity index 100% rename from java-concurrency/build.gradle rename to concurrency/build.gradle diff --git a/java-concurrency/src/main/java/com/github/kuangcp/README.md b/concurrency/src/main/java/com/github/kuangcp/README.md similarity index 100% rename from java-concurrency/src/main/java/com/github/kuangcp/README.md rename to concurrency/src/main/java/com/github/kuangcp/README.md diff --git a/java-concurrency/src/main/java/com/github/kuangcp/countlatch/CountLatchDemo.java b/concurrency/src/main/java/com/github/kuangcp/countlatch/CountLatchDemo.java similarity index 100% rename from java-concurrency/src/main/java/com/github/kuangcp/countlatch/CountLatchDemo.java rename to concurrency/src/main/java/com/github/kuangcp/countlatch/CountLatchDemo.java diff --git a/java-concurrency/src/main/java/com/github/kuangcp/countlatch/ProcessingThread.java b/concurrency/src/main/java/com/github/kuangcp/countlatch/ProcessingThread.java similarity index 100% rename from java-concurrency/src/main/java/com/github/kuangcp/countlatch/ProcessingThread.java rename to concurrency/src/main/java/com/github/kuangcp/countlatch/ProcessingThread.java diff --git a/java-concurrency/src/main/java/com/github/kuangcp/forkjoin/Counter.groovy b/concurrency/src/main/java/com/github/kuangcp/forkjoin/Counter.groovy similarity index 100% rename from java-concurrency/src/main/java/com/github/kuangcp/forkjoin/Counter.groovy rename to concurrency/src/main/java/com/github/kuangcp/forkjoin/Counter.groovy diff --git a/java-concurrency/src/main/java/com/github/kuangcp/forkjoin/Element.groovy b/concurrency/src/main/java/com/github/kuangcp/forkjoin/Element.groovy similarity index 100% rename from java-concurrency/src/main/java/com/github/kuangcp/forkjoin/Element.groovy rename to concurrency/src/main/java/com/github/kuangcp/forkjoin/Element.groovy diff --git a/java-concurrency/src/main/java/com/github/kuangcp/forkjoin/ElementSorter.groovy b/concurrency/src/main/java/com/github/kuangcp/forkjoin/ElementSorter.groovy similarity index 100% rename from java-concurrency/src/main/java/com/github/kuangcp/forkjoin/ElementSorter.groovy rename to concurrency/src/main/java/com/github/kuangcp/forkjoin/ElementSorter.groovy diff --git a/java-concurrency/src/main/java/com/github/kuangcp/forkjoin/ForkJoinEasyDemo.groovy b/concurrency/src/main/java/com/github/kuangcp/forkjoin/ForkJoinEasyDemo.groovy similarity index 100% rename from java-concurrency/src/main/java/com/github/kuangcp/forkjoin/ForkJoinEasyDemo.groovy rename to concurrency/src/main/java/com/github/kuangcp/forkjoin/ForkJoinEasyDemo.groovy diff --git a/java-concurrency/src/main/java/com/github/kuangcp/forkjoin/Readme.md b/concurrency/src/main/java/com/github/kuangcp/forkjoin/Readme.md similarity index 100% rename from java-concurrency/src/main/java/com/github/kuangcp/forkjoin/Readme.md rename to concurrency/src/main/java/com/github/kuangcp/forkjoin/Readme.md diff --git a/java-concurrency/src/main/java/com/github/kuangcp/forkjoin/SynchronizedDemo.groovy b/concurrency/src/main/java/com/github/kuangcp/forkjoin/SynchronizedDemo.groovy similarity index 100% rename from java-concurrency/src/main/java/com/github/kuangcp/forkjoin/SynchronizedDemo.groovy rename to concurrency/src/main/java/com/github/kuangcp/forkjoin/SynchronizedDemo.groovy diff --git a/java-concurrency/src/main/java/com/github/kuangcp/list/ArrayListDemo.java b/concurrency/src/main/java/com/github/kuangcp/list/ArrayListDemo.java similarity index 100% rename from java-concurrency/src/main/java/com/github/kuangcp/list/ArrayListDemo.java rename to concurrency/src/main/java/com/github/kuangcp/list/ArrayListDemo.java diff --git a/java-concurrency/src/main/java/com/github/kuangcp/list/CopyOnWriteDemo.java b/concurrency/src/main/java/com/github/kuangcp/list/CopyOnWriteDemo.java similarity index 100% rename from java-concurrency/src/main/java/com/github/kuangcp/list/CopyOnWriteDemo.java rename to concurrency/src/main/java/com/github/kuangcp/list/CopyOnWriteDemo.java diff --git a/java-concurrency/src/main/java/com/github/kuangcp/list/Element.java b/concurrency/src/main/java/com/github/kuangcp/list/Element.java similarity index 100% rename from java-concurrency/src/main/java/com/github/kuangcp/list/Element.java rename to concurrency/src/main/java/com/github/kuangcp/list/Element.java diff --git a/java-concurrency/src/main/java/com/github/kuangcp/list/ElementArrayList.java b/concurrency/src/main/java/com/github/kuangcp/list/ElementArrayList.java similarity index 100% rename from java-concurrency/src/main/java/com/github/kuangcp/list/ElementArrayList.java rename to concurrency/src/main/java/com/github/kuangcp/list/ElementArrayList.java diff --git a/java-concurrency/src/main/java/com/github/kuangcp/list/ElementList.java b/concurrency/src/main/java/com/github/kuangcp/list/ElementList.java similarity index 100% rename from java-concurrency/src/main/java/com/github/kuangcp/list/ElementList.java rename to concurrency/src/main/java/com/github/kuangcp/list/ElementList.java diff --git a/java-concurrency/src/main/java/com/github/kuangcp/list/ShowCopyOnWrite.java b/concurrency/src/main/java/com/github/kuangcp/list/ShowCopyOnWrite.java similarity index 100% rename from java-concurrency/src/main/java/com/github/kuangcp/list/ShowCopyOnWrite.java rename to concurrency/src/main/java/com/github/kuangcp/list/ShowCopyOnWrite.java diff --git a/java-concurrency/src/main/java/com/github/kuangcp/lock/dead/DeadLock.java b/concurrency/src/main/java/com/github/kuangcp/lock/dead/DeadLock.java similarity index 100% rename from java-concurrency/src/main/java/com/github/kuangcp/lock/dead/DeadLock.java rename to concurrency/src/main/java/com/github/kuangcp/lock/dead/DeadLock.java diff --git a/java-concurrency/src/main/java/com/github/kuangcp/lock/dead/DeadLockDemo.java b/concurrency/src/main/java/com/github/kuangcp/lock/dead/DeadLockDemo.java similarity index 100% rename from java-concurrency/src/main/java/com/github/kuangcp/lock/dead/DeadLockDemo.java rename to concurrency/src/main/java/com/github/kuangcp/lock/dead/DeadLockDemo.java diff --git a/java-concurrency/src/main/java/com/github/kuangcp/lock/dead/DeadLockHandler.java b/concurrency/src/main/java/com/github/kuangcp/lock/dead/DeadLockHandler.java similarity index 100% rename from java-concurrency/src/main/java/com/github/kuangcp/lock/dead/DeadLockHandler.java rename to concurrency/src/main/java/com/github/kuangcp/lock/dead/DeadLockHandler.java diff --git a/java-concurrency/src/main/java/com/github/kuangcp/lock/dead/DeadLockHandlerDemo.java b/concurrency/src/main/java/com/github/kuangcp/lock/dead/DeadLockHandlerDemo.java similarity index 100% rename from java-concurrency/src/main/java/com/github/kuangcp/lock/dead/DeadLockHandlerDemo.java rename to concurrency/src/main/java/com/github/kuangcp/lock/dead/DeadLockHandlerDemo.java diff --git a/java-concurrency/src/main/java/com/github/kuangcp/lock/dead/DeadLockRewrite.java b/concurrency/src/main/java/com/github/kuangcp/lock/dead/DeadLockRewrite.java similarity index 100% rename from java-concurrency/src/main/java/com/github/kuangcp/lock/dead/DeadLockRewrite.java rename to concurrency/src/main/java/com/github/kuangcp/lock/dead/DeadLockRewrite.java diff --git a/java-concurrency/src/main/java/com/github/kuangcp/lock/dead/DeadLockRewriteDemo.java b/concurrency/src/main/java/com/github/kuangcp/lock/dead/DeadLockRewriteDemo.java similarity index 100% rename from java-concurrency/src/main/java/com/github/kuangcp/lock/dead/DeadLockRewriteDemo.java rename to concurrency/src/main/java/com/github/kuangcp/lock/dead/DeadLockRewriteDemo.java diff --git a/java-concurrency/src/main/java/com/github/kuangcp/lock/pvp/Player.java b/concurrency/src/main/java/com/github/kuangcp/lock/pvp/Player.java similarity index 100% rename from java-concurrency/src/main/java/com/github/kuangcp/lock/pvp/Player.java rename to concurrency/src/main/java/com/github/kuangcp/lock/pvp/Player.java diff --git a/java-concurrency/src/main/java/com/github/kuangcp/lock/sync/Action.java b/concurrency/src/main/java/com/github/kuangcp/lock/sync/Action.java similarity index 100% rename from java-concurrency/src/main/java/com/github/kuangcp/lock/sync/Action.java rename to concurrency/src/main/java/com/github/kuangcp/lock/sync/Action.java diff --git a/java-concurrency/src/main/java/com/github/kuangcp/lock/sync/Readme.md b/concurrency/src/main/java/com/github/kuangcp/lock/sync/Readme.md similarity index 100% rename from java-concurrency/src/main/java/com/github/kuangcp/lock/sync/Readme.md rename to concurrency/src/main/java/com/github/kuangcp/lock/sync/Readme.md diff --git a/java-concurrency/src/main/java/com/github/kuangcp/lock/sync/SleepDemo.java b/concurrency/src/main/java/com/github/kuangcp/lock/sync/SleepDemo.java similarity index 100% rename from java-concurrency/src/main/java/com/github/kuangcp/lock/sync/SleepDemo.java rename to concurrency/src/main/java/com/github/kuangcp/lock/sync/SleepDemo.java diff --git a/java-concurrency/src/main/java/com/github/kuangcp/old/BuildFactory.java b/concurrency/src/main/java/com/github/kuangcp/old/BuildFactory.java similarity index 100% rename from java-concurrency/src/main/java/com/github/kuangcp/old/BuildFactory.java rename to concurrency/src/main/java/com/github/kuangcp/old/BuildFactory.java diff --git a/java-concurrency/src/main/java/com/github/kuangcp/old/CreateObjByBuilder.groovy b/concurrency/src/main/java/com/github/kuangcp/old/CreateObjByBuilder.groovy similarity index 100% rename from java-concurrency/src/main/java/com/github/kuangcp/old/CreateObjByBuilder.groovy rename to concurrency/src/main/java/com/github/kuangcp/old/CreateObjByBuilder.groovy diff --git a/java-concurrency/src/main/java/com/github/kuangcp/old/DeadLock.java b/concurrency/src/main/java/com/github/kuangcp/old/DeadLock.java similarity index 100% rename from java-concurrency/src/main/java/com/github/kuangcp/old/DeadLock.java rename to concurrency/src/main/java/com/github/kuangcp/old/DeadLock.java diff --git a/java-concurrency/src/main/java/com/github/kuangcp/old/DeadLockShow.java b/concurrency/src/main/java/com/github/kuangcp/old/DeadLockShow.java similarity index 100% rename from java-concurrency/src/main/java/com/github/kuangcp/old/DeadLockShow.java rename to concurrency/src/main/java/com/github/kuangcp/old/DeadLockShow.java diff --git a/java-concurrency/src/main/java/com/github/kuangcp/old/Food.java b/concurrency/src/main/java/com/github/kuangcp/old/Food.java similarity index 100% rename from java-concurrency/src/main/java/com/github/kuangcp/old/Food.java rename to concurrency/src/main/java/com/github/kuangcp/old/Food.java diff --git a/java-concurrency/src/main/java/com/github/kuangcp/old/MainThread.java b/concurrency/src/main/java/com/github/kuangcp/old/MainThread.java similarity index 100% rename from java-concurrency/src/main/java/com/github/kuangcp/old/MainThread.java rename to concurrency/src/main/java/com/github/kuangcp/old/MainThread.java diff --git a/java-concurrency/src/main/java/com/github/kuangcp/old/ObjBuilder.java b/concurrency/src/main/java/com/github/kuangcp/old/ObjBuilder.java similarity index 100% rename from java-concurrency/src/main/java/com/github/kuangcp/old/ObjBuilder.java rename to concurrency/src/main/java/com/github/kuangcp/old/ObjBuilder.java diff --git a/java-concurrency/src/main/java/com/github/kuangcp/old/PrefectSynchronized.java b/concurrency/src/main/java/com/github/kuangcp/old/PrefectSynchronized.java similarity index 100% rename from java-concurrency/src/main/java/com/github/kuangcp/old/PrefectSynchronized.java rename to concurrency/src/main/java/com/github/kuangcp/old/PrefectSynchronized.java diff --git a/java-concurrency/src/main/java/com/github/kuangcp/queue/ShowMicro.groovy b/concurrency/src/main/java/com/github/kuangcp/queue/ShowMicro.groovy similarity index 100% rename from java-concurrency/src/main/java/com/github/kuangcp/queue/ShowMicro.groovy rename to concurrency/src/main/java/com/github/kuangcp/queue/ShowMicro.groovy diff --git a/java-concurrency/src/main/java/com/github/kuangcp/queue/use/blocking/Appointment.java b/concurrency/src/main/java/com/github/kuangcp/queue/use/blocking/Appointment.java similarity index 100% rename from java-concurrency/src/main/java/com/github/kuangcp/queue/use/blocking/Appointment.java rename to concurrency/src/main/java/com/github/kuangcp/queue/use/blocking/Appointment.java diff --git a/java-concurrency/src/main/java/com/github/kuangcp/queue/use/blocking/Cat.java b/concurrency/src/main/java/com/github/kuangcp/queue/use/blocking/Cat.java similarity index 100% rename from java-concurrency/src/main/java/com/github/kuangcp/queue/use/blocking/Cat.java rename to concurrency/src/main/java/com/github/kuangcp/queue/use/blocking/Cat.java diff --git a/java-concurrency/src/main/java/com/github/kuangcp/queue/use/blocking/Dog.java b/concurrency/src/main/java/com/github/kuangcp/queue/use/blocking/Dog.java similarity index 100% rename from java-concurrency/src/main/java/com/github/kuangcp/queue/use/blocking/Dog.java rename to concurrency/src/main/java/com/github/kuangcp/queue/use/blocking/Dog.java diff --git a/java-concurrency/src/main/java/com/github/kuangcp/queue/use/blocking/Pet.java b/concurrency/src/main/java/com/github/kuangcp/queue/use/blocking/Pet.java similarity index 100% rename from java-concurrency/src/main/java/com/github/kuangcp/queue/use/blocking/Pet.java rename to concurrency/src/main/java/com/github/kuangcp/queue/use/blocking/Pet.java diff --git a/java-concurrency/src/main/java/com/github/kuangcp/queue/use/blocking/Veterinarian.java b/concurrency/src/main/java/com/github/kuangcp/queue/use/blocking/Veterinarian.java similarity index 100% rename from java-concurrency/src/main/java/com/github/kuangcp/queue/use/blocking/Veterinarian.java rename to concurrency/src/main/java/com/github/kuangcp/queue/use/blocking/Veterinarian.java diff --git a/java-concurrency/src/main/java/com/github/kuangcp/schedule/CreateModel.groovy b/concurrency/src/main/java/com/github/kuangcp/schedule/CreateModel.groovy similarity index 100% rename from java-concurrency/src/main/java/com/github/kuangcp/schedule/CreateModel.groovy rename to concurrency/src/main/java/com/github/kuangcp/schedule/CreateModel.groovy diff --git a/java-concurrency/src/main/java/com/github/kuangcp/schedule/ShowSTPE.groovy b/concurrency/src/main/java/com/github/kuangcp/schedule/ShowSTPE.groovy similarity index 100% rename from java-concurrency/src/main/java/com/github/kuangcp/schedule/ShowSTPE.groovy rename to concurrency/src/main/java/com/github/kuangcp/schedule/ShowSTPE.groovy diff --git a/java-concurrency/src/main/java/com/github/kuangcp/sync/Cache.groovy b/concurrency/src/main/java/com/github/kuangcp/sync/Cache.groovy similarity index 100% rename from java-concurrency/src/main/java/com/github/kuangcp/sync/Cache.groovy rename to concurrency/src/main/java/com/github/kuangcp/sync/Cache.groovy diff --git a/java-concurrency/src/main/java/com/github/kuangcp/sync/SyncField.groovy b/concurrency/src/main/java/com/github/kuangcp/sync/SyncField.groovy similarity index 100% rename from java-concurrency/src/main/java/com/github/kuangcp/sync/SyncField.groovy rename to concurrency/src/main/java/com/github/kuangcp/sync/SyncField.groovy diff --git a/java-concurrency/src/main/java/com/github/kuangcp/sync/package-info.java b/concurrency/src/main/java/com/github/kuangcp/sync/package-info.java similarity index 100% rename from java-concurrency/src/main/java/com/github/kuangcp/sync/package-info.java rename to concurrency/src/main/java/com/github/kuangcp/sync/package-info.java diff --git a/java-concurrency/src/main/java/com/github/kuangcp/volatiles/NeverStopThread.java b/concurrency/src/main/java/com/github/kuangcp/volatiles/NeverStopThread.java similarity index 100% rename from java-concurrency/src/main/java/com/github/kuangcp/volatiles/NeverStopThread.java rename to concurrency/src/main/java/com/github/kuangcp/volatiles/NeverStopThread.java diff --git a/java-concurrency/src/main/java/redis/migration/Main.java b/concurrency/src/main/java/redis/migration/Main.java similarity index 100% rename from java-concurrency/src/main/java/redis/migration/Main.java rename to concurrency/src/main/java/redis/migration/Main.java diff --git a/java-concurrency/src/main/java/redis/migration/Readme.md b/concurrency/src/main/java/redis/migration/Readme.md similarity index 100% rename from java-concurrency/src/main/java/redis/migration/Readme.md rename to concurrency/src/main/java/redis/migration/Readme.md diff --git a/java-concurrency/src/main/java/redis/migration/RedisDataType.java b/concurrency/src/main/java/redis/migration/RedisDataType.java similarity index 100% rename from java-concurrency/src/main/java/redis/migration/RedisDataType.java rename to concurrency/src/main/java/redis/migration/RedisDataType.java diff --git a/java-concurrency/src/main/java/redis/migration/RedisPoolProperty.java b/concurrency/src/main/java/redis/migration/RedisPoolProperty.java similarity index 100% rename from java-concurrency/src/main/java/redis/migration/RedisPoolProperty.java rename to concurrency/src/main/java/redis/migration/RedisPoolProperty.java diff --git a/java-concurrency/src/main/java/redis/migration/RedisPools.java b/concurrency/src/main/java/redis/migration/RedisPools.java similarity index 100% rename from java-concurrency/src/main/java/redis/migration/RedisPools.java rename to concurrency/src/main/java/redis/migration/RedisPools.java diff --git a/java-concurrency/src/main/java/redis/package-info.java b/concurrency/src/main/java/redis/package-info.java similarity index 100% rename from java-concurrency/src/main/java/redis/package-info.java rename to concurrency/src/main/java/redis/package-info.java diff --git a/java-concurrency/src/main/java/thread/HowToCreateThread.java b/concurrency/src/main/java/thread/HowToCreateThread.java similarity index 100% rename from java-concurrency/src/main/java/thread/HowToCreateThread.java rename to concurrency/src/main/java/thread/HowToCreateThread.java diff --git a/java-concurrency/src/main/java/thread/README.md b/concurrency/src/main/java/thread/README.md similarity index 100% rename from java-concurrency/src/main/java/thread/README.md rename to concurrency/src/main/java/thread/README.md diff --git a/java-concurrency/src/main/java/thread/ShowCreateThreadForSimpleMain.java b/concurrency/src/main/java/thread/ShowCreateThreadForSimpleMain.java similarity index 100% rename from java-concurrency/src/main/java/thread/ShowCreateThreadForSimpleMain.java rename to concurrency/src/main/java/thread/ShowCreateThreadForSimpleMain.java diff --git a/java-concurrency/src/main/java/thread/ThreadInterruptedDemo.java b/concurrency/src/main/java/thread/ThreadInterruptedDemo.java similarity index 100% rename from java-concurrency/src/main/java/thread/ThreadInterruptedDemo.java rename to concurrency/src/main/java/thread/ThreadInterruptedDemo.java diff --git a/java-concurrency/src/main/java/thread/ThreadJoinDemo.java b/concurrency/src/main/java/thread/ThreadJoinDemo.java similarity index 100% rename from java-concurrency/src/main/java/thread/ThreadJoinDemo.java rename to concurrency/src/main/java/thread/ThreadJoinDemo.java diff --git a/java-concurrency/src/main/java/thread/ThreadStatusTransfer.java b/concurrency/src/main/java/thread/ThreadStatusTransfer.java similarity index 100% rename from java-concurrency/src/main/java/thread/ThreadStatusTransfer.java rename to concurrency/src/main/java/thread/ThreadStatusTransfer.java diff --git a/java-concurrency/src/main/java/thread/UseThreadPool.java b/concurrency/src/main/java/thread/UseThreadPool.java similarity index 100% rename from java-concurrency/src/main/java/thread/UseThreadPool.java rename to concurrency/src/main/java/thread/UseThreadPool.java diff --git a/java-concurrency/src/main/java/thread/order/README.md b/concurrency/src/main/java/thread/order/README.md similarity index 100% rename from java-concurrency/src/main/java/thread/order/README.md rename to concurrency/src/main/java/thread/order/README.md diff --git a/java-concurrency/src/main/java/thread/order/Task.java b/concurrency/src/main/java/thread/order/Task.java similarity index 100% rename from java-concurrency/src/main/java/thread/order/Task.java rename to concurrency/src/main/java/thread/order/Task.java diff --git a/java-concurrency/src/main/java/thread/order/TaskWithVolatile.java b/concurrency/src/main/java/thread/order/TaskWithVolatile.java similarity index 100% rename from java-concurrency/src/main/java/thread/order/TaskWithVolatile.java rename to concurrency/src/main/java/thread/order/TaskWithVolatile.java diff --git a/java-concurrency/src/main/java/thread/pcstatus/Consumer.java b/concurrency/src/main/java/thread/pcstatus/Consumer.java similarity index 100% rename from java-concurrency/src/main/java/thread/pcstatus/Consumer.java rename to concurrency/src/main/java/thread/pcstatus/Consumer.java diff --git a/java-concurrency/src/main/java/thread/pcstatus/Main.java b/concurrency/src/main/java/thread/pcstatus/Main.java similarity index 100% rename from java-concurrency/src/main/java/thread/pcstatus/Main.java rename to concurrency/src/main/java/thread/pcstatus/Main.java diff --git a/java-concurrency/src/main/java/thread/pcstatus/Producer.java b/concurrency/src/main/java/thread/pcstatus/Producer.java similarity index 100% rename from java-concurrency/src/main/java/thread/pcstatus/Producer.java rename to concurrency/src/main/java/thread/pcstatus/Producer.java diff --git a/java-concurrency/src/main/java/thread/pcstatus/README.md b/concurrency/src/main/java/thread/pcstatus/README.md similarity index 100% rename from java-concurrency/src/main/java/thread/pcstatus/README.md rename to concurrency/src/main/java/thread/pcstatus/README.md diff --git a/java-concurrency/src/main/java/thread/pcstatus/Share.java b/concurrency/src/main/java/thread/pcstatus/Share.java similarity index 100% rename from java-concurrency/src/main/java/thread/pcstatus/Share.java rename to concurrency/src/main/java/thread/pcstatus/Share.java diff --git a/java-concurrency/src/test/java/com/github/kuangcp/lock/pvp/PlayerTest.java b/concurrency/src/test/java/com/github/kuangcp/lock/pvp/PlayerTest.java similarity index 100% rename from java-concurrency/src/test/java/com/github/kuangcp/lock/pvp/PlayerTest.java rename to concurrency/src/test/java/com/github/kuangcp/lock/pvp/PlayerTest.java diff --git a/java-concurrency/src/test/java/com/github/kuangcp/lock/sync/ActionTest.java b/concurrency/src/test/java/com/github/kuangcp/lock/sync/ActionTest.java similarity index 100% rename from java-concurrency/src/test/java/com/github/kuangcp/lock/sync/ActionTest.java rename to concurrency/src/test/java/com/github/kuangcp/lock/sync/ActionTest.java diff --git a/java-concurrency/src/test/java/com/github/kuangcp/queue/use/blocking/VeterinarianTest.java b/concurrency/src/test/java/com/github/kuangcp/queue/use/blocking/VeterinarianTest.java similarity index 100% rename from java-concurrency/src/test/java/com/github/kuangcp/queue/use/blocking/VeterinarianTest.java rename to concurrency/src/test/java/com/github/kuangcp/queue/use/blocking/VeterinarianTest.java diff --git a/java-concurrency/src/test/java/com/github/kuangcp/volatiles/NeverStopThreadTest.java b/concurrency/src/test/java/com/github/kuangcp/volatiles/NeverStopThreadTest.java similarity index 100% rename from java-concurrency/src/test/java/com/github/kuangcp/volatiles/NeverStopThreadTest.java rename to concurrency/src/test/java/com/github/kuangcp/volatiles/NeverStopThreadTest.java diff --git a/java-concurrency/src/test/java/redis/migration/MainTest.java b/concurrency/src/test/java/redis/migration/MainTest.java similarity index 100% rename from java-concurrency/src/test/java/redis/migration/MainTest.java rename to concurrency/src/test/java/redis/migration/MainTest.java diff --git a/java-concurrency/src/test/java/thread/ThreadStatusTransferTest.java b/concurrency/src/test/java/thread/ThreadStatusTransferTest.java similarity index 100% rename from java-concurrency/src/test/java/thread/ThreadStatusTransferTest.java rename to concurrency/src/test/java/thread/ThreadStatusTransferTest.java diff --git a/java-concurrency/src/test/java/thread/order/OrderedThreadTest.java b/concurrency/src/test/java/thread/order/OrderedThreadTest.java similarity index 100% rename from java-concurrency/src/test/java/thread/order/OrderedThreadTest.java rename to concurrency/src/test/java/thread/order/OrderedThreadTest.java diff --git a/java-concurrency/src/test/java/thread/schdule/TimeTaskTest.java b/concurrency/src/test/java/thread/schdule/TimeTaskTest.java similarity index 100% rename from java-concurrency/src/test/java/thread/schdule/TimeTaskTest.java rename to concurrency/src/test/java/thread/schdule/TimeTaskTest.java diff --git a/java-concurrency/src/test/resources/logback.xml b/concurrency/src/test/resources/logback.xml similarity index 100% rename from java-concurrency/src/test/resources/logback.xml rename to concurrency/src/test/resources/logback.xml diff --git a/java-guava/build.gradle b/flink/build.gradle similarity index 100% rename from java-guava/build.gradle rename to flink/build.gradle diff --git a/java-generic/build.gradle b/generic/build.gradle similarity index 100% rename from java-generic/build.gradle rename to generic/build.gradle diff --git a/java-generic/src/main/java/com/github/kuangcp/ArrayUtils.java b/generic/src/main/java/com/github/kuangcp/ArrayUtils.java similarity index 100% rename from java-generic/src/main/java/com/github/kuangcp/ArrayUtils.java rename to generic/src/main/java/com/github/kuangcp/ArrayUtils.java diff --git a/java-generic/src/main/java/com/github/kuangcp/README.md b/generic/src/main/java/com/github/kuangcp/README.md similarity index 100% rename from java-generic/src/main/java/com/github/kuangcp/README.md rename to generic/src/main/java/com/github/kuangcp/README.md diff --git a/java-generic/src/main/java/com/github/kuangcp/common/Human.java b/generic/src/main/java/com/github/kuangcp/common/Human.java similarity index 100% rename from java-generic/src/main/java/com/github/kuangcp/common/Human.java rename to generic/src/main/java/com/github/kuangcp/common/Human.java diff --git a/java-generic/src/main/java/com/github/kuangcp/common/Junior.java b/generic/src/main/java/com/github/kuangcp/common/Junior.java similarity index 100% rename from java-generic/src/main/java/com/github/kuangcp/common/Junior.java rename to generic/src/main/java/com/github/kuangcp/common/Junior.java diff --git a/java-generic/src/main/java/com/github/kuangcp/common/Student.java b/generic/src/main/java/com/github/kuangcp/common/Student.java similarity index 100% rename from java-generic/src/main/java/com/github/kuangcp/common/Student.java rename to generic/src/main/java/com/github/kuangcp/common/Student.java diff --git a/java-generic/src/main/java/com/github/kuangcp/distribution/SampleAble.java b/generic/src/main/java/com/github/kuangcp/distribution/SampleAble.java similarity index 100% rename from java-generic/src/main/java/com/github/kuangcp/distribution/SampleAble.java rename to generic/src/main/java/com/github/kuangcp/distribution/SampleAble.java diff --git a/java-generic/src/main/java/com/github/kuangcp/distribution/SampleUtil.java b/generic/src/main/java/com/github/kuangcp/distribution/SampleUtil.java similarity index 100% rename from java-generic/src/main/java/com/github/kuangcp/distribution/SampleUtil.java rename to generic/src/main/java/com/github/kuangcp/distribution/SampleUtil.java diff --git a/java-generic/src/main/java/com/github/kuangcp/inherit/Container.java b/generic/src/main/java/com/github/kuangcp/inherit/Container.java similarity index 100% rename from java-generic/src/main/java/com/github/kuangcp/inherit/Container.java rename to generic/src/main/java/com/github/kuangcp/inherit/Container.java diff --git a/java-generic/src/main/java/com/github/kuangcp/nesting/AbstractLoader.java b/generic/src/main/java/com/github/kuangcp/nesting/AbstractLoader.java similarity index 100% rename from java-generic/src/main/java/com/github/kuangcp/nesting/AbstractLoader.java rename to generic/src/main/java/com/github/kuangcp/nesting/AbstractLoader.java diff --git a/java-generic/src/main/java/com/github/kuangcp/nesting/HumanLoader.java b/generic/src/main/java/com/github/kuangcp/nesting/HumanLoader.java similarity index 100% rename from java-generic/src/main/java/com/github/kuangcp/nesting/HumanLoader.java rename to generic/src/main/java/com/github/kuangcp/nesting/HumanLoader.java diff --git a/java-generic/src/main/java/com/github/kuangcp/nesting/HumanVO.java b/generic/src/main/java/com/github/kuangcp/nesting/HumanVO.java similarity index 100% rename from java-generic/src/main/java/com/github/kuangcp/nesting/HumanVO.java rename to generic/src/main/java/com/github/kuangcp/nesting/HumanVO.java diff --git a/java-generic/src/main/java/com/github/kuangcp/nesting/JsonVO.java b/generic/src/main/java/com/github/kuangcp/nesting/JsonVO.java similarity index 100% rename from java-generic/src/main/java/com/github/kuangcp/nesting/JsonVO.java rename to generic/src/main/java/com/github/kuangcp/nesting/JsonVO.java diff --git a/java-generic/src/main/java/com/github/kuangcp/nesting/Readme.md b/generic/src/main/java/com/github/kuangcp/nesting/Readme.md similarity index 100% rename from java-generic/src/main/java/com/github/kuangcp/nesting/Readme.md rename to generic/src/main/java/com/github/kuangcp/nesting/Readme.md diff --git a/java-generic/src/main/java/com/github/kuangcp/simple/DateInterval.java b/generic/src/main/java/com/github/kuangcp/simple/DateInterval.java similarity index 100% rename from java-generic/src/main/java/com/github/kuangcp/simple/DateInterval.java rename to generic/src/main/java/com/github/kuangcp/simple/DateInterval.java diff --git a/java-generic/src/main/java/com/github/kuangcp/simple/Pair.java b/generic/src/main/java/com/github/kuangcp/simple/Pair.java similarity index 100% rename from java-generic/src/main/java/com/github/kuangcp/simple/Pair.java rename to generic/src/main/java/com/github/kuangcp/simple/Pair.java diff --git a/java-generic/src/main/java/com/github/kuangcp/simple/SimpleGenericMethod.java b/generic/src/main/java/com/github/kuangcp/simple/SimpleGenericMethod.java similarity index 100% rename from java-generic/src/main/java/com/github/kuangcp/simple/SimpleGenericMethod.java rename to generic/src/main/java/com/github/kuangcp/simple/SimpleGenericMethod.java diff --git a/java-generic/src/main/resources/logback.xml b/generic/src/main/resources/logback.xml similarity index 100% rename from java-generic/src/main/resources/logback.xml rename to generic/src/main/resources/logback.xml diff --git a/java-generic/src/test/java/com/github/kuangcp/ArrayUtilsTest.java b/generic/src/test/java/com/github/kuangcp/ArrayUtilsTest.java similarity index 100% rename from java-generic/src/test/java/com/github/kuangcp/ArrayUtilsTest.java rename to generic/src/test/java/com/github/kuangcp/ArrayUtilsTest.java diff --git a/java-generic/src/test/java/com/github/kuangcp/constraint/GenericArrayTest.java b/generic/src/test/java/com/github/kuangcp/constraint/GenericArrayTest.java similarity index 100% rename from java-generic/src/test/java/com/github/kuangcp/constraint/GenericArrayTest.java rename to generic/src/test/java/com/github/kuangcp/constraint/GenericArrayTest.java diff --git a/java-generic/src/test/java/com/github/kuangcp/distribution/DogRef.java b/generic/src/test/java/com/github/kuangcp/distribution/DogRef.java similarity index 100% rename from java-generic/src/test/java/com/github/kuangcp/distribution/DogRef.java rename to generic/src/test/java/com/github/kuangcp/distribution/DogRef.java diff --git a/java-generic/src/test/java/com/github/kuangcp/distribution/SampleUtilTest.java b/generic/src/test/java/com/github/kuangcp/distribution/SampleUtilTest.java similarity index 100% rename from java-generic/src/test/java/com/github/kuangcp/distribution/SampleUtilTest.java rename to generic/src/test/java/com/github/kuangcp/distribution/SampleUtilTest.java diff --git a/java-generic/src/test/java/com/github/kuangcp/inherit/InheritTest.java b/generic/src/test/java/com/github/kuangcp/inherit/InheritTest.java similarity index 100% rename from java-generic/src/test/java/com/github/kuangcp/inherit/InheritTest.java rename to generic/src/test/java/com/github/kuangcp/inherit/InheritTest.java diff --git a/java-generic/src/test/java/com/github/kuangcp/nesting/HumanLoaderTest.java b/generic/src/test/java/com/github/kuangcp/nesting/HumanLoaderTest.java similarity index 100% rename from java-generic/src/test/java/com/github/kuangcp/nesting/HumanLoaderTest.java rename to generic/src/test/java/com/github/kuangcp/nesting/HumanLoaderTest.java diff --git a/java-generic/src/test/java/com/github/kuangcp/simple/PairTest.java b/generic/src/test/java/com/github/kuangcp/simple/PairTest.java similarity index 100% rename from java-generic/src/test/java/com/github/kuangcp/simple/PairTest.java rename to generic/src/test/java/com/github/kuangcp/simple/PairTest.java diff --git a/java-generic/src/test/java/com/github/kuangcp/simple/Score.java b/generic/src/test/java/com/github/kuangcp/simple/Score.java similarity index 100% rename from java-generic/src/test/java/com/github/kuangcp/simple/Score.java rename to generic/src/test/java/com/github/kuangcp/simple/Score.java diff --git a/java-generic/src/test/java/com/github/kuangcp/simple/SimpleGenericMethodTest.java b/generic/src/test/java/com/github/kuangcp/simple/SimpleGenericMethodTest.java similarity index 100% rename from java-generic/src/test/java/com/github/kuangcp/simple/SimpleGenericMethodTest.java rename to generic/src/test/java/com/github/kuangcp/simple/SimpleGenericMethodTest.java diff --git a/guava/build.gradle b/guava/build.gradle new file mode 100644 index 00000000..34352b2c --- /dev/null +++ b/guava/build.gradle @@ -0,0 +1,3 @@ +dependencies { + +} \ No newline at end of file diff --git a/java-guava/src/test/java/guava/base/AvoidNull.java b/guava/src/test/java/guava/base/AvoidNull.java similarity index 100% rename from java-guava/src/test/java/guava/base/AvoidNull.java rename to guava/src/test/java/guava/base/AvoidNull.java diff --git a/java-guava/src/test/java/guava/package-info.java b/guava/src/test/java/guava/package-info.java similarity index 100% rename from java-guava/src/test/java/guava/package-info.java rename to guava/src/test/java/guava/package-info.java diff --git a/java-guava/src/test/java/guava/service/package-info.java b/guava/src/test/java/guava/service/package-info.java similarity index 100% rename from java-guava/src/test/java/guava/service/package-info.java rename to guava/src/test/java/guava/service/package-info.java diff --git a/java-gui/build.gradle b/gui/build.gradle similarity index 100% rename from java-gui/build.gradle rename to gui/build.gradle diff --git a/java-gui/src/main/java/com/github/kuangcp/README.md b/gui/src/main/java/com/github/kuangcp/README.md similarity index 100% rename from java-gui/src/main/java/com/github/kuangcp/README.md rename to gui/src/main/java/com/github/kuangcp/README.md diff --git a/java-gui/src/main/java/com/github/kuangcp/caculator/Calculate_btnBegin_actionAdapter.java b/gui/src/main/java/com/github/kuangcp/caculator/Calculate_btnBegin_actionAdapter.java similarity index 100% rename from java-gui/src/main/java/com/github/kuangcp/caculator/Calculate_btnBegin_actionAdapter.java rename to gui/src/main/java/com/github/kuangcp/caculator/Calculate_btnBegin_actionAdapter.java diff --git a/java-gui/src/main/java/com/github/kuangcp/caculator/Calculate_btnCancel_actionAdapter.java b/gui/src/main/java/com/github/kuangcp/caculator/Calculate_btnCancel_actionAdapter.java similarity index 100% rename from java-gui/src/main/java/com/github/kuangcp/caculator/Calculate_btnCancel_actionAdapter.java rename to gui/src/main/java/com/github/kuangcp/caculator/Calculate_btnCancel_actionAdapter.java diff --git a/java-gui/src/main/java/com/github/kuangcp/caculator/Calculate_btnEqual_actionAdapter.java b/gui/src/main/java/com/github/kuangcp/caculator/Calculate_btnEqual_actionAdapter.java similarity index 100% rename from java-gui/src/main/java/com/github/kuangcp/caculator/Calculate_btnEqual_actionAdapter.java rename to gui/src/main/java/com/github/kuangcp/caculator/Calculate_btnEqual_actionAdapter.java diff --git a/java-gui/src/main/java/com/github/kuangcp/caculator/Calculate_btnIncrease_actionAdapter.java b/gui/src/main/java/com/github/kuangcp/caculator/Calculate_btnIncrease_actionAdapter.java similarity index 100% rename from java-gui/src/main/java/com/github/kuangcp/caculator/Calculate_btnIncrease_actionAdapter.java rename to gui/src/main/java/com/github/kuangcp/caculator/Calculate_btnIncrease_actionAdapter.java diff --git a/java-gui/src/main/java/com/github/kuangcp/caculator/Calculate_btnMinus_actionAdapter.java b/gui/src/main/java/com/github/kuangcp/caculator/Calculate_btnMinus_actionAdapter.java similarity index 100% rename from java-gui/src/main/java/com/github/kuangcp/caculator/Calculate_btnMinus_actionAdapter.java rename to gui/src/main/java/com/github/kuangcp/caculator/Calculate_btnMinus_actionAdapter.java diff --git a/java-gui/src/main/java/com/github/kuangcp/caculator/Calculate_btnPoint_actionAdapter.java b/gui/src/main/java/com/github/kuangcp/caculator/Calculate_btnPoint_actionAdapter.java similarity index 100% rename from java-gui/src/main/java/com/github/kuangcp/caculator/Calculate_btnPoint_actionAdapter.java rename to gui/src/main/java/com/github/kuangcp/caculator/Calculate_btnPoint_actionAdapter.java diff --git a/java-gui/src/main/java/com/github/kuangcp/caculator/Calculate_btnZero_actionAdapter.java b/gui/src/main/java/com/github/kuangcp/caculator/Calculate_btnZero_actionAdapter.java similarity index 100% rename from java-gui/src/main/java/com/github/kuangcp/caculator/Calculate_btnZero_actionAdapter.java rename to gui/src/main/java/com/github/kuangcp/caculator/Calculate_btnZero_actionAdapter.java diff --git a/java-gui/src/main/java/com/github/kuangcp/caculator/Calculator.java b/gui/src/main/java/com/github/kuangcp/caculator/Calculator.java similarity index 100% rename from java-gui/src/main/java/com/github/kuangcp/caculator/Calculator.java rename to gui/src/main/java/com/github/kuangcp/caculator/Calculator.java diff --git a/java-gui/src/main/java/com/github/kuangcp/jigsaw/ButtonListener.java b/gui/src/main/java/com/github/kuangcp/jigsaw/ButtonListener.java similarity index 100% rename from java-gui/src/main/java/com/github/kuangcp/jigsaw/ButtonListener.java rename to gui/src/main/java/com/github/kuangcp/jigsaw/ButtonListener.java diff --git a/java-gui/src/main/java/com/github/kuangcp/jigsaw/ImageBlock.java b/gui/src/main/java/com/github/kuangcp/jigsaw/ImageBlock.java similarity index 100% rename from java-gui/src/main/java/com/github/kuangcp/jigsaw/ImageBlock.java rename to gui/src/main/java/com/github/kuangcp/jigsaw/ImageBlock.java diff --git a/java-gui/src/main/java/com/github/kuangcp/jigsaw/ImageBlockMgr.java b/gui/src/main/java/com/github/kuangcp/jigsaw/ImageBlockMgr.java similarity index 100% rename from java-gui/src/main/java/com/github/kuangcp/jigsaw/ImageBlockMgr.java rename to gui/src/main/java/com/github/kuangcp/jigsaw/ImageBlockMgr.java diff --git a/java-gui/src/main/java/com/github/kuangcp/jigsaw/MainFrame.java b/gui/src/main/java/com/github/kuangcp/jigsaw/MainFrame.java similarity index 100% rename from java-gui/src/main/java/com/github/kuangcp/jigsaw/MainFrame.java rename to gui/src/main/java/com/github/kuangcp/jigsaw/MainFrame.java diff --git a/java-gui/src/main/java/com/github/kuangcp/jigsaw/MainPanel.java b/gui/src/main/java/com/github/kuangcp/jigsaw/MainPanel.java similarity index 100% rename from java-gui/src/main/java/com/github/kuangcp/jigsaw/MainPanel.java rename to gui/src/main/java/com/github/kuangcp/jigsaw/MainPanel.java diff --git a/java-gui/src/main/java/com/github/kuangcp/jigsaw/Move.java b/gui/src/main/java/com/github/kuangcp/jigsaw/Move.java similarity index 100% rename from java-gui/src/main/java/com/github/kuangcp/jigsaw/Move.java rename to gui/src/main/java/com/github/kuangcp/jigsaw/Move.java diff --git a/java-gui/src/main/java/com/github/kuangcp/notepad/ICommand.java b/gui/src/main/java/com/github/kuangcp/notepad/ICommand.java similarity index 100% rename from java-gui/src/main/java/com/github/kuangcp/notepad/ICommand.java rename to gui/src/main/java/com/github/kuangcp/notepad/ICommand.java diff --git a/java-gui/src/main/java/com/github/kuangcp/notepad/Note.java b/gui/src/main/java/com/github/kuangcp/notepad/Note.java similarity index 100% rename from java-gui/src/main/java/com/github/kuangcp/notepad/Note.java rename to gui/src/main/java/com/github/kuangcp/notepad/Note.java diff --git a/java-gui/src/main/resources/jigsaw/0.jpg b/gui/src/main/resources/jigsaw/0.jpg similarity index 100% rename from java-gui/src/main/resources/jigsaw/0.jpg rename to gui/src/main/resources/jigsaw/0.jpg diff --git a/java-gui/src/main/resources/jigsaw/1.jpg b/gui/src/main/resources/jigsaw/1.jpg similarity index 100% rename from java-gui/src/main/resources/jigsaw/1.jpg rename to gui/src/main/resources/jigsaw/1.jpg diff --git a/java-gui/src/main/resources/jigsaw/2.jpg b/gui/src/main/resources/jigsaw/2.jpg similarity index 100% rename from java-gui/src/main/resources/jigsaw/2.jpg rename to gui/src/main/resources/jigsaw/2.jpg diff --git a/java-gui/src/main/resources/jigsaw/3.jpg b/gui/src/main/resources/jigsaw/3.jpg similarity index 100% rename from java-gui/src/main/resources/jigsaw/3.jpg rename to gui/src/main/resources/jigsaw/3.jpg diff --git a/java-gui/src/main/resources/jigsaw/4.jpg b/gui/src/main/resources/jigsaw/4.jpg similarity index 100% rename from java-gui/src/main/resources/jigsaw/4.jpg rename to gui/src/main/resources/jigsaw/4.jpg diff --git a/java-gui/src/main/resources/jigsaw/5.jpg b/gui/src/main/resources/jigsaw/5.jpg similarity index 100% rename from java-gui/src/main/resources/jigsaw/5.jpg rename to gui/src/main/resources/jigsaw/5.jpg diff --git a/java-gui/src/main/resources/jigsaw/6.jpg b/gui/src/main/resources/jigsaw/6.jpg similarity index 100% rename from java-gui/src/main/resources/jigsaw/6.jpg rename to gui/src/main/resources/jigsaw/6.jpg diff --git a/java-gui/src/main/resources/jigsaw/7.jpg b/gui/src/main/resources/jigsaw/7.jpg similarity index 100% rename from java-gui/src/main/resources/jigsaw/7.jpg rename to gui/src/main/resources/jigsaw/7.jpg diff --git a/java-gui/src/main/resources/jigsaw/8.jpg b/gui/src/main/resources/jigsaw/8.jpg similarity index 100% rename from java-gui/src/main/resources/jigsaw/8.jpg rename to gui/src/main/resources/jigsaw/8.jpg diff --git a/java-gui/src/main/resources/logback.xml b/gui/src/main/resources/logback.xml similarity index 100% rename from java-gui/src/main/resources/logback.xml rename to gui/src/main/resources/logback.xml diff --git a/java-hadoop/build.gradle b/hadoop/build.gradle similarity index 100% rename from java-hadoop/build.gradle rename to hadoop/build.gradle diff --git a/java-hadoop/src/main/java/com/github/kuangcp/hdfs/hi/GeneralFileActionDemo.java b/hadoop/src/main/java/com/github/kuangcp/hdfs/hi/GeneralFileActionDemo.java similarity index 100% rename from java-hadoop/src/main/java/com/github/kuangcp/hdfs/hi/GeneralFileActionDemo.java rename to hadoop/src/main/java/com/github/kuangcp/hdfs/hi/GeneralFileActionDemo.java diff --git a/java-hadoop/src/main/resources/logback.xml b/hadoop/src/main/resources/logback.xml similarity index 100% rename from java-hadoop/src/main/resources/logback.xml rename to hadoop/src/main/resources/logback.xml diff --git a/java-hadoop/src/test/java/com/github/kuangcp/hdfs/hi/GeneralFileActionDemoTest.java b/hadoop/src/test/java/com/github/kuangcp/hdfs/hi/GeneralFileActionDemoTest.java similarity index 100% rename from java-hadoop/src/test/java/com/github/kuangcp/hdfs/hi/GeneralFileActionDemoTest.java rename to hadoop/src/test/java/com/github/kuangcp/hdfs/hi/GeneralFileActionDemoTest.java diff --git a/java-io/README.md b/io/README.md similarity index 100% rename from java-io/README.md rename to io/README.md diff --git a/java-io/build.gradle b/io/build.gradle similarity index 100% rename from java-io/build.gradle rename to io/build.gradle diff --git a/java-io/src/main/java/com/github/kuangcp/file/CopyFile.java b/io/src/main/java/com/github/kuangcp/file/CopyFile.java similarity index 100% rename from java-io/src/main/java/com/github/kuangcp/file/CopyFile.java rename to io/src/main/java/com/github/kuangcp/file/CopyFile.java diff --git a/java-io/src/main/java/com/github/kuangcp/standard/Readme.md b/io/src/main/java/com/github/kuangcp/standard/Readme.md similarity index 100% rename from java-io/src/main/java/com/github/kuangcp/standard/Readme.md rename to io/src/main/java/com/github/kuangcp/standard/Readme.md diff --git a/java-io/src/main/java/com/github/kuangcp/zip/package-info.java b/io/src/main/java/com/github/kuangcp/zip/package-info.java similarity index 100% rename from java-io/src/main/java/com/github/kuangcp/zip/package-info.java rename to io/src/main/java/com/github/kuangcp/zip/package-info.java diff --git a/java-io/src/test/java/buffer/BufferTest.java b/io/src/test/java/buffer/BufferTest.java similarity index 100% rename from java-io/src/test/java/buffer/BufferTest.java rename to io/src/test/java/buffer/BufferTest.java diff --git a/java-io/src/test/java/com/github/kuangcp/PropertiesTest.java b/io/src/test/java/com/github/kuangcp/PropertiesTest.java similarity index 100% rename from java-io/src/test/java/com/github/kuangcp/PropertiesTest.java rename to io/src/test/java/com/github/kuangcp/PropertiesTest.java diff --git a/java-io/src/test/java/com/github/kuangcp/file/CopyFileTest.java b/io/src/test/java/com/github/kuangcp/file/CopyFileTest.java similarity index 100% rename from java-io/src/test/java/com/github/kuangcp/file/CopyFileTest.java rename to io/src/test/java/com/github/kuangcp/file/CopyFileTest.java diff --git a/java-io/src/test/resources/properties/main.properties b/io/src/test/resources/properties/main.properties similarity index 100% rename from java-io/src/test/resources/properties/main.properties rename to io/src/test/resources/properties/main.properties diff --git a/java-spring/src/test/java/com/github/kuangcp/proxy/salary/Privilege.java b/java-spring/src/test/java/com/github/kuangcp/proxy/salary/Privilege.java deleted file mode 100644 index a245e4ff..00000000 --- a/java-spring/src/test/java/com/github/kuangcp/proxy/salary/Privilege.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.github.kuangcp.proxy.salary; - -public class Privilege { - private String access; - - public String getAccess() { - return access; - } - - public void setAccess(String access) { - this.access = access; - } -} diff --git a/java-spring/src/test/java/com/github/kuangcp/proxy/salary/SalaryManager.java b/java-spring/src/test/java/com/github/kuangcp/proxy/salary/SalaryManager.java deleted file mode 100644 index dc34c901..00000000 --- a/java-spring/src/test/java/com/github/kuangcp/proxy/salary/SalaryManager.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.github.kuangcp.proxy.salary; - -public class SalaryManager { - private Logger logger; - private Security security; - private Privilege privilege; - public SalaryManager(Logger logger,Security security,Privilege privilege){ - this.logger = logger; - this.security = security; - this.privilege = privilege; - } - public void showSalary(){ - this.logger.logging(); - this.security.security(); - if("admin".equals(this.privilege.getAccess())){ - System.out.println("正在查看工资:涨了2W"); - }else{ - System.out.println("您没有该权限"); - } - } -} diff --git a/java-spring/src/test/java/com/github/kuangcp/proxy/salary/SalaryTest.java b/java-spring/src/test/java/com/github/kuangcp/proxy/salary/SalaryTest.java deleted file mode 100644 index f42616e0..00000000 --- a/java-spring/src/test/java/com/github/kuangcp/proxy/salary/SalaryTest.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.github.kuangcp.proxy.salary; - -import org.junit.Test; - -public class SalaryTest { - @Test - public void test(){ - Logger logger = new Logger(); - Privilege privilege = new Privilege(); - Security security = new Security(); - privilege.setAccess("admin"); - SalaryManager salaryManager = new SalaryManager(logger, security, privilege); - salaryManager.showSalary(); - } -} diff --git a/java-spring/src/test/java/com/github/kuangcp/proxy/salary/proxy/Privilege.java b/java-spring/src/test/java/com/github/kuangcp/proxy/salary/proxy/Privilege.java deleted file mode 100644 index a34cb4db..00000000 --- a/java-spring/src/test/java/com/github/kuangcp/proxy/salary/proxy/Privilege.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.github.kuangcp.proxy.salary.proxy; - -public class Privilege { - private String access; - public String getAccess() { - return access; - } - public void setAccess(String access) { - this.access = access; - } -} diff --git a/java-spring/src/test/java/com/github/kuangcp/proxy/salary/proxy/SalaryManagerImpl.java b/java-spring/src/test/java/com/github/kuangcp/proxy/salary/proxy/SalaryManagerImpl.java deleted file mode 100644 index d78e0e80..00000000 --- a/java-spring/src/test/java/com/github/kuangcp/proxy/salary/proxy/SalaryManagerImpl.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.github.kuangcp.proxy.salary.proxy; - -public class SalaryManagerImpl implements SalaryManager{ - @Override - public void showSalary() { - System.out.println("正在查看工资:哦哦,涨了2W闽比"); - } -} diff --git a/java-8/Readme.md b/java8/Readme.md similarity index 100% rename from java-8/Readme.md rename to java8/Readme.md diff --git a/java-8/build.gradle b/java8/build.gradle similarity index 100% rename from java-8/build.gradle rename to java8/build.gradle diff --git a/java-8/src/test/java/com/github/kuangcp/function/sort/BaseTypeSortTest.java b/java8/src/test/java/com/github/kuangcp/function/sort/BaseTypeSortTest.java similarity index 100% rename from java-8/src/test/java/com/github/kuangcp/function/sort/BaseTypeSortTest.java rename to java8/src/test/java/com/github/kuangcp/function/sort/BaseTypeSortTest.java diff --git a/java-8/src/test/java/com/github/kuangcp/function/sort/CustomSortTest.java b/java8/src/test/java/com/github/kuangcp/function/sort/CustomSortTest.java similarity index 100% rename from java-8/src/test/java/com/github/kuangcp/function/sort/CustomSortTest.java rename to java8/src/test/java/com/github/kuangcp/function/sort/CustomSortTest.java diff --git a/java-8/src/test/java/com/github/kuangcp/function/sort/DateSortTest.java b/java8/src/test/java/com/github/kuangcp/function/sort/DateSortTest.java similarity index 100% rename from java-8/src/test/java/com/github/kuangcp/function/sort/DateSortTest.java rename to java8/src/test/java/com/github/kuangcp/function/sort/DateSortTest.java diff --git a/java-8/src/test/java/com/github/kuangcp/lambda/filter/FilterTest.java b/java8/src/test/java/com/github/kuangcp/lambda/filter/FilterTest.java similarity index 100% rename from java-8/src/test/java/com/github/kuangcp/lambda/filter/FilterTest.java rename to java8/src/test/java/com/github/kuangcp/lambda/filter/FilterTest.java diff --git a/java-8/src/test/java/com/github/kuangcp/lambda/firstdemo/MainTest.java b/java8/src/test/java/com/github/kuangcp/lambda/firstdemo/MainTest.java similarity index 100% rename from java-8/src/test/java/com/github/kuangcp/lambda/firstdemo/MainTest.java rename to java8/src/test/java/com/github/kuangcp/lambda/firstdemo/MainTest.java diff --git a/java-8/src/test/java/com/github/kuangcp/lambda/firstdemo/PointAction.java b/java8/src/test/java/com/github/kuangcp/lambda/firstdemo/PointAction.java similarity index 100% rename from java-8/src/test/java/com/github/kuangcp/lambda/firstdemo/PointAction.java rename to java8/src/test/java/com/github/kuangcp/lambda/firstdemo/PointAction.java diff --git a/java-8/src/test/java/com/github/kuangcp/lambda/firstdemo/PointArrayList.java b/java8/src/test/java/com/github/kuangcp/lambda/firstdemo/PointArrayList.java similarity index 100% rename from java-8/src/test/java/com/github/kuangcp/lambda/firstdemo/PointArrayList.java rename to java8/src/test/java/com/github/kuangcp/lambda/firstdemo/PointArrayList.java diff --git a/java-8/src/test/java/com/github/kuangcp/lambda/firstdemo/TranslateByOne.java b/java8/src/test/java/com/github/kuangcp/lambda/firstdemo/TranslateByOne.java similarity index 100% rename from java-8/src/test/java/com/github/kuangcp/lambda/firstdemo/TranslateByOne.java rename to java8/src/test/java/com/github/kuangcp/lambda/firstdemo/TranslateByOne.java diff --git a/java-8/src/test/java/com/github/kuangcp/lambda/firstdemo/TranslateByOne8.java b/java8/src/test/java/com/github/kuangcp/lambda/firstdemo/TranslateByOne8.java similarity index 100% rename from java-8/src/test/java/com/github/kuangcp/lambda/firstdemo/TranslateByOne8.java rename to java8/src/test/java/com/github/kuangcp/lambda/firstdemo/TranslateByOne8.java diff --git a/java-8/src/test/java/com/github/kuangcp/optional/OptionalTest.java b/java8/src/test/java/com/github/kuangcp/optional/OptionalTest.java similarity index 100% rename from java-8/src/test/java/com/github/kuangcp/optional/OptionalTest.java rename to java8/src/test/java/com/github/kuangcp/optional/OptionalTest.java diff --git a/java-8/src/test/java/com/github/kuangcp/stream/Readme.md b/java8/src/test/java/com/github/kuangcp/stream/Readme.md similarity index 100% rename from java-8/src/test/java/com/github/kuangcp/stream/Readme.md rename to java8/src/test/java/com/github/kuangcp/stream/Readme.md diff --git a/java-8/src/test/java/com/github/kuangcp/stream/debug/UsePluginWithDebugTest.java b/java8/src/test/java/com/github/kuangcp/stream/debug/UsePluginWithDebugTest.java similarity index 100% rename from java-8/src/test/java/com/github/kuangcp/stream/debug/UsePluginWithDebugTest.java rename to java8/src/test/java/com/github/kuangcp/stream/debug/UsePluginWithDebugTest.java diff --git a/java-8/src/test/java/com/github/kuangcp/stream/list/HandleExceptionTest.java b/java8/src/test/java/com/github/kuangcp/stream/list/HandleExceptionTest.java similarity index 100% rename from java-8/src/test/java/com/github/kuangcp/stream/list/HandleExceptionTest.java rename to java8/src/test/java/com/github/kuangcp/stream/list/HandleExceptionTest.java diff --git a/java-8/src/test/java/com/github/kuangcp/stream/map/ToMapTest.java b/java8/src/test/java/com/github/kuangcp/stream/map/ToMapTest.java similarity index 100% rename from java-8/src/test/java/com/github/kuangcp/stream/map/ToMapTest.java rename to java8/src/test/java/com/github/kuangcp/stream/map/ToMapTest.java diff --git a/java-8/src/test/java/com/github/kuangcp/time/CalendarTest.java b/java8/src/test/java/com/github/kuangcp/time/CalendarTest.java similarity index 100% rename from java-8/src/test/java/com/github/kuangcp/time/CalendarTest.java rename to java8/src/test/java/com/github/kuangcp/time/CalendarTest.java diff --git a/java-8/src/test/java/com/github/kuangcp/time/InstantTest.java b/java8/src/test/java/com/github/kuangcp/time/InstantTest.java similarity index 100% rename from java-8/src/test/java/com/github/kuangcp/time/InstantTest.java rename to java8/src/test/java/com/github/kuangcp/time/InstantTest.java diff --git a/java-8/src/test/java/com/github/kuangcp/time/LocalDateTimeTest.java b/java8/src/test/java/com/github/kuangcp/time/LocalDateTimeTest.java similarity index 100% rename from java-8/src/test/java/com/github/kuangcp/time/LocalDateTimeTest.java rename to java8/src/test/java/com/github/kuangcp/time/LocalDateTimeTest.java diff --git a/java-kafka/build.gradle b/kafka/build.gradle similarity index 100% rename from java-kafka/build.gradle rename to kafka/build.gradle diff --git a/java-kafka/src/main/java/com/github/kuangcp/hi/Constants.java b/kafka/src/main/java/com/github/kuangcp/hi/Constants.java similarity index 100% rename from java-kafka/src/main/java/com/github/kuangcp/hi/Constants.java rename to kafka/src/main/java/com/github/kuangcp/hi/Constants.java diff --git a/java-kafka/src/main/java/com/github/kuangcp/hi/ConsumerDemo.java b/kafka/src/main/java/com/github/kuangcp/hi/ConsumerDemo.java similarity index 100% rename from java-kafka/src/main/java/com/github/kuangcp/hi/ConsumerDemo.java rename to kafka/src/main/java/com/github/kuangcp/hi/ConsumerDemo.java diff --git a/java-kafka/src/main/java/com/github/kuangcp/hi/ProducerDemo.java b/kafka/src/main/java/com/github/kuangcp/hi/ProducerDemo.java similarity index 100% rename from java-kafka/src/main/java/com/github/kuangcp/hi/ProducerDemo.java rename to kafka/src/main/java/com/github/kuangcp/hi/ProducerDemo.java diff --git a/java-kafka/src/main/java/com/github/kuangcp/hi/dto/ProductStatisticJobCommand.java b/kafka/src/main/java/com/github/kuangcp/hi/dto/ProductStatisticJobCommand.java similarity index 100% rename from java-kafka/src/main/java/com/github/kuangcp/hi/dto/ProductStatisticJobCommand.java rename to kafka/src/main/java/com/github/kuangcp/hi/dto/ProductStatisticJobCommand.java diff --git a/java-kafka/src/main/java/com/github/kuangcp/hi/dto/ProductStatisticSpan.java b/kafka/src/main/java/com/github/kuangcp/hi/dto/ProductStatisticSpan.java similarity index 100% rename from java-kafka/src/main/java/com/github/kuangcp/hi/dto/ProductStatisticSpan.java rename to kafka/src/main/java/com/github/kuangcp/hi/dto/ProductStatisticSpan.java diff --git a/java-kafka/src/main/java/com/github/kuangcp/hi/dto/StartCommand.java b/kafka/src/main/java/com/github/kuangcp/hi/dto/StartCommand.java similarity index 100% rename from java-kafka/src/main/java/com/github/kuangcp/hi/dto/StartCommand.java rename to kafka/src/main/java/com/github/kuangcp/hi/dto/StartCommand.java diff --git a/java-kafka/src/main/java/com/github/kuangcp/hi/dto/StatisticSpan.java b/kafka/src/main/java/com/github/kuangcp/hi/dto/StatisticSpan.java similarity index 100% rename from java-kafka/src/main/java/com/github/kuangcp/hi/dto/StatisticSpan.java rename to kafka/src/main/java/com/github/kuangcp/hi/dto/StatisticSpan.java diff --git a/java-kafka/src/main/resources/logback.xml b/kafka/src/main/resources/logback.xml similarity index 100% rename from java-kafka/src/main/resources/logback.xml rename to kafka/src/main/resources/logback.xml diff --git a/java-kafka/src/test/java/com/github/kuangcp/hi/ConsumerDemoTest.java b/kafka/src/test/java/com/github/kuangcp/hi/ConsumerDemoTest.java similarity index 100% rename from java-kafka/src/test/java/com/github/kuangcp/hi/ConsumerDemoTest.java rename to kafka/src/test/java/com/github/kuangcp/hi/ConsumerDemoTest.java diff --git a/java-kafka/src/test/java/com/github/kuangcp/hi/ProducerDemoTest.java b/kafka/src/test/java/com/github/kuangcp/hi/ProducerDemoTest.java similarity index 100% rename from java-kafka/src/test/java/com/github/kuangcp/hi/ProducerDemoTest.java rename to kafka/src/test/java/com/github/kuangcp/hi/ProducerDemoTest.java diff --git a/java-netty/build.gradle b/netty/build.gradle similarity index 100% rename from java-netty/build.gradle rename to netty/build.gradle diff --git a/java-netty/src/main/java/netty/package-info.java b/netty/src/main/java/netty/package-info.java similarity index 100% rename from java-netty/src/main/java/netty/package-info.java rename to netty/src/main/java/netty/package-info.java diff --git a/java-netty/src/main/java/netty/timeServer/Command.java b/netty/src/main/java/netty/timeServer/Command.java similarity index 100% rename from java-netty/src/main/java/netty/timeServer/Command.java rename to netty/src/main/java/netty/timeServer/Command.java diff --git a/java-netty/src/main/java/netty/timeServer/README.md b/netty/src/main/java/netty/timeServer/README.md similarity index 100% rename from java-netty/src/main/java/netty/timeServer/README.md rename to netty/src/main/java/netty/timeServer/README.md diff --git a/java-netty/src/main/java/netty/timeServer/TimeClient.java b/netty/src/main/java/netty/timeServer/TimeClient.java similarity index 100% rename from java-netty/src/main/java/netty/timeServer/TimeClient.java rename to netty/src/main/java/netty/timeServer/TimeClient.java diff --git a/java-netty/src/main/java/netty/timeServer/TimeClientHandler.java b/netty/src/main/java/netty/timeServer/TimeClientHandler.java similarity index 100% rename from java-netty/src/main/java/netty/timeServer/TimeClientHandler.java rename to netty/src/main/java/netty/timeServer/TimeClientHandler.java diff --git a/java-netty/src/main/java/netty/timeServer/TimeServer.java b/netty/src/main/java/netty/timeServer/TimeServer.java similarity index 100% rename from java-netty/src/main/java/netty/timeServer/TimeServer.java rename to netty/src/main/java/netty/timeServer/TimeServer.java diff --git a/java-netty/src/main/java/netty/timeServer/TimeServerHandler.java b/netty/src/main/java/netty/timeServer/TimeServerHandler.java similarity index 100% rename from java-netty/src/main/java/netty/timeServer/TimeServerHandler.java rename to netty/src/main/java/netty/timeServer/TimeServerHandler.java diff --git a/java-netty/src/main/resources/logback.xml b/netty/src/main/resources/logback.xml similarity index 100% rename from java-netty/src/main/resources/logback.xml rename to netty/src/main/resources/logback.xml diff --git a/java-netty/src/test/java/netty/timeServer/TimeServerTest.java b/netty/src/test/java/netty/timeServer/TimeServerTest.java similarity index 100% rename from java-netty/src/test/java/netty/timeServer/TimeServerTest.java rename to netty/src/test/java/netty/timeServer/TimeServerTest.java diff --git a/java-network/README.md b/network/README.md similarity index 100% rename from java-network/README.md rename to network/README.md diff --git a/java-network/build.gradle b/network/build.gradle similarity index 100% rename from java-network/build.gradle rename to network/build.gradle diff --git a/java-network/src/main/java/com/github/kuangcp/bio/Readme.md b/network/src/main/java/com/github/kuangcp/bio/Readme.md similarity index 100% rename from java-network/src/main/java/com/github/kuangcp/bio/Readme.md rename to network/src/main/java/com/github/kuangcp/bio/Readme.md diff --git a/java-network/src/main/java/com/github/kuangcp/bio/chattingroom/ClientThread.java b/network/src/main/java/com/github/kuangcp/bio/chattingroom/ClientThread.java similarity index 100% rename from java-network/src/main/java/com/github/kuangcp/bio/chattingroom/ClientThread.java rename to network/src/main/java/com/github/kuangcp/bio/chattingroom/ClientThread.java diff --git a/java-network/src/main/java/com/github/kuangcp/bio/chattingroom/README.md b/network/src/main/java/com/github/kuangcp/bio/chattingroom/README.md similarity index 100% rename from java-network/src/main/java/com/github/kuangcp/bio/chattingroom/README.md rename to network/src/main/java/com/github/kuangcp/bio/chattingroom/README.md diff --git a/java-network/src/main/java/com/github/kuangcp/bio/chattingroom/ServerThread.java b/network/src/main/java/com/github/kuangcp/bio/chattingroom/ServerThread.java similarity index 100% rename from java-network/src/main/java/com/github/kuangcp/bio/chattingroom/ServerThread.java rename to network/src/main/java/com/github/kuangcp/bio/chattingroom/ServerThread.java diff --git a/java-network/src/main/java/com/github/kuangcp/bio/chattingroom/SocketClient.java b/network/src/main/java/com/github/kuangcp/bio/chattingroom/SocketClient.java similarity index 100% rename from java-network/src/main/java/com/github/kuangcp/bio/chattingroom/SocketClient.java rename to network/src/main/java/com/github/kuangcp/bio/chattingroom/SocketClient.java diff --git a/java-network/src/main/java/com/github/kuangcp/bio/chattingroom/SocketServer.java b/network/src/main/java/com/github/kuangcp/bio/chattingroom/SocketServer.java similarity index 100% rename from java-network/src/main/java/com/github/kuangcp/bio/chattingroom/SocketServer.java rename to network/src/main/java/com/github/kuangcp/bio/chattingroom/SocketServer.java diff --git a/java-network/src/main/java/com/github/kuangcp/bio/onechatone/ChatMaps.java b/network/src/main/java/com/github/kuangcp/bio/onechatone/ChatMaps.java similarity index 100% rename from java-network/src/main/java/com/github/kuangcp/bio/onechatone/ChatMaps.java rename to network/src/main/java/com/github/kuangcp/bio/onechatone/ChatMaps.java diff --git a/java-network/src/main/java/com/github/kuangcp/bio/onechatone/ChatProtocol.java b/network/src/main/java/com/github/kuangcp/bio/onechatone/ChatProtocol.java similarity index 100% rename from java-network/src/main/java/com/github/kuangcp/bio/onechatone/ChatProtocol.java rename to network/src/main/java/com/github/kuangcp/bio/onechatone/ChatProtocol.java diff --git a/java-network/src/main/java/com/github/kuangcp/bio/onechatone/Client.java b/network/src/main/java/com/github/kuangcp/bio/onechatone/Client.java similarity index 100% rename from java-network/src/main/java/com/github/kuangcp/bio/onechatone/Client.java rename to network/src/main/java/com/github/kuangcp/bio/onechatone/Client.java diff --git a/java-network/src/main/java/com/github/kuangcp/bio/onechatone/ClientThread.java b/network/src/main/java/com/github/kuangcp/bio/onechatone/ClientThread.java similarity index 100% rename from java-network/src/main/java/com/github/kuangcp/bio/onechatone/ClientThread.java rename to network/src/main/java/com/github/kuangcp/bio/onechatone/ClientThread.java diff --git a/java-network/src/main/java/com/github/kuangcp/bio/onechatone/README.md b/network/src/main/java/com/github/kuangcp/bio/onechatone/README.md similarity index 100% rename from java-network/src/main/java/com/github/kuangcp/bio/onechatone/README.md rename to network/src/main/java/com/github/kuangcp/bio/onechatone/README.md diff --git a/java-network/src/main/java/com/github/kuangcp/bio/onechatone/Server.java b/network/src/main/java/com/github/kuangcp/bio/onechatone/Server.java similarity index 100% rename from java-network/src/main/java/com/github/kuangcp/bio/onechatone/Server.java rename to network/src/main/java/com/github/kuangcp/bio/onechatone/Server.java diff --git a/java-network/src/main/java/com/github/kuangcp/bio/onechatone/ServerThread.java b/network/src/main/java/com/github/kuangcp/bio/onechatone/ServerThread.java similarity index 100% rename from java-network/src/main/java/com/github/kuangcp/bio/onechatone/ServerThread.java rename to network/src/main/java/com/github/kuangcp/bio/onechatone/ServerThread.java diff --git a/java-network/src/main/java/com/github/kuangcp/nio/EchoNIOServer.java b/network/src/main/java/com/github/kuangcp/nio/EchoNIOServer.java similarity index 100% rename from java-network/src/main/java/com/github/kuangcp/nio/EchoNIOServer.java rename to network/src/main/java/com/github/kuangcp/nio/EchoNIOServer.java diff --git a/java-network/src/main/java/com/github/kuangcp/nio/NIOClient.java b/network/src/main/java/com/github/kuangcp/nio/NIOClient.java similarity index 100% rename from java-network/src/main/java/com/github/kuangcp/nio/NIOClient.java rename to network/src/main/java/com/github/kuangcp/nio/NIOClient.java diff --git a/java-network/src/main/java/com/github/kuangcp/nio/NIOServer.java b/network/src/main/java/com/github/kuangcp/nio/NIOServer.java similarity index 100% rename from java-network/src/main/java/com/github/kuangcp/nio/NIOServer.java rename to network/src/main/java/com/github/kuangcp/nio/NIOServer.java diff --git a/java-network/src/main/java/com/github/kuangcp/port/LogicThread.java b/network/src/main/java/com/github/kuangcp/port/LogicThread.java similarity index 100% rename from java-network/src/main/java/com/github/kuangcp/port/LogicThread.java rename to network/src/main/java/com/github/kuangcp/port/LogicThread.java diff --git a/java-network/src/main/java/com/github/kuangcp/port/LogicThreadTest.java b/network/src/main/java/com/github/kuangcp/port/LogicThreadTest.java similarity index 100% rename from java-network/src/main/java/com/github/kuangcp/port/LogicThreadTest.java rename to network/src/main/java/com/github/kuangcp/port/LogicThreadTest.java diff --git a/java-network/src/main/java/com/github/kuangcp/port/MulSocketServer.java b/network/src/main/java/com/github/kuangcp/port/MulSocketServer.java similarity index 100% rename from java-network/src/main/java/com/github/kuangcp/port/MulSocketServer.java rename to network/src/main/java/com/github/kuangcp/port/MulSocketServer.java diff --git a/java-network/src/main/java/com/github/kuangcp/runable/GreetingClient.java b/network/src/main/java/com/github/kuangcp/runable/GreetingClient.java similarity index 100% rename from java-network/src/main/java/com/github/kuangcp/runable/GreetingClient.java rename to network/src/main/java/com/github/kuangcp/runable/GreetingClient.java diff --git a/java-network/src/main/java/com/github/kuangcp/runable/GreetingServer.java b/network/src/main/java/com/github/kuangcp/runable/GreetingServer.java similarity index 100% rename from java-network/src/main/java/com/github/kuangcp/runable/GreetingServer.java rename to network/src/main/java/com/github/kuangcp/runable/GreetingServer.java diff --git a/java-network/src/main/java/com/github/kuangcp/selfclose/SocketSelfClose.java b/network/src/main/java/com/github/kuangcp/selfclose/SocketSelfClose.java similarity index 100% rename from java-network/src/main/java/com/github/kuangcp/selfclose/SocketSelfClose.java rename to network/src/main/java/com/github/kuangcp/selfclose/SocketSelfClose.java diff --git a/java-network/src/main/resources/logback.xml b/network/src/main/resources/logback.xml similarity index 100% rename from java-network/src/main/resources/logback.xml rename to network/src/main/resources/logback.xml diff --git a/java-network/src/test/java/com/github/kuangcp/port/DomainQueryTest.java b/network/src/test/java/com/github/kuangcp/port/DomainQueryTest.java similarity index 100% rename from java-network/src/test/java/com/github/kuangcp/port/DomainQueryTest.java rename to network/src/test/java/com/github/kuangcp/port/DomainQueryTest.java diff --git a/java-network/src/test/java/com/github/kuangcp/runable/TuLingRobotTest.java b/network/src/test/java/com/github/kuangcp/runable/TuLingRobotTest.java similarity index 100% rename from java-network/src/test/java/com/github/kuangcp/runable/TuLingRobotTest.java rename to network/src/test/java/com/github/kuangcp/runable/TuLingRobotTest.java diff --git a/java-pattern/Readme.md b/pattern/Readme.md similarity index 100% rename from java-pattern/Readme.md rename to pattern/Readme.md diff --git a/java-pattern/build.gradle b/pattern/build.gradle similarity index 100% rename from java-pattern/build.gradle rename to pattern/build.gradle diff --git a/java-pattern/src/main/java/com/github/kuangcp/abstractfactory/AbstractFactory.java b/pattern/src/main/java/com/github/kuangcp/abstractfactory/AbstractFactory.java similarity index 100% rename from java-pattern/src/main/java/com/github/kuangcp/abstractfactory/AbstractFactory.java rename to pattern/src/main/java/com/github/kuangcp/abstractfactory/AbstractFactory.java diff --git a/java-pattern/src/main/java/com/github/kuangcp/abstractfactory/ColorFactory.java b/pattern/src/main/java/com/github/kuangcp/abstractfactory/ColorFactory.java similarity index 100% rename from java-pattern/src/main/java/com/github/kuangcp/abstractfactory/ColorFactory.java rename to pattern/src/main/java/com/github/kuangcp/abstractfactory/ColorFactory.java diff --git a/java-pattern/src/main/java/com/github/kuangcp/abstractfactory/FactoryProducer.java b/pattern/src/main/java/com/github/kuangcp/abstractfactory/FactoryProducer.java similarity index 100% rename from java-pattern/src/main/java/com/github/kuangcp/abstractfactory/FactoryProducer.java rename to pattern/src/main/java/com/github/kuangcp/abstractfactory/FactoryProducer.java diff --git a/java-pattern/src/main/java/com/github/kuangcp/abstractfactory/ShapeFactory.java b/pattern/src/main/java/com/github/kuangcp/abstractfactory/ShapeFactory.java similarity index 100% rename from java-pattern/src/main/java/com/github/kuangcp/abstractfactory/ShapeFactory.java rename to pattern/src/main/java/com/github/kuangcp/abstractfactory/ShapeFactory.java diff --git a/java-pattern/src/main/java/com/github/kuangcp/abstractfactory/base/Color.java b/pattern/src/main/java/com/github/kuangcp/abstractfactory/base/Color.java similarity index 100% rename from java-pattern/src/main/java/com/github/kuangcp/abstractfactory/base/Color.java rename to pattern/src/main/java/com/github/kuangcp/abstractfactory/base/Color.java diff --git a/java-pattern/src/main/java/com/github/kuangcp/abstractfactory/base/Shape.java b/pattern/src/main/java/com/github/kuangcp/abstractfactory/base/Shape.java similarity index 100% rename from java-pattern/src/main/java/com/github/kuangcp/abstractfactory/base/Shape.java rename to pattern/src/main/java/com/github/kuangcp/abstractfactory/base/Shape.java diff --git a/java-pattern/src/main/java/com/github/kuangcp/abstractfactory/domain/Green.java b/pattern/src/main/java/com/github/kuangcp/abstractfactory/domain/Green.java similarity index 100% rename from java-pattern/src/main/java/com/github/kuangcp/abstractfactory/domain/Green.java rename to pattern/src/main/java/com/github/kuangcp/abstractfactory/domain/Green.java diff --git a/java-pattern/src/main/java/com/github/kuangcp/abstractfactory/domain/Rectangle.java b/pattern/src/main/java/com/github/kuangcp/abstractfactory/domain/Rectangle.java similarity index 100% rename from java-pattern/src/main/java/com/github/kuangcp/abstractfactory/domain/Rectangle.java rename to pattern/src/main/java/com/github/kuangcp/abstractfactory/domain/Rectangle.java diff --git a/java-pattern/src/main/java/com/github/kuangcp/abstractfactory/domain/Red.java b/pattern/src/main/java/com/github/kuangcp/abstractfactory/domain/Red.java similarity index 100% rename from java-pattern/src/main/java/com/github/kuangcp/abstractfactory/domain/Red.java rename to pattern/src/main/java/com/github/kuangcp/abstractfactory/domain/Red.java diff --git a/java-pattern/src/main/java/com/github/kuangcp/abstractfactory/domain/Square.java b/pattern/src/main/java/com/github/kuangcp/abstractfactory/domain/Square.java similarity index 100% rename from java-pattern/src/main/java/com/github/kuangcp/abstractfactory/domain/Square.java rename to pattern/src/main/java/com/github/kuangcp/abstractfactory/domain/Square.java diff --git a/java-pattern/src/main/java/com/github/kuangcp/decorator/Decorator.java b/pattern/src/main/java/com/github/kuangcp/decorator/Decorator.java similarity index 100% rename from java-pattern/src/main/java/com/github/kuangcp/decorator/Decorator.java rename to pattern/src/main/java/com/github/kuangcp/decorator/Decorator.java diff --git a/java-pattern/src/main/java/com/github/kuangcp/decorator/FootDecorator.java b/pattern/src/main/java/com/github/kuangcp/decorator/FootDecorator.java similarity index 100% rename from java-pattern/src/main/java/com/github/kuangcp/decorator/FootDecorator.java rename to pattern/src/main/java/com/github/kuangcp/decorator/FootDecorator.java diff --git a/java-pattern/src/main/java/com/github/kuangcp/decorator/HeadDecorator.java b/pattern/src/main/java/com/github/kuangcp/decorator/HeadDecorator.java similarity index 100% rename from java-pattern/src/main/java/com/github/kuangcp/decorator/HeadDecorator.java rename to pattern/src/main/java/com/github/kuangcp/decorator/HeadDecorator.java diff --git a/java-pattern/src/main/java/com/github/kuangcp/decorator/Invoice.java b/pattern/src/main/java/com/github/kuangcp/decorator/Invoice.java similarity index 100% rename from java-pattern/src/main/java/com/github/kuangcp/decorator/Invoice.java rename to pattern/src/main/java/com/github/kuangcp/decorator/Invoice.java diff --git a/java-pattern/src/main/java/com/github/kuangcp/decorator/Readme.md b/pattern/src/main/java/com/github/kuangcp/decorator/Readme.md similarity index 100% rename from java-pattern/src/main/java/com/github/kuangcp/decorator/Readme.md rename to pattern/src/main/java/com/github/kuangcp/decorator/Readme.md diff --git a/java-pattern/src/main/java/com/github/kuangcp/singleton/DoubleCheck.java b/pattern/src/main/java/com/github/kuangcp/singleton/DoubleCheck.java similarity index 100% rename from java-pattern/src/main/java/com/github/kuangcp/singleton/DoubleCheck.java rename to pattern/src/main/java/com/github/kuangcp/singleton/DoubleCheck.java diff --git a/java-pattern/src/main/java/com/github/kuangcp/singleton/DoubleCheckWithVolatile.java b/pattern/src/main/java/com/github/kuangcp/singleton/DoubleCheckWithVolatile.java similarity index 100% rename from java-pattern/src/main/java/com/github/kuangcp/singleton/DoubleCheckWithVolatile.java rename to pattern/src/main/java/com/github/kuangcp/singleton/DoubleCheckWithVolatile.java diff --git a/java-pattern/src/main/java/com/github/kuangcp/singleton/Enum.java b/pattern/src/main/java/com/github/kuangcp/singleton/Enum.java similarity index 100% rename from java-pattern/src/main/java/com/github/kuangcp/singleton/Enum.java rename to pattern/src/main/java/com/github/kuangcp/singleton/Enum.java diff --git a/java-pattern/src/main/java/com/github/kuangcp/singleton/Readme.md b/pattern/src/main/java/com/github/kuangcp/singleton/Readme.md similarity index 100% rename from java-pattern/src/main/java/com/github/kuangcp/singleton/Readme.md rename to pattern/src/main/java/com/github/kuangcp/singleton/Readme.md diff --git a/java-pattern/src/main/java/com/github/kuangcp/singleton/StaticBlock.java b/pattern/src/main/java/com/github/kuangcp/singleton/StaticBlock.java similarity index 100% rename from java-pattern/src/main/java/com/github/kuangcp/singleton/StaticBlock.java rename to pattern/src/main/java/com/github/kuangcp/singleton/StaticBlock.java diff --git a/java-pattern/src/main/java/com/github/kuangcp/singleton/StaticFinal.java b/pattern/src/main/java/com/github/kuangcp/singleton/StaticFinal.java similarity index 100% rename from java-pattern/src/main/java/com/github/kuangcp/singleton/StaticFinal.java rename to pattern/src/main/java/com/github/kuangcp/singleton/StaticFinal.java diff --git a/java-pattern/src/main/java/com/github/kuangcp/singleton/StaticInit.java b/pattern/src/main/java/com/github/kuangcp/singleton/StaticInit.java similarity index 100% rename from java-pattern/src/main/java/com/github/kuangcp/singleton/StaticInit.java rename to pattern/src/main/java/com/github/kuangcp/singleton/StaticInit.java diff --git a/java-pattern/src/main/java/com/github/kuangcp/singleton/StaticInnerClass.java b/pattern/src/main/java/com/github/kuangcp/singleton/StaticInnerClass.java similarity index 100% rename from java-pattern/src/main/java/com/github/kuangcp/singleton/StaticInnerClass.java rename to pattern/src/main/java/com/github/kuangcp/singleton/StaticInnerClass.java diff --git a/java-pattern/src/main/java/com/github/kuangcp/singleton/StaticLazyWithSyncBlock.java b/pattern/src/main/java/com/github/kuangcp/singleton/StaticLazyWithSyncBlock.java similarity index 100% rename from java-pattern/src/main/java/com/github/kuangcp/singleton/StaticLazyWithSyncBlock.java rename to pattern/src/main/java/com/github/kuangcp/singleton/StaticLazyWithSyncBlock.java diff --git a/java-pattern/src/main/java/com/github/kuangcp/singleton/StaticLazyWithSyncMethod.java b/pattern/src/main/java/com/github/kuangcp/singleton/StaticLazyWithSyncMethod.java similarity index 100% rename from java-pattern/src/main/java/com/github/kuangcp/singleton/StaticLazyWithSyncMethod.java rename to pattern/src/main/java/com/github/kuangcp/singleton/StaticLazyWithSyncMethod.java diff --git a/java-pattern/src/main/resources/logback.xml b/pattern/src/main/resources/logback.xml similarity index 100% rename from java-pattern/src/main/resources/logback.xml rename to pattern/src/main/resources/logback.xml diff --git a/java-pattern/src/test/java/com/github/kuangcp/abstractfactory/FactoryProducerTest.java b/pattern/src/test/java/com/github/kuangcp/abstractfactory/FactoryProducerTest.java similarity index 100% rename from java-pattern/src/test/java/com/github/kuangcp/abstractfactory/FactoryProducerTest.java rename to pattern/src/test/java/com/github/kuangcp/abstractfactory/FactoryProducerTest.java diff --git a/java-pattern/src/test/java/com/github/kuangcp/decorator/DecoratorTest.java b/pattern/src/test/java/com/github/kuangcp/decorator/DecoratorTest.java similarity index 100% rename from java-pattern/src/test/java/com/github/kuangcp/decorator/DecoratorTest.java rename to pattern/src/test/java/com/github/kuangcp/decorator/DecoratorTest.java diff --git a/java-question/README.md b/question/README.md similarity index 100% rename from java-question/README.md rename to question/README.md diff --git a/java-question/build.gradle b/question/build.gradle similarity index 100% rename from java-question/build.gradle rename to question/build.gradle diff --git a/java-question/src/main/java/com/github/kuangcp/caculator/ListDengShi2.java b/question/src/main/java/com/github/kuangcp/caculator/ListDengShi2.java similarity index 100% rename from java-question/src/main/java/com/github/kuangcp/caculator/ListDengShi2.java rename to question/src/main/java/com/github/kuangcp/caculator/ListDengShi2.java diff --git a/java-question/src/main/java/com/github/kuangcp/caculator/ListExpressionDemo.java b/question/src/main/java/com/github/kuangcp/caculator/ListExpressionDemo.java similarity index 100% rename from java-question/src/main/java/com/github/kuangcp/caculator/ListExpressionDemo.java rename to question/src/main/java/com/github/kuangcp/caculator/ListExpressionDemo.java diff --git a/java-question/src/main/java/com/github/kuangcp/key/CreateKey.java b/question/src/main/java/com/github/kuangcp/key/CreateKey.java similarity index 100% rename from java-question/src/main/java/com/github/kuangcp/key/CreateKey.java rename to question/src/main/java/com/github/kuangcp/key/CreateKey.java diff --git a/java-question/src/main/java/com/github/kuangcp/key/RSAEncoder.java b/question/src/main/java/com/github/kuangcp/key/RSAEncoder.java similarity index 100% rename from java-question/src/main/java/com/github/kuangcp/key/RSAEncoder.java rename to question/src/main/java/com/github/kuangcp/key/RSAEncoder.java diff --git a/java-question/src/main/java/com/github/kuangcp/simpleMethod/README.md b/question/src/main/java/com/github/kuangcp/simpleMethod/README.md similarity index 100% rename from java-question/src/main/java/com/github/kuangcp/simpleMethod/README.md rename to question/src/main/java/com/github/kuangcp/simpleMethod/README.md diff --git a/java-question/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethod/Equality.java b/question/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethod/Equality.java similarity index 100% rename from java-question/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethod/Equality.java rename to question/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethod/Equality.java diff --git a/java-question/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethod/SimplexMethod.java b/question/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethod/SimplexMethod.java similarity index 100% rename from java-question/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethod/SimplexMethod.java rename to question/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethod/SimplexMethod.java diff --git a/java-question/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethod/Table.java b/question/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethod/Table.java similarity index 100% rename from java-question/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethod/Table.java rename to question/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethod/Table.java diff --git a/java-question/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethodQuarter/Equality.java b/question/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethodQuarter/Equality.java similarity index 100% rename from java-question/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethodQuarter/Equality.java rename to question/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethodQuarter/Equality.java diff --git a/java-question/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethodQuarter/SimplexMethod.java b/question/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethodQuarter/SimplexMethod.java similarity index 100% rename from java-question/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethodQuarter/SimplexMethod.java rename to question/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethodQuarter/SimplexMethod.java diff --git a/java-question/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethodQuarter/Table.java b/question/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethodQuarter/Table.java similarity index 100% rename from java-question/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethodQuarter/Table.java rename to question/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethodQuarter/Table.java diff --git a/java-question/src/main/java/com/github/kuangcp/simpleMethod/number/ReadProperties.java b/question/src/main/java/com/github/kuangcp/simpleMethod/number/ReadProperties.java similarity index 100% rename from java-question/src/main/java/com/github/kuangcp/simpleMethod/number/ReadProperties.java rename to question/src/main/java/com/github/kuangcp/simpleMethod/number/ReadProperties.java diff --git a/java-question/src/main/resources/math/SimplexMethod.properties b/question/src/main/resources/math/SimplexMethod.properties similarity index 100% rename from java-question/src/main/resources/math/SimplexMethod.properties rename to question/src/main/resources/math/SimplexMethod.properties diff --git a/java-question/src/main/resources/math/log4j.xml b/question/src/main/resources/math/log4j.xml similarity index 100% rename from java-question/src/main/resources/math/log4j.xml rename to question/src/main/resources/math/log4j.xml diff --git a/java-question/src/test/java/com/github/kuangcp/key/CreateKeyTest.java b/question/src/test/java/com/github/kuangcp/key/CreateKeyTest.java similarity index 100% rename from java-question/src/test/java/com/github/kuangcp/key/CreateKeyTest.java rename to question/src/test/java/com/github/kuangcp/key/CreateKeyTest.java diff --git a/java-question/src/test/java/com/github/kuangcp/math/AbsTest.java b/question/src/test/java/com/github/kuangcp/math/AbsTest.java similarity index 100% rename from java-question/src/test/java/com/github/kuangcp/math/AbsTest.java rename to question/src/test/java/com/github/kuangcp/math/AbsTest.java diff --git a/java-question/src/test/java/com/github/kuangcp/simpleMethod/SimplexMethodQuarter/MethodTest.java b/question/src/test/java/com/github/kuangcp/simpleMethod/SimplexMethodQuarter/MethodTest.java similarity index 100% rename from java-question/src/test/java/com/github/kuangcp/simpleMethod/SimplexMethodQuarter/MethodTest.java rename to question/src/test/java/com/github/kuangcp/simpleMethod/SimplexMethodQuarter/MethodTest.java diff --git a/java-question/src/test/java/scattered/SmoothingTheData.java b/question/src/test/java/scattered/SmoothingTheData.java similarity index 100% rename from java-question/src/test/java/scattered/SmoothingTheData.java rename to question/src/test/java/scattered/SmoothingTheData.java diff --git a/settings.gradle b/settings.gradle index b1e51939..1935f692 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,20 +1,21 @@ rootProject.name = 'JavaBase' include( - 'java-io' - , 'java-generic' - , 'java-collection' - , 'java-class' - , 'java-gui' - , 'java-network' - , 'java-algorithms' - , 'java-question' - , 'java-pattern' - , 'java-8' - , 'java-concurrency' - , 'java-test' - , 'java-guava' - , 'java-netty' - , 'java-spring' - , 'java-kafka' - , 'java-hadoop' + 'io' + , 'generic' + , 'collection' + , 'class' + , 'gui' + , 'network' + , 'algorithms' + , 'question' + , 'pattern' + , 'java8' + , 'concurrency' + , 'test' + , 'guava' + , 'netty' + , 'spring' + , 'kafka' + , 'hadoop' + , 'flink' ) diff --git a/java-spring/.gitignore b/spring/.gitignore similarity index 100% rename from java-spring/.gitignore rename to spring/.gitignore diff --git a/java-spring/Readme.md b/spring/Readme.md similarity index 100% rename from java-spring/Readme.md rename to spring/Readme.md diff --git a/java-spring/build.gradle b/spring/build.gradle similarity index 100% rename from java-spring/build.gradle rename to spring/build.gradle diff --git a/java-spring/src/main/java/com/github/kuangcp/proxy/dao/base/CustomInterceptor.java b/spring/src/main/java/com/github/kuangcp/proxy/dao/base/CustomInterceptor.java similarity index 100% rename from java-spring/src/main/java/com/github/kuangcp/proxy/dao/base/CustomInterceptor.java rename to spring/src/main/java/com/github/kuangcp/proxy/dao/base/CustomInterceptor.java diff --git a/java-spring/src/main/java/com/github/kuangcp/proxy/dao/base/Person.java b/spring/src/main/java/com/github/kuangcp/proxy/dao/base/Person.java similarity index 100% rename from java-spring/src/main/java/com/github/kuangcp/proxy/dao/base/Person.java rename to spring/src/main/java/com/github/kuangcp/proxy/dao/base/Person.java diff --git a/java-spring/src/main/java/com/github/kuangcp/proxy/dao/base/Transaction.java b/spring/src/main/java/com/github/kuangcp/proxy/dao/base/Transaction.java similarity index 100% rename from java-spring/src/main/java/com/github/kuangcp/proxy/dao/base/Transaction.java rename to spring/src/main/java/com/github/kuangcp/proxy/dao/base/Transaction.java diff --git a/java-spring/src/main/java/com/github/kuangcp/proxy/dao/cglibproxy/PersonDaoImpl.java b/spring/src/main/java/com/github/kuangcp/proxy/dao/cglibproxy/PersonDaoImpl.java similarity index 100% rename from java-spring/src/main/java/com/github/kuangcp/proxy/dao/cglibproxy/PersonDaoImpl.java rename to spring/src/main/java/com/github/kuangcp/proxy/dao/cglibproxy/PersonDaoImpl.java diff --git a/java-spring/src/main/java/com/github/kuangcp/proxy/dao/cglibproxy/PersonDaoInterceptor.java b/spring/src/main/java/com/github/kuangcp/proxy/dao/cglibproxy/PersonDaoInterceptor.java similarity index 100% rename from java-spring/src/main/java/com/github/kuangcp/proxy/dao/cglibproxy/PersonDaoInterceptor.java rename to spring/src/main/java/com/github/kuangcp/proxy/dao/cglibproxy/PersonDaoInterceptor.java diff --git a/java-spring/src/main/java/com/github/kuangcp/proxy/dao/jdkproxy/PersonDao.java b/spring/src/main/java/com/github/kuangcp/proxy/dao/jdkproxy/PersonDao.java similarity index 100% rename from java-spring/src/main/java/com/github/kuangcp/proxy/dao/jdkproxy/PersonDao.java rename to spring/src/main/java/com/github/kuangcp/proxy/dao/jdkproxy/PersonDao.java diff --git a/java-spring/src/main/java/com/github/kuangcp/proxy/dao/jdkproxy/PersonDaoImpl.java b/spring/src/main/java/com/github/kuangcp/proxy/dao/jdkproxy/PersonDaoImpl.java similarity index 100% rename from java-spring/src/main/java/com/github/kuangcp/proxy/dao/jdkproxy/PersonDaoImpl.java rename to spring/src/main/java/com/github/kuangcp/proxy/dao/jdkproxy/PersonDaoImpl.java diff --git a/java-spring/src/main/java/com/github/kuangcp/proxy/dao/jdkproxy/PersonDaoInterceptor.java b/spring/src/main/java/com/github/kuangcp/proxy/dao/jdkproxy/PersonDaoInterceptor.java similarity index 100% rename from java-spring/src/main/java/com/github/kuangcp/proxy/dao/jdkproxy/PersonDaoInterceptor.java rename to spring/src/main/java/com/github/kuangcp/proxy/dao/jdkproxy/PersonDaoInterceptor.java diff --git a/java-spring/src/main/resources/logback.xml b/spring/src/main/resources/logback.xml similarity index 100% rename from java-spring/src/main/resources/logback.xml rename to spring/src/main/resources/logback.xml diff --git a/java-spring/src/test/java/com/github/kuangcp/aop/ExceptionTest.java b/spring/src/test/java/com/github/kuangcp/aop/ExceptionTest.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/aop/ExceptionTest.java rename to spring/src/test/java/com/github/kuangcp/aop/ExceptionTest.java diff --git a/java-spring/src/test/java/com/github/kuangcp/aop/annotation/Person.java b/spring/src/test/java/com/github/kuangcp/aop/annotation/Person.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/aop/annotation/Person.java rename to spring/src/test/java/com/github/kuangcp/aop/annotation/Person.java diff --git a/java-spring/src/test/java/com/github/kuangcp/aop/annotation/PersonDao.java b/spring/src/test/java/com/github/kuangcp/aop/annotation/PersonDao.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/aop/annotation/PersonDao.java rename to spring/src/test/java/com/github/kuangcp/aop/annotation/PersonDao.java diff --git a/java-spring/src/test/java/com/github/kuangcp/aop/annotation/PersonDaoImpl.java b/spring/src/test/java/com/github/kuangcp/aop/annotation/PersonDaoImpl.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/aop/annotation/PersonDaoImpl.java rename to spring/src/test/java/com/github/kuangcp/aop/annotation/PersonDaoImpl.java diff --git a/java-spring/src/test/java/com/github/kuangcp/aop/annotation/PersonTest.java b/spring/src/test/java/com/github/kuangcp/aop/annotation/PersonTest.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/aop/annotation/PersonTest.java rename to spring/src/test/java/com/github/kuangcp/aop/annotation/PersonTest.java diff --git a/java-spring/src/test/java/com/github/kuangcp/aop/annotation/Transaction.java b/spring/src/test/java/com/github/kuangcp/aop/annotation/Transaction.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/aop/annotation/Transaction.java rename to spring/src/test/java/com/github/kuangcp/aop/annotation/Transaction.java diff --git a/java-spring/src/test/java/com/github/kuangcp/aop/annotation/applicationContext.xml b/spring/src/test/java/com/github/kuangcp/aop/annotation/applicationContext.xml similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/aop/annotation/applicationContext.xml rename to spring/src/test/java/com/github/kuangcp/aop/annotation/applicationContext.xml diff --git a/java-spring/src/test/java/com/github/kuangcp/aop/applicationContext-exception.xml b/spring/src/test/java/com/github/kuangcp/aop/applicationContext-exception.xml similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/aop/applicationContext-exception.xml rename to spring/src/test/java/com/github/kuangcp/aop/applicationContext-exception.xml diff --git a/java-spring/src/test/java/com/github/kuangcp/aop/exception/MyException.java b/spring/src/test/java/com/github/kuangcp/aop/exception/MyException.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/aop/exception/MyException.java rename to spring/src/test/java/com/github/kuangcp/aop/exception/MyException.java diff --git a/java-spring/src/test/java/com/github/kuangcp/aop/exception/Readme.md b/spring/src/test/java/com/github/kuangcp/aop/exception/Readme.md similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/aop/exception/Readme.md rename to spring/src/test/java/com/github/kuangcp/aop/exception/Readme.md diff --git a/java-spring/src/test/java/com/github/kuangcp/aop/exception/action/PersonAction.java b/spring/src/test/java/com/github/kuangcp/aop/exception/action/PersonAction.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/aop/exception/action/PersonAction.java rename to spring/src/test/java/com/github/kuangcp/aop/exception/action/PersonAction.java diff --git a/java-spring/src/test/java/com/github/kuangcp/aop/exception/dao/PersonDao.java b/spring/src/test/java/com/github/kuangcp/aop/exception/dao/PersonDao.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/aop/exception/dao/PersonDao.java rename to spring/src/test/java/com/github/kuangcp/aop/exception/dao/PersonDao.java diff --git a/java-spring/src/test/java/com/github/kuangcp/aop/exception/dao/PersonDaoImpl.java b/spring/src/test/java/com/github/kuangcp/aop/exception/dao/PersonDaoImpl.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/aop/exception/dao/PersonDaoImpl.java rename to spring/src/test/java/com/github/kuangcp/aop/exception/dao/PersonDaoImpl.java diff --git a/java-spring/src/test/java/com/github/kuangcp/aop/exception/service/PersonService.java b/spring/src/test/java/com/github/kuangcp/aop/exception/service/PersonService.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/aop/exception/service/PersonService.java rename to spring/src/test/java/com/github/kuangcp/aop/exception/service/PersonService.java diff --git a/java-spring/src/test/java/com/github/kuangcp/aop/exception/service/PersonServiceImpl.java b/spring/src/test/java/com/github/kuangcp/aop/exception/service/PersonServiceImpl.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/aop/exception/service/PersonServiceImpl.java rename to spring/src/test/java/com/github/kuangcp/aop/exception/service/PersonServiceImpl.java diff --git a/java-spring/src/test/java/com/github/kuangcp/aop/xml/Person.java b/spring/src/test/java/com/github/kuangcp/aop/xml/Person.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/aop/xml/Person.java rename to spring/src/test/java/com/github/kuangcp/aop/xml/Person.java diff --git a/java-spring/src/test/java/com/github/kuangcp/aop/xml/PersonDao.java b/spring/src/test/java/com/github/kuangcp/aop/xml/PersonDao.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/aop/xml/PersonDao.java rename to spring/src/test/java/com/github/kuangcp/aop/xml/PersonDao.java diff --git a/java-spring/src/test/java/com/github/kuangcp/aop/xml/PersonDaoImpl.java b/spring/src/test/java/com/github/kuangcp/aop/xml/PersonDaoImpl.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/aop/xml/PersonDaoImpl.java rename to spring/src/test/java/com/github/kuangcp/aop/xml/PersonDaoImpl.java diff --git a/java-spring/src/test/java/com/github/kuangcp/aop/xml/PersonTest.java b/spring/src/test/java/com/github/kuangcp/aop/xml/PersonTest.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/aop/xml/PersonTest.java rename to spring/src/test/java/com/github/kuangcp/aop/xml/PersonTest.java diff --git a/java-spring/src/test/java/com/github/kuangcp/aop/xml/Readme.md b/spring/src/test/java/com/github/kuangcp/aop/xml/Readme.md similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/aop/xml/Readme.md rename to spring/src/test/java/com/github/kuangcp/aop/xml/Readme.md diff --git a/java-spring/src/test/java/com/github/kuangcp/aop/xml/Transaction.java b/spring/src/test/java/com/github/kuangcp/aop/xml/Transaction.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/aop/xml/Transaction.java rename to spring/src/test/java/com/github/kuangcp/aop/xml/Transaction.java diff --git a/java-spring/src/test/java/com/github/kuangcp/aop/xml/applicationContext.xml b/spring/src/test/java/com/github/kuangcp/aop/xml/applicationContext.xml similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/aop/xml/applicationContext.xml rename to spring/src/test/java/com/github/kuangcp/aop/xml/applicationContext.xml diff --git a/java-spring/src/test/java/com/github/kuangcp/aop/xml/salary/Logger.java b/spring/src/test/java/com/github/kuangcp/aop/xml/salary/Logger.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/aop/xml/salary/Logger.java rename to spring/src/test/java/com/github/kuangcp/aop/xml/salary/Logger.java diff --git a/java-spring/src/test/java/com/github/kuangcp/aop/xml/salary/Privilege.java b/spring/src/test/java/com/github/kuangcp/aop/xml/salary/Privilege.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/aop/xml/salary/Privilege.java rename to spring/src/test/java/com/github/kuangcp/aop/xml/salary/Privilege.java diff --git a/java-spring/src/test/java/com/github/kuangcp/aop/xml/salary/Readmd.md b/spring/src/test/java/com/github/kuangcp/aop/xml/salary/Readmd.md similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/aop/xml/salary/Readmd.md rename to spring/src/test/java/com/github/kuangcp/aop/xml/salary/Readmd.md diff --git a/java-spring/src/test/java/com/github/kuangcp/aop/xml/salary/SalaryManager.java b/spring/src/test/java/com/github/kuangcp/aop/xml/salary/SalaryManager.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/aop/xml/salary/SalaryManager.java rename to spring/src/test/java/com/github/kuangcp/aop/xml/salary/SalaryManager.java diff --git a/java-spring/src/test/java/com/github/kuangcp/aop/xml/salary/SalaryTest.java b/spring/src/test/java/com/github/kuangcp/aop/xml/salary/SalaryTest.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/aop/xml/salary/SalaryTest.java rename to spring/src/test/java/com/github/kuangcp/aop/xml/salary/SalaryTest.java diff --git a/java-spring/src/test/java/com/github/kuangcp/aop/xml/salary/Security.java b/spring/src/test/java/com/github/kuangcp/aop/xml/salary/Security.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/aop/xml/salary/Security.java rename to spring/src/test/java/com/github/kuangcp/aop/xml/salary/Security.java diff --git a/java-spring/src/test/java/com/github/kuangcp/aop/xml/salary/applicationContext.xml b/spring/src/test/java/com/github/kuangcp/aop/xml/salary/applicationContext.xml similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/aop/xml/salary/applicationContext.xml rename to spring/src/test/java/com/github/kuangcp/aop/xml/salary/applicationContext.xml diff --git a/java-spring/src/test/java/com/github/kuangcp/di/annotation/AnnotationParse.java b/spring/src/test/java/com/github/kuangcp/di/annotation/AnnotationParse.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/di/annotation/AnnotationParse.java rename to spring/src/test/java/com/github/kuangcp/di/annotation/AnnotationParse.java diff --git a/java-spring/src/test/java/com/github/kuangcp/di/annotation/Description.java b/spring/src/test/java/com/github/kuangcp/di/annotation/Description.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/di/annotation/Description.java rename to spring/src/test/java/com/github/kuangcp/di/annotation/Description.java diff --git a/java-spring/src/test/java/com/github/kuangcp/di/annotation/ITCAST.java b/spring/src/test/java/com/github/kuangcp/di/annotation/ITCAST.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/di/annotation/ITCAST.java rename to spring/src/test/java/com/github/kuangcp/di/annotation/ITCAST.java diff --git a/java-spring/src/test/java/com/github/kuangcp/di/annotation/Name.java b/spring/src/test/java/com/github/kuangcp/di/annotation/Name.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/di/annotation/Name.java rename to spring/src/test/java/com/github/kuangcp/di/annotation/Name.java diff --git a/java-spring/src/test/java/com/github/kuangcp/di/di/annotation/Person.java b/spring/src/test/java/com/github/kuangcp/di/di/annotation/Person.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/di/di/annotation/Person.java rename to spring/src/test/java/com/github/kuangcp/di/di/annotation/Person.java diff --git a/java-spring/src/test/java/com/github/kuangcp/di/di/annotation/PersonTest.java b/spring/src/test/java/com/github/kuangcp/di/di/annotation/PersonTest.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/di/di/annotation/PersonTest.java rename to spring/src/test/java/com/github/kuangcp/di/di/annotation/PersonTest.java diff --git a/java-spring/src/test/java/com/github/kuangcp/di/di/annotation/Student.java b/spring/src/test/java/com/github/kuangcp/di/di/annotation/Student.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/di/di/annotation/Student.java rename to spring/src/test/java/com/github/kuangcp/di/di/annotation/Student.java diff --git a/java-spring/src/test/java/com/github/kuangcp/di/di/annotation/applicationContext.xml b/spring/src/test/java/com/github/kuangcp/di/di/annotation/applicationContext.xml similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/di/di/annotation/applicationContext.xml rename to spring/src/test/java/com/github/kuangcp/di/di/annotation/applicationContext.xml diff --git a/java-spring/src/test/java/com/github/kuangcp/di/di/xml/constructor/Person.java b/spring/src/test/java/com/github/kuangcp/di/di/xml/constructor/Person.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/di/di/xml/constructor/Person.java rename to spring/src/test/java/com/github/kuangcp/di/di/xml/constructor/Person.java diff --git a/java-spring/src/test/java/com/github/kuangcp/di/di/xml/constructor/PersonTest.java b/spring/src/test/java/com/github/kuangcp/di/di/xml/constructor/PersonTest.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/di/di/xml/constructor/PersonTest.java rename to spring/src/test/java/com/github/kuangcp/di/di/xml/constructor/PersonTest.java diff --git a/java-spring/src/test/java/com/github/kuangcp/di/di/xml/constructor/Student.java b/spring/src/test/java/com/github/kuangcp/di/di/xml/constructor/Student.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/di/di/xml/constructor/Student.java rename to spring/src/test/java/com/github/kuangcp/di/di/xml/constructor/Student.java diff --git a/java-spring/src/test/java/com/github/kuangcp/di/di/xml/constructor/applicationContext.xml b/spring/src/test/java/com/github/kuangcp/di/di/xml/constructor/applicationContext.xml similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/di/di/xml/constructor/applicationContext.xml rename to spring/src/test/java/com/github/kuangcp/di/di/xml/constructor/applicationContext.xml diff --git a/java-spring/src/test/java/com/github/kuangcp/di/di/xml/set/Person.java b/spring/src/test/java/com/github/kuangcp/di/di/xml/set/Person.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/di/di/xml/set/Person.java rename to spring/src/test/java/com/github/kuangcp/di/di/xml/set/Person.java diff --git a/java-spring/src/test/java/com/github/kuangcp/di/di/xml/set/PersonTest.java b/spring/src/test/java/com/github/kuangcp/di/di/xml/set/PersonTest.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/di/di/xml/set/PersonTest.java rename to spring/src/test/java/com/github/kuangcp/di/di/xml/set/PersonTest.java diff --git a/java-spring/src/test/java/com/github/kuangcp/di/di/xml/set/Student.java b/spring/src/test/java/com/github/kuangcp/di/di/xml/set/Student.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/di/di/xml/set/Student.java rename to spring/src/test/java/com/github/kuangcp/di/di/xml/set/Student.java diff --git a/java-spring/src/test/java/com/github/kuangcp/di/di/xml/set/applicationContext.xml b/spring/src/test/java/com/github/kuangcp/di/di/xml/set/applicationContext.xml similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/di/di/xml/set/applicationContext.xml rename to spring/src/test/java/com/github/kuangcp/di/di/xml/set/applicationContext.xml diff --git a/java-spring/src/test/java/com/github/kuangcp/di/scan/Person.java b/spring/src/test/java/com/github/kuangcp/di/scan/Person.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/di/scan/Person.java rename to spring/src/test/java/com/github/kuangcp/di/scan/Person.java diff --git a/java-spring/src/test/java/com/github/kuangcp/di/scan/PersonTest.java b/spring/src/test/java/com/github/kuangcp/di/scan/PersonTest.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/di/scan/PersonTest.java rename to spring/src/test/java/com/github/kuangcp/di/scan/PersonTest.java diff --git a/java-spring/src/test/java/com/github/kuangcp/di/scan/Student.java b/spring/src/test/java/com/github/kuangcp/di/scan/Student.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/di/scan/Student.java rename to spring/src/test/java/com/github/kuangcp/di/scan/Student.java diff --git a/java-spring/src/test/java/com/github/kuangcp/di/scan/applicationContext.xml b/spring/src/test/java/com/github/kuangcp/di/scan/applicationContext.xml similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/di/scan/applicationContext.xml rename to spring/src/test/java/com/github/kuangcp/di/scan/applicationContext.xml diff --git a/java-spring/src/test/java/com/github/kuangcp/document/Document.java b/spring/src/test/java/com/github/kuangcp/document/Document.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/document/Document.java rename to spring/src/test/java/com/github/kuangcp/document/Document.java diff --git a/java-spring/src/test/java/com/github/kuangcp/document/DocumentManager.java b/spring/src/test/java/com/github/kuangcp/document/DocumentManager.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/document/DocumentManager.java rename to spring/src/test/java/com/github/kuangcp/document/DocumentManager.java diff --git a/java-spring/src/test/java/com/github/kuangcp/document/DocumentTest.java b/spring/src/test/java/com/github/kuangcp/document/DocumentTest.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/document/DocumentTest.java rename to spring/src/test/java/com/github/kuangcp/document/DocumentTest.java diff --git a/java-spring/src/test/java/com/github/kuangcp/document/ExcelDocument.java b/spring/src/test/java/com/github/kuangcp/document/ExcelDocument.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/document/ExcelDocument.java rename to spring/src/test/java/com/github/kuangcp/document/ExcelDocument.java diff --git a/java-spring/src/test/java/com/github/kuangcp/document/PDFDocument.java b/spring/src/test/java/com/github/kuangcp/document/PDFDocument.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/document/PDFDocument.java rename to spring/src/test/java/com/github/kuangcp/document/PDFDocument.java diff --git a/java-spring/src/test/java/com/github/kuangcp/document/WordDocument.java b/spring/src/test/java/com/github/kuangcp/document/WordDocument.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/document/WordDocument.java rename to spring/src/test/java/com/github/kuangcp/document/WordDocument.java diff --git a/java-spring/src/test/java/com/github/kuangcp/document/spring/Document.java b/spring/src/test/java/com/github/kuangcp/document/spring/Document.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/document/spring/Document.java rename to spring/src/test/java/com/github/kuangcp/document/spring/Document.java diff --git a/java-spring/src/test/java/com/github/kuangcp/document/spring/DocumentManager.java b/spring/src/test/java/com/github/kuangcp/document/spring/DocumentManager.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/document/spring/DocumentManager.java rename to spring/src/test/java/com/github/kuangcp/document/spring/DocumentManager.java diff --git a/java-spring/src/test/java/com/github/kuangcp/document/spring/DocumentTest.java b/spring/src/test/java/com/github/kuangcp/document/spring/DocumentTest.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/document/spring/DocumentTest.java rename to spring/src/test/java/com/github/kuangcp/document/spring/DocumentTest.java diff --git a/java-spring/src/test/java/com/github/kuangcp/document/spring/ExcelDocument.java b/spring/src/test/java/com/github/kuangcp/document/spring/ExcelDocument.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/document/spring/ExcelDocument.java rename to spring/src/test/java/com/github/kuangcp/document/spring/ExcelDocument.java diff --git a/java-spring/src/test/java/com/github/kuangcp/document/spring/PDFDocument.java b/spring/src/test/java/com/github/kuangcp/document/spring/PDFDocument.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/document/spring/PDFDocument.java rename to spring/src/test/java/com/github/kuangcp/document/spring/PDFDocument.java diff --git a/java-spring/src/test/java/com/github/kuangcp/document/spring/WordDocument.java b/spring/src/test/java/com/github/kuangcp/document/spring/WordDocument.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/document/spring/WordDocument.java rename to spring/src/test/java/com/github/kuangcp/document/spring/WordDocument.java diff --git a/java-spring/src/test/java/com/github/kuangcp/document/spring/applicationContext.xml b/spring/src/test/java/com/github/kuangcp/document/spring/applicationContext.xml similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/document/spring/applicationContext.xml rename to spring/src/test/java/com/github/kuangcp/document/spring/applicationContext.xml diff --git a/java-spring/src/test/java/com/github/kuangcp/exception/ExceptionTest.java b/spring/src/test/java/com/github/kuangcp/exception/ExceptionTest.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/exception/ExceptionTest.java rename to spring/src/test/java/com/github/kuangcp/exception/ExceptionTest.java diff --git a/java-spring/src/test/java/com/github/kuangcp/exception/Readme.md b/spring/src/test/java/com/github/kuangcp/exception/Readme.md similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/exception/Readme.md rename to spring/src/test/java/com/github/kuangcp/exception/Readme.md diff --git a/java-spring/src/test/java/com/github/kuangcp/exception/ServiceInvocation.java b/spring/src/test/java/com/github/kuangcp/exception/ServiceInvocation.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/exception/ServiceInvocation.java rename to spring/src/test/java/com/github/kuangcp/exception/ServiceInvocation.java diff --git a/java-spring/src/test/java/com/github/kuangcp/exception/ServiceMapping.java b/spring/src/test/java/com/github/kuangcp/exception/ServiceMapping.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/exception/ServiceMapping.java rename to spring/src/test/java/com/github/kuangcp/exception/ServiceMapping.java diff --git a/java-spring/src/test/java/com/github/kuangcp/exception/service/PersonServiceImpl.java b/spring/src/test/java/com/github/kuangcp/exception/service/PersonServiceImpl.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/exception/service/PersonServiceImpl.java rename to spring/src/test/java/com/github/kuangcp/exception/service/PersonServiceImpl.java diff --git a/java-spring/src/test/java/com/github/kuangcp/exception/service/Service.java b/spring/src/test/java/com/github/kuangcp/exception/service/Service.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/exception/service/Service.java rename to spring/src/test/java/com/github/kuangcp/exception/service/Service.java diff --git a/java-spring/src/test/java/com/github/kuangcp/exception/service/StudentServiceImpl.java b/spring/src/test/java/com/github/kuangcp/exception/service/StudentServiceImpl.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/exception/service/StudentServiceImpl.java rename to spring/src/test/java/com/github/kuangcp/exception/service/StudentServiceImpl.java diff --git a/java-spring/src/test/java/com/github/kuangcp/extend/Person.java b/spring/src/test/java/com/github/kuangcp/extend/Person.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/extend/Person.java rename to spring/src/test/java/com/github/kuangcp/extend/Person.java diff --git a/java-spring/src/test/java/com/github/kuangcp/extend/PersonTest.java b/spring/src/test/java/com/github/kuangcp/extend/PersonTest.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/extend/PersonTest.java rename to spring/src/test/java/com/github/kuangcp/extend/PersonTest.java diff --git a/java-spring/src/test/java/com/github/kuangcp/extend/Student.java b/spring/src/test/java/com/github/kuangcp/extend/Student.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/extend/Student.java rename to spring/src/test/java/com/github/kuangcp/extend/Student.java diff --git a/java-spring/src/test/java/com/github/kuangcp/extend/applicationContext.xml b/spring/src/test/java/com/github/kuangcp/extend/applicationContext.xml similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/extend/applicationContext.xml rename to spring/src/test/java/com/github/kuangcp/extend/applicationContext.xml diff --git a/java-spring/src/test/java/com/github/kuangcp/extend/log4j.xml b/spring/src/test/java/com/github/kuangcp/extend/log4j.xml similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/extend/log4j.xml rename to spring/src/test/java/com/github/kuangcp/extend/log4j.xml diff --git a/java-spring/src/test/java/com/github/kuangcp/hibernate/annotation/Person.hbm.xml b/spring/src/test/java/com/github/kuangcp/hibernate/annotation/Person.hbm.xml similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/hibernate/annotation/Person.hbm.xml rename to spring/src/test/java/com/github/kuangcp/hibernate/annotation/Person.hbm.xml diff --git a/java-spring/src/test/java/com/github/kuangcp/hibernate/annotation/Person.java b/spring/src/test/java/com/github/kuangcp/hibernate/annotation/Person.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/hibernate/annotation/Person.java rename to spring/src/test/java/com/github/kuangcp/hibernate/annotation/Person.java diff --git a/java-spring/src/test/java/com/github/kuangcp/hibernate/annotation/PersonDao.java b/spring/src/test/java/com/github/kuangcp/hibernate/annotation/PersonDao.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/hibernate/annotation/PersonDao.java rename to spring/src/test/java/com/github/kuangcp/hibernate/annotation/PersonDao.java diff --git a/java-spring/src/test/java/com/github/kuangcp/hibernate/annotation/PersonDaoImpl.java b/spring/src/test/java/com/github/kuangcp/hibernate/annotation/PersonDaoImpl.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/hibernate/annotation/PersonDaoImpl.java rename to spring/src/test/java/com/github/kuangcp/hibernate/annotation/PersonDaoImpl.java diff --git a/java-spring/src/test/java/com/github/kuangcp/hibernate/annotation/PersonService.java b/spring/src/test/java/com/github/kuangcp/hibernate/annotation/PersonService.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/hibernate/annotation/PersonService.java rename to spring/src/test/java/com/github/kuangcp/hibernate/annotation/PersonService.java diff --git a/java-spring/src/test/java/com/github/kuangcp/hibernate/annotation/PersonServiceImpl.java b/spring/src/test/java/com/github/kuangcp/hibernate/annotation/PersonServiceImpl.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/hibernate/annotation/PersonServiceImpl.java rename to spring/src/test/java/com/github/kuangcp/hibernate/annotation/PersonServiceImpl.java diff --git a/java-spring/src/test/java/com/github/kuangcp/hibernate/annotation/PersonTest.java b/spring/src/test/java/com/github/kuangcp/hibernate/annotation/PersonTest.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/hibernate/annotation/PersonTest.java rename to spring/src/test/java/com/github/kuangcp/hibernate/annotation/PersonTest.java diff --git a/java-spring/src/test/java/com/github/kuangcp/hibernate/annotation/applicationContext.xml b/spring/src/test/java/com/github/kuangcp/hibernate/annotation/applicationContext.xml similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/hibernate/annotation/applicationContext.xml rename to spring/src/test/java/com/github/kuangcp/hibernate/annotation/applicationContext.xml diff --git a/java-spring/src/test/java/com/github/kuangcp/hibernate/hibernate.cfg.xml b/spring/src/test/java/com/github/kuangcp/hibernate/hibernate.cfg.xml similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/hibernate/hibernate.cfg.xml rename to spring/src/test/java/com/github/kuangcp/hibernate/hibernate.cfg.xml diff --git a/java-spring/src/test/java/com/github/kuangcp/hibernate/xml/Person.hbm.xml b/spring/src/test/java/com/github/kuangcp/hibernate/xml/Person.hbm.xml similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/hibernate/xml/Person.hbm.xml rename to spring/src/test/java/com/github/kuangcp/hibernate/xml/Person.hbm.xml diff --git a/java-spring/src/test/java/com/github/kuangcp/hibernate/xml/Person.java b/spring/src/test/java/com/github/kuangcp/hibernate/xml/Person.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/hibernate/xml/Person.java rename to spring/src/test/java/com/github/kuangcp/hibernate/xml/Person.java diff --git a/java-spring/src/test/java/com/github/kuangcp/hibernate/xml/PersonDao.java b/spring/src/test/java/com/github/kuangcp/hibernate/xml/PersonDao.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/hibernate/xml/PersonDao.java rename to spring/src/test/java/com/github/kuangcp/hibernate/xml/PersonDao.java diff --git a/java-spring/src/test/java/com/github/kuangcp/hibernate/xml/PersonDaoImpl.java b/spring/src/test/java/com/github/kuangcp/hibernate/xml/PersonDaoImpl.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/hibernate/xml/PersonDaoImpl.java rename to spring/src/test/java/com/github/kuangcp/hibernate/xml/PersonDaoImpl.java diff --git a/java-spring/src/test/java/com/github/kuangcp/hibernate/xml/PersonService.java b/spring/src/test/java/com/github/kuangcp/hibernate/xml/PersonService.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/hibernate/xml/PersonService.java rename to spring/src/test/java/com/github/kuangcp/hibernate/xml/PersonService.java diff --git a/java-spring/src/test/java/com/github/kuangcp/hibernate/xml/PersonServiceImpl.java b/spring/src/test/java/com/github/kuangcp/hibernate/xml/PersonServiceImpl.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/hibernate/xml/PersonServiceImpl.java rename to spring/src/test/java/com/github/kuangcp/hibernate/xml/PersonServiceImpl.java diff --git a/java-spring/src/test/java/com/github/kuangcp/hibernate/xml/PersonTest.java b/spring/src/test/java/com/github/kuangcp/hibernate/xml/PersonTest.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/hibernate/xml/PersonTest.java rename to spring/src/test/java/com/github/kuangcp/hibernate/xml/PersonTest.java diff --git a/java-spring/src/test/java/com/github/kuangcp/hibernate/xml/Readme.md b/spring/src/test/java/com/github/kuangcp/hibernate/xml/Readme.md similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/hibernate/xml/Readme.md rename to spring/src/test/java/com/github/kuangcp/hibernate/xml/Readme.md diff --git a/java-spring/src/test/java/com/github/kuangcp/hibernate/xml/applicationContext.xml b/spring/src/test/java/com/github/kuangcp/hibernate/xml/applicationContext.xml similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/hibernate/xml/applicationContext.xml rename to spring/src/test/java/com/github/kuangcp/hibernate/xml/applicationContext.xml diff --git a/java-spring/src/test/java/com/github/kuangcp/ioc/alias/AliasTest.java b/spring/src/test/java/com/github/kuangcp/ioc/alias/AliasTest.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/ioc/alias/AliasTest.java rename to spring/src/test/java/com/github/kuangcp/ioc/alias/AliasTest.java diff --git a/java-spring/src/test/java/com/github/kuangcp/ioc/alias/HelloWorld.java b/spring/src/test/java/com/github/kuangcp/ioc/alias/HelloWorld.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/ioc/alias/HelloWorld.java rename to spring/src/test/java/com/github/kuangcp/ioc/alias/HelloWorld.java diff --git a/java-spring/src/test/java/com/github/kuangcp/ioc/alias/applicationContext.xml b/spring/src/test/java/com/github/kuangcp/ioc/alias/applicationContext.xml similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/ioc/alias/applicationContext.xml rename to spring/src/test/java/com/github/kuangcp/ioc/alias/applicationContext.xml diff --git a/java-spring/src/test/java/com/github/kuangcp/ioc/createobject/CreateObjectTest.java b/spring/src/test/java/com/github/kuangcp/ioc/createobject/CreateObjectTest.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/ioc/createobject/CreateObjectTest.java rename to spring/src/test/java/com/github/kuangcp/ioc/createobject/CreateObjectTest.java diff --git a/java-spring/src/test/java/com/github/kuangcp/ioc/createobject/HelloWorld.java b/spring/src/test/java/com/github/kuangcp/ioc/createobject/HelloWorld.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/ioc/createobject/HelloWorld.java rename to spring/src/test/java/com/github/kuangcp/ioc/createobject/HelloWorld.java diff --git a/java-spring/src/test/java/com/github/kuangcp/ioc/createobject/HelloWorldFactory.java b/spring/src/test/java/com/github/kuangcp/ioc/createobject/HelloWorldFactory.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/ioc/createobject/HelloWorldFactory.java rename to spring/src/test/java/com/github/kuangcp/ioc/createobject/HelloWorldFactory.java diff --git a/java-spring/src/test/java/com/github/kuangcp/ioc/createobject/applicationContext.xml b/spring/src/test/java/com/github/kuangcp/ioc/createobject/applicationContext.xml similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/ioc/createobject/applicationContext.xml rename to spring/src/test/java/com/github/kuangcp/ioc/createobject/applicationContext.xml diff --git a/java-spring/src/test/java/com/github/kuangcp/ioc/createobject/when/HelloWorld.java b/spring/src/test/java/com/github/kuangcp/ioc/createobject/when/HelloWorld.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/ioc/createobject/when/HelloWorld.java rename to spring/src/test/java/com/github/kuangcp/ioc/createobject/when/HelloWorld.java diff --git a/java-spring/src/test/java/com/github/kuangcp/ioc/createobject/when/HelloWorldFactory.java b/spring/src/test/java/com/github/kuangcp/ioc/createobject/when/HelloWorldFactory.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/ioc/createobject/when/HelloWorldFactory.java rename to spring/src/test/java/com/github/kuangcp/ioc/createobject/when/HelloWorldFactory.java diff --git a/java-spring/src/test/java/com/github/kuangcp/ioc/createobject/when/Person.java b/spring/src/test/java/com/github/kuangcp/ioc/createobject/when/Person.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/ioc/createobject/when/Person.java rename to spring/src/test/java/com/github/kuangcp/ioc/createobject/when/Person.java diff --git a/java-spring/src/test/java/com/github/kuangcp/ioc/createobject/when/WhenTest.java b/spring/src/test/java/com/github/kuangcp/ioc/createobject/when/WhenTest.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/ioc/createobject/when/WhenTest.java rename to spring/src/test/java/com/github/kuangcp/ioc/createobject/when/WhenTest.java diff --git a/java-spring/src/test/java/com/github/kuangcp/ioc/createobject/when/applicationContext.xml b/spring/src/test/java/com/github/kuangcp/ioc/createobject/when/applicationContext.xml similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/ioc/createobject/when/applicationContext.xml rename to spring/src/test/java/com/github/kuangcp/ioc/createobject/when/applicationContext.xml diff --git a/java-spring/src/test/java/com/github/kuangcp/ioc/helloworld/HelloWorld.java b/spring/src/test/java/com/github/kuangcp/ioc/helloworld/HelloWorld.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/ioc/helloworld/HelloWorld.java rename to spring/src/test/java/com/github/kuangcp/ioc/helloworld/HelloWorld.java diff --git a/java-spring/src/test/java/com/github/kuangcp/ioc/helloworld/HelloWorldTest.java b/spring/src/test/java/com/github/kuangcp/ioc/helloworld/HelloWorldTest.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/ioc/helloworld/HelloWorldTest.java rename to spring/src/test/java/com/github/kuangcp/ioc/helloworld/HelloWorldTest.java diff --git a/java-spring/src/test/java/com/github/kuangcp/ioc/helloworld/applicationContext.xml b/spring/src/test/java/com/github/kuangcp/ioc/helloworld/applicationContext.xml similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/ioc/helloworld/applicationContext.xml rename to spring/src/test/java/com/github/kuangcp/ioc/helloworld/applicationContext.xml diff --git a/java-spring/src/test/java/com/github/kuangcp/ioc/initdestroy/HelloWorld.java b/spring/src/test/java/com/github/kuangcp/ioc/initdestroy/HelloWorld.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/ioc/initdestroy/HelloWorld.java rename to spring/src/test/java/com/github/kuangcp/ioc/initdestroy/HelloWorld.java diff --git a/java-spring/src/test/java/com/github/kuangcp/ioc/initdestroy/InitDestroyTest.java b/spring/src/test/java/com/github/kuangcp/ioc/initdestroy/InitDestroyTest.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/ioc/initdestroy/InitDestroyTest.java rename to spring/src/test/java/com/github/kuangcp/ioc/initdestroy/InitDestroyTest.java diff --git a/java-spring/src/test/java/com/github/kuangcp/ioc/initdestroy/applicationContext.xml b/spring/src/test/java/com/github/kuangcp/ioc/initdestroy/applicationContext.xml similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/ioc/initdestroy/applicationContext.xml rename to spring/src/test/java/com/github/kuangcp/ioc/initdestroy/applicationContext.xml diff --git a/java-spring/src/test/java/com/github/kuangcp/ioc/scope/HelloWorld.java b/spring/src/test/java/com/github/kuangcp/ioc/scope/HelloWorld.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/ioc/scope/HelloWorld.java rename to spring/src/test/java/com/github/kuangcp/ioc/scope/HelloWorld.java diff --git a/java-spring/src/test/java/com/github/kuangcp/ioc/scope/ScopeTest.java b/spring/src/test/java/com/github/kuangcp/ioc/scope/ScopeTest.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/ioc/scope/ScopeTest.java rename to spring/src/test/java/com/github/kuangcp/ioc/scope/ScopeTest.java diff --git a/java-spring/src/test/java/com/github/kuangcp/ioc/scope/applicationContext.xml b/spring/src/test/java/com/github/kuangcp/ioc/scope/applicationContext.xml similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/ioc/scope/applicationContext.xml rename to spring/src/test/java/com/github/kuangcp/ioc/scope/applicationContext.xml diff --git a/java-spring/src/test/java/com/github/kuangcp/jdbc/jdbc.properties b/spring/src/test/java/com/github/kuangcp/jdbc/jdbc.properties similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/jdbc/jdbc.properties rename to spring/src/test/java/com/github/kuangcp/jdbc/jdbc.properties diff --git a/java-spring/src/test/java/com/github/kuangcp/jdbc/jdbc/DataSourceTest.java b/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/DataSourceTest.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/jdbc/jdbc/DataSourceTest.java rename to spring/src/test/java/com/github/kuangcp/jdbc/jdbc/DataSourceTest.java diff --git a/java-spring/src/test/java/com/github/kuangcp/jdbc/jdbc/Person.java b/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/Person.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/jdbc/jdbc/Person.java rename to spring/src/test/java/com/github/kuangcp/jdbc/jdbc/Person.java diff --git a/java-spring/src/test/java/com/github/kuangcp/jdbc/jdbc/PersonDao.java b/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/PersonDao.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/jdbc/jdbc/PersonDao.java rename to spring/src/test/java/com/github/kuangcp/jdbc/jdbc/PersonDao.java diff --git a/java-spring/src/test/java/com/github/kuangcp/jdbc/jdbc/PersonDaoImpl.java b/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/PersonDaoImpl.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/jdbc/jdbc/PersonDaoImpl.java rename to spring/src/test/java/com/github/kuangcp/jdbc/jdbc/PersonDaoImpl.java diff --git a/java-spring/src/test/java/com/github/kuangcp/jdbc/jdbc/PersonDaoImpl2.java b/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/PersonDaoImpl2.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/jdbc/jdbc/PersonDaoImpl2.java rename to spring/src/test/java/com/github/kuangcp/jdbc/jdbc/PersonDaoImpl2.java diff --git a/java-spring/src/test/java/com/github/kuangcp/jdbc/jdbc/PersonDaoImpl3.java b/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/PersonDaoImpl3.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/jdbc/jdbc/PersonDaoImpl3.java rename to spring/src/test/java/com/github/kuangcp/jdbc/jdbc/PersonDaoImpl3.java diff --git a/java-spring/src/test/java/com/github/kuangcp/jdbc/jdbc/PersonRowMapper.java b/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/PersonRowMapper.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/jdbc/jdbc/PersonRowMapper.java rename to spring/src/test/java/com/github/kuangcp/jdbc/jdbc/PersonRowMapper.java diff --git a/java-spring/src/test/java/com/github/kuangcp/jdbc/jdbc/PersonTest.java b/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/PersonTest.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/jdbc/jdbc/PersonTest.java rename to spring/src/test/java/com/github/kuangcp/jdbc/jdbc/PersonTest.java diff --git a/java-spring/src/test/java/com/github/kuangcp/jdbc/jdbc/applicationContext.xml b/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/applicationContext.xml similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/jdbc/jdbc/applicationContext.xml rename to spring/src/test/java/com/github/kuangcp/jdbc/jdbc/applicationContext.xml diff --git a/java-spring/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/PersonDao.java b/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/PersonDao.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/PersonDao.java rename to spring/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/PersonDao.java diff --git a/java-spring/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/PersonDaoImpl.java b/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/PersonDaoImpl.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/PersonDaoImpl.java rename to spring/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/PersonDaoImpl.java diff --git a/java-spring/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/PersonService.java b/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/PersonService.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/PersonService.java rename to spring/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/PersonService.java diff --git a/java-spring/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/PersonServiceImpl.java b/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/PersonServiceImpl.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/PersonServiceImpl.java rename to spring/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/PersonServiceImpl.java diff --git a/java-spring/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/PersonTest.java b/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/PersonTest.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/PersonTest.java rename to spring/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/PersonTest.java diff --git a/java-spring/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/annotation/PersonDao.java b/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/annotation/PersonDao.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/annotation/PersonDao.java rename to spring/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/annotation/PersonDao.java diff --git a/java-spring/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/annotation/PersonDaoImpl.java b/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/annotation/PersonDaoImpl.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/annotation/PersonDaoImpl.java rename to spring/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/annotation/PersonDaoImpl.java diff --git a/java-spring/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/annotation/PersonService.java b/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/annotation/PersonService.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/annotation/PersonService.java rename to spring/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/annotation/PersonService.java diff --git a/java-spring/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/annotation/PersonServiceImpl.java b/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/annotation/PersonServiceImpl.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/annotation/PersonServiceImpl.java rename to spring/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/annotation/PersonServiceImpl.java diff --git a/java-spring/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/annotation/PersonTest.java b/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/annotation/PersonTest.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/annotation/PersonTest.java rename to spring/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/annotation/PersonTest.java diff --git a/java-spring/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/annotation/applicationContext.xml b/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/annotation/applicationContext.xml similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/annotation/applicationContext.xml rename to spring/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/annotation/applicationContext.xml diff --git a/java-spring/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/applicationContext.xml b/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/applicationContext.xml similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/applicationContext.xml rename to spring/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/applicationContext.xml diff --git a/java-spring/src/test/java/com/github/kuangcp/jdbc/transaction/PersonDao.java b/spring/src/test/java/com/github/kuangcp/jdbc/transaction/PersonDao.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/jdbc/transaction/PersonDao.java rename to spring/src/test/java/com/github/kuangcp/jdbc/transaction/PersonDao.java diff --git a/java-spring/src/test/java/com/github/kuangcp/jdbc/transaction/PersonDaoImpl.java b/spring/src/test/java/com/github/kuangcp/jdbc/transaction/PersonDaoImpl.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/jdbc/transaction/PersonDaoImpl.java rename to spring/src/test/java/com/github/kuangcp/jdbc/transaction/PersonDaoImpl.java diff --git a/java-spring/src/test/java/com/github/kuangcp/jdbc/transaction/PersonService.java b/spring/src/test/java/com/github/kuangcp/jdbc/transaction/PersonService.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/jdbc/transaction/PersonService.java rename to spring/src/test/java/com/github/kuangcp/jdbc/transaction/PersonService.java diff --git a/java-spring/src/test/java/com/github/kuangcp/jdbc/transaction/PersonServiceImpl.java b/spring/src/test/java/com/github/kuangcp/jdbc/transaction/PersonServiceImpl.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/jdbc/transaction/PersonServiceImpl.java rename to spring/src/test/java/com/github/kuangcp/jdbc/transaction/PersonServiceImpl.java diff --git a/java-spring/src/test/java/com/github/kuangcp/jdbc/transaction/PersonTest.java b/spring/src/test/java/com/github/kuangcp/jdbc/transaction/PersonTest.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/jdbc/transaction/PersonTest.java rename to spring/src/test/java/com/github/kuangcp/jdbc/transaction/PersonTest.java diff --git a/java-spring/src/test/java/com/github/kuangcp/jdbc/transaction/applicationContext.xml b/spring/src/test/java/com/github/kuangcp/jdbc/transaction/applicationContext.xml similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/jdbc/transaction/applicationContext.xml rename to spring/src/test/java/com/github/kuangcp/jdbc/transaction/applicationContext.xml diff --git a/java-spring/src/test/java/com/github/kuangcp/mvc/PersonAction.java b/spring/src/test/java/com/github/kuangcp/mvc/PersonAction.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/mvc/PersonAction.java rename to spring/src/test/java/com/github/kuangcp/mvc/PersonAction.java diff --git a/java-spring/src/test/java/com/github/kuangcp/mvc/PersonDao.java b/spring/src/test/java/com/github/kuangcp/mvc/PersonDao.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/mvc/PersonDao.java rename to spring/src/test/java/com/github/kuangcp/mvc/PersonDao.java diff --git a/java-spring/src/test/java/com/github/kuangcp/mvc/PersonDaoImpl.java b/spring/src/test/java/com/github/kuangcp/mvc/PersonDaoImpl.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/mvc/PersonDaoImpl.java rename to spring/src/test/java/com/github/kuangcp/mvc/PersonDaoImpl.java diff --git a/java-spring/src/test/java/com/github/kuangcp/mvc/PersonService.java b/spring/src/test/java/com/github/kuangcp/mvc/PersonService.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/mvc/PersonService.java rename to spring/src/test/java/com/github/kuangcp/mvc/PersonService.java diff --git a/java-spring/src/test/java/com/github/kuangcp/mvc/PersonServiceImpl.java b/spring/src/test/java/com/github/kuangcp/mvc/PersonServiceImpl.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/mvc/PersonServiceImpl.java rename to spring/src/test/java/com/github/kuangcp/mvc/PersonServiceImpl.java diff --git a/java-spring/src/test/java/com/github/kuangcp/mvc/PersonTest.java b/spring/src/test/java/com/github/kuangcp/mvc/PersonTest.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/mvc/PersonTest.java rename to spring/src/test/java/com/github/kuangcp/mvc/PersonTest.java diff --git a/java-spring/src/test/java/com/github/kuangcp/mvc/annotation/PersonAction.java b/spring/src/test/java/com/github/kuangcp/mvc/annotation/PersonAction.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/mvc/annotation/PersonAction.java rename to spring/src/test/java/com/github/kuangcp/mvc/annotation/PersonAction.java diff --git a/java-spring/src/test/java/com/github/kuangcp/mvc/annotation/PersonDao.java b/spring/src/test/java/com/github/kuangcp/mvc/annotation/PersonDao.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/mvc/annotation/PersonDao.java rename to spring/src/test/java/com/github/kuangcp/mvc/annotation/PersonDao.java diff --git a/java-spring/src/test/java/com/github/kuangcp/mvc/annotation/PersonDaoImpl.java b/spring/src/test/java/com/github/kuangcp/mvc/annotation/PersonDaoImpl.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/mvc/annotation/PersonDaoImpl.java rename to spring/src/test/java/com/github/kuangcp/mvc/annotation/PersonDaoImpl.java diff --git a/java-spring/src/test/java/com/github/kuangcp/mvc/annotation/PersonService.java b/spring/src/test/java/com/github/kuangcp/mvc/annotation/PersonService.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/mvc/annotation/PersonService.java rename to spring/src/test/java/com/github/kuangcp/mvc/annotation/PersonService.java diff --git a/java-spring/src/test/java/com/github/kuangcp/mvc/annotation/PersonServiceImpl.java b/spring/src/test/java/com/github/kuangcp/mvc/annotation/PersonServiceImpl.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/mvc/annotation/PersonServiceImpl.java rename to spring/src/test/java/com/github/kuangcp/mvc/annotation/PersonServiceImpl.java diff --git a/java-spring/src/test/java/com/github/kuangcp/mvc/annotation/PersonTest.java b/spring/src/test/java/com/github/kuangcp/mvc/annotation/PersonTest.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/mvc/annotation/PersonTest.java rename to spring/src/test/java/com/github/kuangcp/mvc/annotation/PersonTest.java diff --git a/java-spring/src/test/java/com/github/kuangcp/mvc/annotation/applicationContext.xml b/spring/src/test/java/com/github/kuangcp/mvc/annotation/applicationContext.xml similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/mvc/annotation/applicationContext.xml rename to spring/src/test/java/com/github/kuangcp/mvc/annotation/applicationContext.xml diff --git a/java-spring/src/test/java/com/github/kuangcp/mvc/applicationContext.xml b/spring/src/test/java/com/github/kuangcp/mvc/applicationContext.xml similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/mvc/applicationContext.xml rename to spring/src/test/java/com/github/kuangcp/mvc/applicationContext.xml diff --git a/java-spring/src/test/java/com/github/kuangcp/proxy/dao/Readme.md b/spring/src/test/java/com/github/kuangcp/proxy/dao/Readme.md similarity index 69% rename from java-spring/src/test/java/com/github/kuangcp/proxy/dao/Readme.md rename to spring/src/test/java/com/github/kuangcp/proxy/dao/Readme.md index 65c8b59e..937f6e0d 100644 --- a/java-spring/src/test/java/com/github/kuangcp/proxy/dao/Readme.md +++ b/spring/src/test/java/com/github/kuangcp/proxy/dao/Readme.md @@ -1,3 +1,3 @@ -# AOP 手动实现方式 +# AOP 手动实现 Dao 功能 针对 Person 类, 增删改操作, AOP 的方式添加事务控制 diff --git a/java-spring/src/test/java/com/github/kuangcp/proxy/dao/cglibproxy/CGlibProxyTest.java b/spring/src/test/java/com/github/kuangcp/proxy/dao/cglibproxy/CGlibProxyTest.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/proxy/dao/cglibproxy/CGlibProxyTest.java rename to spring/src/test/java/com/github/kuangcp/proxy/dao/cglibproxy/CGlibProxyTest.java diff --git a/java-spring/src/test/java/com/github/kuangcp/proxy/dao/jdkproxy/JDKProxyTest.java b/spring/src/test/java/com/github/kuangcp/proxy/dao/jdkproxy/JDKProxyTest.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/proxy/dao/jdkproxy/JDKProxyTest.java rename to spring/src/test/java/com/github/kuangcp/proxy/dao/jdkproxy/JDKProxyTest.java diff --git a/java-spring/src/test/java/com/github/kuangcp/proxy/dao/jdkproxy/Readme.md b/spring/src/test/java/com/github/kuangcp/proxy/dao/jdkproxy/Readme.md similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/proxy/dao/jdkproxy/Readme.md rename to spring/src/test/java/com/github/kuangcp/proxy/dao/jdkproxy/Readme.md diff --git a/java-spring/src/test/java/com/github/kuangcp/proxy/salary/Logger.java b/spring/src/test/java/com/github/kuangcp/proxy/salary/Logger.java similarity index 50% rename from java-spring/src/test/java/com/github/kuangcp/proxy/salary/Logger.java rename to spring/src/test/java/com/github/kuangcp/proxy/salary/Logger.java index 6548d9fe..529d2966 100644 --- a/java-spring/src/test/java/com/github/kuangcp/proxy/salary/Logger.java +++ b/spring/src/test/java/com/github/kuangcp/proxy/salary/Logger.java @@ -1,7 +1,8 @@ package com.github.kuangcp.proxy.salary; public class Logger { - public void logging(){ - System.out.println("logging"); - } + + public void logging() { + System.out.println("logging"); + } } diff --git a/spring/src/test/java/com/github/kuangcp/proxy/salary/Privilege.java b/spring/src/test/java/com/github/kuangcp/proxy/salary/Privilege.java new file mode 100644 index 00000000..01e6291b --- /dev/null +++ b/spring/src/test/java/com/github/kuangcp/proxy/salary/Privilege.java @@ -0,0 +1,14 @@ +package com.github.kuangcp.proxy.salary; + +public class Privilege { + + private String access; + + public String getAccess() { + return access; + } + + public void setAccess(String access) { + this.access = access; + } +} diff --git a/spring/src/test/java/com/github/kuangcp/proxy/salary/SalaryManager.java b/spring/src/test/java/com/github/kuangcp/proxy/salary/SalaryManager.java new file mode 100644 index 00000000..1b1dc4a3 --- /dev/null +++ b/spring/src/test/java/com/github/kuangcp/proxy/salary/SalaryManager.java @@ -0,0 +1,24 @@ +package com.github.kuangcp.proxy.salary; + +public class SalaryManager { + + private Logger logger; + private Security security; + private Privilege privilege; + + public SalaryManager(Logger logger, Security security, Privilege privilege) { + this.logger = logger; + this.security = security; + this.privilege = privilege; + } + + public void showSalary() { + this.logger.logging(); + this.security.security(); + if ("admin".equals(this.privilege.getAccess())) { + System.out.println("正在查看工资:涨了2W"); + } else { + System.out.println("您没有该权限"); + } + } +} diff --git a/spring/src/test/java/com/github/kuangcp/proxy/salary/SalaryTest.java b/spring/src/test/java/com/github/kuangcp/proxy/salary/SalaryTest.java new file mode 100644 index 00000000..de99dab0 --- /dev/null +++ b/spring/src/test/java/com/github/kuangcp/proxy/salary/SalaryTest.java @@ -0,0 +1,16 @@ +package com.github.kuangcp.proxy.salary; + +import org.junit.Test; + +public class SalaryTest { + + @Test + public void test() { + Logger logger = new Logger(); + Privilege privilege = new Privilege(); + Security security = new Security(); + privilege.setAccess("admin"); + SalaryManager salaryManager = new SalaryManager(logger, security, privilege); + salaryManager.showSalary(); + } +} diff --git a/java-spring/src/test/java/com/github/kuangcp/proxy/salary/Security.java b/spring/src/test/java/com/github/kuangcp/proxy/salary/Security.java similarity index 50% rename from java-spring/src/test/java/com/github/kuangcp/proxy/salary/Security.java rename to spring/src/test/java/com/github/kuangcp/proxy/salary/Security.java index 2a10cb5c..cda622c4 100644 --- a/java-spring/src/test/java/com/github/kuangcp/proxy/salary/Security.java +++ b/spring/src/test/java/com/github/kuangcp/proxy/salary/Security.java @@ -1,7 +1,8 @@ package com.github.kuangcp.proxy.salary; public class Security { - public void security(){ - System.out.println("security"); - } + + public void security() { + System.out.println("security"); + } } diff --git a/java-spring/src/test/java/com/github/kuangcp/proxy/salary/aop/xml/Person.java b/spring/src/test/java/com/github/kuangcp/proxy/salary/aop/xml/Person.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/proxy/salary/aop/xml/Person.java rename to spring/src/test/java/com/github/kuangcp/proxy/salary/aop/xml/Person.java diff --git a/java-spring/src/test/java/com/github/kuangcp/proxy/salary/aop/xml/PersonDao.java b/spring/src/test/java/com/github/kuangcp/proxy/salary/aop/xml/PersonDao.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/proxy/salary/aop/xml/PersonDao.java rename to spring/src/test/java/com/github/kuangcp/proxy/salary/aop/xml/PersonDao.java diff --git a/java-spring/src/test/java/com/github/kuangcp/proxy/salary/aop/xml/PersonDaoImpl.java b/spring/src/test/java/com/github/kuangcp/proxy/salary/aop/xml/PersonDaoImpl.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/proxy/salary/aop/xml/PersonDaoImpl.java rename to spring/src/test/java/com/github/kuangcp/proxy/salary/aop/xml/PersonDaoImpl.java diff --git a/java-spring/src/test/java/com/github/kuangcp/proxy/salary/aop/xml/PersonTest.java b/spring/src/test/java/com/github/kuangcp/proxy/salary/aop/xml/PersonTest.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/proxy/salary/aop/xml/PersonTest.java rename to spring/src/test/java/com/github/kuangcp/proxy/salary/aop/xml/PersonTest.java diff --git a/java-spring/src/test/java/com/github/kuangcp/proxy/salary/aop/xml/Transaction.java b/spring/src/test/java/com/github/kuangcp/proxy/salary/aop/xml/Transaction.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/proxy/salary/aop/xml/Transaction.java rename to spring/src/test/java/com/github/kuangcp/proxy/salary/aop/xml/Transaction.java diff --git a/java-spring/src/test/java/com/github/kuangcp/proxy/salary/aop/xml/applicationContext.xml b/spring/src/test/java/com/github/kuangcp/proxy/salary/aop/xml/applicationContext.xml similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/proxy/salary/aop/xml/applicationContext.xml rename to spring/src/test/java/com/github/kuangcp/proxy/salary/aop/xml/applicationContext.xml diff --git "a/java-spring/src/test/java/com/github/kuangcp/proxy/salary/aop/xml/\344\275\277\347\224\250XML\345\256\236\347\216\260AOP.txt" "b/spring/src/test/java/com/github/kuangcp/proxy/salary/aop/xml/\344\275\277\347\224\250XML\345\256\236\347\216\260AOP.txt" similarity index 100% rename from "java-spring/src/test/java/com/github/kuangcp/proxy/salary/aop/xml/\344\275\277\347\224\250XML\345\256\236\347\216\260AOP.txt" rename to "spring/src/test/java/com/github/kuangcp/proxy/salary/aop/xml/\344\275\277\347\224\250XML\345\256\236\347\216\260AOP.txt" diff --git a/java-spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/Logger.java b/spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/Logger.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/Logger.java rename to spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/Logger.java diff --git a/java-spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/Privilege.java b/spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/Privilege.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/Privilege.java rename to spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/Privilege.java diff --git a/java-spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/ProxyTest.java b/spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/ProxyTest.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/ProxyTest.java rename to spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/ProxyTest.java diff --git a/java-spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/SalaryInterceptor.java b/spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/SalaryInterceptor.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/SalaryInterceptor.java rename to spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/SalaryInterceptor.java diff --git a/java-spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/SalaryManager.java b/spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/SalaryManager.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/SalaryManager.java rename to spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/SalaryManager.java diff --git a/java-spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/SalaryManagerImpl.java b/spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/SalaryManagerImpl.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/SalaryManagerImpl.java rename to spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/SalaryManagerImpl.java diff --git a/java-spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/Security.java b/spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/Security.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/Security.java rename to spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/Security.java diff --git a/java-spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/aspects/Interceptor.java b/spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/aspects/Interceptor.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/aspects/Interceptor.java rename to spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/aspects/Interceptor.java diff --git a/java-spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/aspects/Logger.java b/spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/aspects/Logger.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/aspects/Logger.java rename to spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/aspects/Logger.java diff --git a/java-spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/aspects/Privilege.java b/spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/aspects/Privilege.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/aspects/Privilege.java rename to spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/aspects/Privilege.java diff --git a/java-spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/aspects/ProxyTest.java b/spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/aspects/ProxyTest.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/aspects/ProxyTest.java rename to spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/aspects/ProxyTest.java diff --git a/java-spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/aspects/SalaryInterceptor.java b/spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/aspects/SalaryInterceptor.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/aspects/SalaryInterceptor.java rename to spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/aspects/SalaryInterceptor.java diff --git a/java-spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/aspects/SalaryManager.java b/spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/aspects/SalaryManager.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/aspects/SalaryManager.java rename to spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/aspects/SalaryManager.java diff --git a/java-spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/aspects/SalaryManagerImpl.java b/spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/aspects/SalaryManagerImpl.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/aspects/SalaryManagerImpl.java rename to spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/aspects/SalaryManagerImpl.java diff --git a/java-spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/aspects/Security.java b/spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/aspects/Security.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/aspects/Security.java rename to spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/aspects/Security.java diff --git a/java-spring/src/test/java/com/github/kuangcp/proxy/salary/proxy/Logger.java b/spring/src/test/java/com/github/kuangcp/proxy/salary/proxy/Logger.java similarity index 52% rename from java-spring/src/test/java/com/github/kuangcp/proxy/salary/proxy/Logger.java rename to spring/src/test/java/com/github/kuangcp/proxy/salary/proxy/Logger.java index 0ad8da3e..de4b9205 100644 --- a/java-spring/src/test/java/com/github/kuangcp/proxy/salary/proxy/Logger.java +++ b/spring/src/test/java/com/github/kuangcp/proxy/salary/proxy/Logger.java @@ -1,7 +1,8 @@ package com.github.kuangcp.proxy.salary.proxy; public class Logger { - public void logging(){ - System.out.println("logging"); - } + + public void logging() { + System.out.println("logging"); + } } diff --git a/spring/src/test/java/com/github/kuangcp/proxy/salary/proxy/Privilege.java b/spring/src/test/java/com/github/kuangcp/proxy/salary/proxy/Privilege.java new file mode 100644 index 00000000..af42408b --- /dev/null +++ b/spring/src/test/java/com/github/kuangcp/proxy/salary/proxy/Privilege.java @@ -0,0 +1,14 @@ +package com.github.kuangcp.proxy.salary.proxy; + +public class Privilege { + + private String access; + + public String getAccess() { + return access; + } + + public void setAccess(String access) { + this.access = access; + } +} diff --git a/java-spring/src/test/java/com/github/kuangcp/proxy/salary/proxy/ProxyTest.java b/spring/src/test/java/com/github/kuangcp/proxy/salary/proxy/ProxyTest.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/proxy/salary/proxy/ProxyTest.java rename to spring/src/test/java/com/github/kuangcp/proxy/salary/proxy/ProxyTest.java diff --git a/java-spring/src/test/java/com/github/kuangcp/proxy/salary/proxy/Readme.md b/spring/src/test/java/com/github/kuangcp/proxy/salary/proxy/Readme.md similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/proxy/salary/proxy/Readme.md rename to spring/src/test/java/com/github/kuangcp/proxy/salary/proxy/Readme.md diff --git a/java-spring/src/test/java/com/github/kuangcp/proxy/salary/proxy/SalaryManager.java b/spring/src/test/java/com/github/kuangcp/proxy/salary/proxy/SalaryManager.java similarity index 75% rename from java-spring/src/test/java/com/github/kuangcp/proxy/salary/proxy/SalaryManager.java rename to spring/src/test/java/com/github/kuangcp/proxy/salary/proxy/SalaryManager.java index c8234c1b..25aba4ce 100644 --- a/java-spring/src/test/java/com/github/kuangcp/proxy/salary/proxy/SalaryManager.java +++ b/spring/src/test/java/com/github/kuangcp/proxy/salary/proxy/SalaryManager.java @@ -1,5 +1,6 @@ package com.github.kuangcp.proxy.salary.proxy; public interface SalaryManager { - public void showSalary(); + + void showSalary(); } diff --git a/spring/src/test/java/com/github/kuangcp/proxy/salary/proxy/SalaryManagerImpl.java b/spring/src/test/java/com/github/kuangcp/proxy/salary/proxy/SalaryManagerImpl.java new file mode 100644 index 00000000..78124a38 --- /dev/null +++ b/spring/src/test/java/com/github/kuangcp/proxy/salary/proxy/SalaryManagerImpl.java @@ -0,0 +1,9 @@ +package com.github.kuangcp.proxy.salary.proxy; + +public class SalaryManagerImpl implements SalaryManager { + + @Override + public void showSalary() { + System.out.println("正在查看工资:哦哦,涨了2W闽比"); + } +} diff --git a/java-spring/src/test/java/com/github/kuangcp/proxy/salary/proxy/SalaryManagerProxy.java b/spring/src/test/java/com/github/kuangcp/proxy/salary/proxy/SalaryManagerProxy.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/proxy/salary/proxy/SalaryManagerProxy.java rename to spring/src/test/java/com/github/kuangcp/proxy/salary/proxy/SalaryManagerProxy.java diff --git a/java-spring/src/test/java/com/github/kuangcp/proxy/salary/proxy/Security.java b/spring/src/test/java/com/github/kuangcp/proxy/salary/proxy/Security.java similarity index 52% rename from java-spring/src/test/java/com/github/kuangcp/proxy/salary/proxy/Security.java rename to spring/src/test/java/com/github/kuangcp/proxy/salary/proxy/Security.java index ede951e9..b0bddf1c 100644 --- a/java-spring/src/test/java/com/github/kuangcp/proxy/salary/proxy/Security.java +++ b/spring/src/test/java/com/github/kuangcp/proxy/salary/proxy/Security.java @@ -1,7 +1,8 @@ package com.github.kuangcp.proxy.salary.proxy; public class Security { - public void security(){ - System.out.println("security"); - } + + public void security() { + System.out.println("security"); + } } diff --git a/java-spring/src/test/java/com/github/kuangcp/util/SpringHelper.java b/spring/src/test/java/com/github/kuangcp/util/SpringHelper.java similarity index 100% rename from java-spring/src/test/java/com/github/kuangcp/util/SpringHelper.java rename to spring/src/test/java/com/github/kuangcp/util/SpringHelper.java diff --git a/java-test/README.md b/test/README.md similarity index 100% rename from java-test/README.md rename to test/README.md diff --git a/java-test/build.gradle b/test/build.gradle similarity index 100% rename from java-test/build.gradle rename to test/build.gradle diff --git a/java-test/src/main/java/com/github/kuangcp/domain/Person.java b/test/src/main/java/com/github/kuangcp/domain/Person.java similarity index 100% rename from java-test/src/main/java/com/github/kuangcp/domain/Person.java rename to test/src/main/java/com/github/kuangcp/domain/Person.java diff --git a/java-test/src/main/resources/person.yml b/test/src/main/resources/person.yml similarity index 100% rename from java-test/src/main/resources/person.yml rename to test/src/main/resources/person.yml diff --git a/java-test/src/test/java/junit4/Readme.md b/test/src/test/java/junit4/Readme.md similarity index 100% rename from java-test/src/test/java/junit4/Readme.md rename to test/src/test/java/junit4/Readme.md diff --git a/java-test/src/test/java/junit5/Readme.md b/test/src/test/java/junit5/Readme.md similarity index 100% rename from java-test/src/test/java/junit5/Readme.md rename to test/src/test/java/junit5/Readme.md diff --git a/java-test/src/test/java/mockito/Readme.md b/test/src/test/java/mockito/Readme.md similarity index 100% rename from java-test/src/test/java/mockito/Readme.md rename to test/src/test/java/mockito/Readme.md diff --git a/java-test/src/test/java/testng/Readme.md b/test/src/test/java/testng/Readme.md similarity index 100% rename from java-test/src/test/java/testng/Readme.md rename to test/src/test/java/testng/Readme.md diff --git a/java-test/src/test/resources/person.yml b/test/src/test/resources/person.yml similarity index 100% rename from java-test/src/test/resources/person.yml rename to test/src/test/resources/person.yml From e44c00fc41eeb02234ededd2c3f5ae4f2867c829 Mon Sep 17 00:00:00 2001 From: kcp Date: Thu, 30 May 2019 14:02:42 +0800 Subject: [PATCH 049/476] -) remove flink --- flink/build.gradle | 3 --- settings.gradle | 1 - 2 files changed, 4 deletions(-) delete mode 100644 flink/build.gradle diff --git a/flink/build.gradle b/flink/build.gradle deleted file mode 100644 index 34352b2c..00000000 --- a/flink/build.gradle +++ /dev/null @@ -1,3 +0,0 @@ -dependencies { - -} \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 1935f692..bcc5c668 100644 --- a/settings.gradle +++ b/settings.gradle @@ -17,5 +17,4 @@ include( , 'spring' , 'kafka' , 'hadoop' - , 'flink' ) From 0d0ef483a5de548f1ebdbab9593f3a10b0bef8b8 Mon Sep 17 00:00:00 2001 From: kcp Date: Thu, 6 Jun 2019 18:14:25 +0800 Subject: [PATCH 050/476] +) guava --- collection/build.gradle | 4 ++-- guava/build.gradle | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/collection/build.gradle b/collection/build.gradle index b9084cdf..4a3eac68 100644 --- a/collection/build.gradle +++ b/collection/build.gradle @@ -2,6 +2,6 @@ plugins { id 'java' } dependencies { - implementation rootProject.libs['kcp-tool'] -// implementation rootProject.libs['testng'] + implementation libs['kcp-tool'] +// implementation libs['testng'] } diff --git a/guava/build.gradle b/guava/build.gradle index 34352b2c..087dbcb9 100644 --- a/guava/build.gradle +++ b/guava/build.gradle @@ -1,3 +1,3 @@ dependencies { - + implementation libs['guava'] } \ No newline at end of file From dd44551ab06e96cfdd42e0d5302afbc8be25350c Mon Sep 17 00:00:00 2001 From: kcp Date: Mon, 10 Jun 2019 20:09:07 +0800 Subject: [PATCH 051/476] *) extract url --- build.gradle | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/build.gradle b/build.gradle index 63b121d5..323ce7c2 100644 --- a/build.gradle +++ b/build.gradle @@ -7,7 +7,6 @@ allprojects { group = 'com.github.kuangcp' - // default build dir is build/ but idea compile dir is out/, i'm dislike this way // dir out/ just have production/ and test/ dir buildDir = 'out/build' @@ -19,20 +18,20 @@ allprojects { mavenLocal() def aliYun = "http://maven.aliyun.com/nexus/content/groups/public/" - def abroad = "http://central.maven.org/maven2/" + def kuangcp = "https://gitee.com/gin9/MavenRepos/raw/master" + maven { url = aliYun - artifactUrls abroad } maven { - url = "https://gitee.com/gin9/MavenRepos/raw/master" + url = kuangcp } jcenter() } - + // All sub-modules add the following dependencies dependencies { - + annotationProcessor libs['lombok'] compileOnly libs['lombok'] testAnnotationProcessor libs['lombok'] From eb993f1bd2375b6ae2439bb3557557488b78a8a6 Mon Sep 17 00:00:00 2001 From: kcp Date: Thu, 13 Jun 2019 10:21:02 +0800 Subject: [PATCH 052/476] +) add asm and cglib problem --- dependency.gradle | 5 ++++ question/build.gradle | 4 +++ .../com/github/kuangcp/situation/Readme.md | 1 + .../asm/cglib/SimpleTransformer.java | 25 +++++++++++++++++++ .../asm/cglib/SimpleTransformerTest.java | 24 ++++++++++++++++++ 5 files changed, 59 insertions(+) create mode 100644 question/src/main/java/com/github/kuangcp/situation/Readme.md create mode 100644 question/src/main/java/com/github/kuangcp/situation/asm/cglib/SimpleTransformer.java create mode 100644 question/src/test/java/com/github/kuangcp/situation/asm/cglib/SimpleTransformerTest.java diff --git a/dependency.gradle b/dependency.gradle index bba99b16..2779e4c1 100644 --- a/dependency.gradle +++ b/dependency.gradle @@ -81,5 +81,10 @@ ext { , "hadoop-common" : "org.apache.hadoop:hadoop-common:$ver.hadoop" , "hadoop-client" : "org.apache.hadoop:hadoop-client:$ver.hadoop" , "hadoop-hdfs" : "org.apache.hadoop:hadoop-hdfs:$ver.hadoop" + + // the dependencies with specific version for problem situation + , "cglib-3.2.4" : "cglib:cglib:3.2.4" + , "asm-3.1" : "asm:asm:3.1" + , "asm-5.1" : "org.ow2.asm:asm:5.1" ] } \ No newline at end of file diff --git a/question/build.gradle b/question/build.gradle index 283b8876..1d1159c3 100644 --- a/question/build.gradle +++ b/question/build.gradle @@ -1,3 +1,7 @@ dependencies { implementation libs['testng'] + + implementation libs["cglib-3.2.4"] + implementation libs["asm-3.1"] + implementation libs["asm-5.1"] } \ No newline at end of file diff --git a/question/src/main/java/com/github/kuangcp/situation/Readme.md b/question/src/main/java/com/github/kuangcp/situation/Readme.md new file mode 100644 index 00000000..29821d73 --- /dev/null +++ b/question/src/main/java/com/github/kuangcp/situation/Readme.md @@ -0,0 +1 @@ +# 特定场景下的故障 diff --git a/question/src/main/java/com/github/kuangcp/situation/asm/cglib/SimpleTransformer.java b/question/src/main/java/com/github/kuangcp/situation/asm/cglib/SimpleTransformer.java new file mode 100644 index 00000000..c48af19b --- /dev/null +++ b/question/src/main/java/com/github/kuangcp/situation/asm/cglib/SimpleTransformer.java @@ -0,0 +1,25 @@ +package com.github.kuangcp.situation.asm.cglib; + +import net.sf.cglib.beans.BeanCopier; + +/** + * @author https://github.com/kuangcp + * @date 2019-06-13 09:12 + */ +class SimpleTransformer { + + class A { + + } + + class B { + + } + + private final BeanCopier COPIER = BeanCopier.create(A.class, B.class, false); + + public static final SimpleTransformer INSTANCE = new SimpleTransformer(); + + private SimpleTransformer() { + } +} diff --git a/question/src/test/java/com/github/kuangcp/situation/asm/cglib/SimpleTransformerTest.java b/question/src/test/java/com/github/kuangcp/situation/asm/cglib/SimpleTransformerTest.java new file mode 100644 index 00000000..117cdad0 --- /dev/null +++ b/question/src/test/java/com/github/kuangcp/situation/asm/cglib/SimpleTransformerTest.java @@ -0,0 +1,24 @@ +package com.github.kuangcp.situation.asm.cglib; + +import lombok.extern.slf4j.Slf4j; +import org.junit.Before; +import org.junit.Test; + +/** + * @author https://github.com/kuangcp + * @date 2019-06-13 09:13 + */ +@Slf4j +public class SimpleTransformerTest { + + @Before + public void setUp() { + + } + + @Test + public void testInit() { + SimpleTransformer instance = SimpleTransformer.INSTANCE; + log.info("instance={}", instance); + } +} \ No newline at end of file From 04155756547d2c94c5fc353a547d9eb88a7e44ab Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Sun, 16 Jun 2019 00:01:08 +0800 Subject: [PATCH 053/476] *) test class static field init --- .../instantiation/ComplexConstructor.java | 36 +++++++++---------- .../InstantiationAndConstructor.java | 8 +---- .../instantiation/ComplexConstructorTest.java | 7 ++-- .../InstantiationAndConstructorTest.java | 30 ++++++++++------ .../asm/cglib/SimpleTransformerTest.java | 21 ++++++----- 5 files changed, 53 insertions(+), 49 deletions(-) diff --git a/class/src/main/java/com/github/kuangcp/instantiation/ComplexConstructor.java b/class/src/main/java/com/github/kuangcp/instantiation/ComplexConstructor.java index 2241f11b..947f287d 100644 --- a/class/src/main/java/com/github/kuangcp/instantiation/ComplexConstructor.java +++ b/class/src/main/java/com/github/kuangcp/instantiation/ComplexConstructor.java @@ -4,36 +4,34 @@ /** * @author https://github.com/kuangcp - * @date 2019-05-15 09:20 */ @Slf4j -public class ComplexConstructor { +class ComplexConstructor { - static abstract class AbstractServer { - - int actualPort; - static final int DEFAULT_PORT = 8848; - - AbstractServer() { - actualPort = getPort(); - - log.info("port={}", actualPort); - } - - abstract int getPort(); - } - - public static class Server extends AbstractServer { + static class Server extends AbstractServer { private int port = 8080; - public Server(int port) { + Server(int port) { this.port = port; } @Override int getPort() { - return Math.random() > 0.5 ? port : DEFAULT_PORT; + return port; } } } + +@Slf4j +abstract class AbstractServer { + + int actualPort; + + AbstractServer() { + actualPort = getPort(); + log.info("actualPort={}", actualPort); + } + + abstract int getPort(); +} \ No newline at end of file diff --git a/class/src/main/java/com/github/kuangcp/instantiation/InstantiationAndConstructor.java b/class/src/main/java/com/github/kuangcp/instantiation/InstantiationAndConstructor.java index 06ebade4..053f1646 100644 --- a/class/src/main/java/com/github/kuangcp/instantiation/InstantiationAndConstructor.java +++ b/class/src/main/java/com/github/kuangcp/instantiation/InstantiationAndConstructor.java @@ -9,7 +9,7 @@ */ @Data @Slf4j -public class InstantiationAndConstructor implements Serializable, Cloneable { +class InstantiationAndConstructor implements Serializable, Cloneable { private String name; @@ -27,10 +27,4 @@ protected Object clone() throws CloneNotSupportedException { return super.clone(); } - @Override - public String toString() { - return "TargetObject{" + - "name='" + name + '\'' + - '}'; - } } diff --git a/class/src/test/java/com/github/kuangcp/instantiation/ComplexConstructorTest.java b/class/src/test/java/com/github/kuangcp/instantiation/ComplexConstructorTest.java index 5a8eaccd..a3701bb7 100644 --- a/class/src/test/java/com/github/kuangcp/instantiation/ComplexConstructorTest.java +++ b/class/src/test/java/com/github/kuangcp/instantiation/ComplexConstructorTest.java @@ -15,11 +15,10 @@ public class ComplexConstructorTest { @Test - public void test() { + public void testInitField() { Server server = new Server(3306); - // TODO 解释 - assertThat(server.actualPort == 0 || server.actualPort == Server.DEFAULT_PORT, - equalTo(true)); + // TODO 永远为 0 + assertThat(server.actualPort, equalTo(0)); } } diff --git a/class/src/test/java/com/github/kuangcp/instantiation/InstantiationAndConstructorTest.java b/class/src/test/java/com/github/kuangcp/instantiation/InstantiationAndConstructorTest.java index 21d043c3..d99f6ff4 100644 --- a/class/src/test/java/com/github/kuangcp/instantiation/InstantiationAndConstructorTest.java +++ b/class/src/test/java/com/github/kuangcp/instantiation/InstantiationAndConstructorTest.java @@ -9,11 +9,13 @@ import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.Constructor; +import java.lang.reflect.Method; import lombok.extern.slf4j.Slf4j; import org.junit.Test; /** * @author kuangcp on 3/9/19-5:48 PM + * * 实例化和构造器的关系 * * clone 以及 serialize 创建对象时 不会调用构造器 @@ -23,23 +25,28 @@ public class InstantiationAndConstructorTest { @Test public void testInitByNew() { - new InstantiationAndConstructor(); - new InstantiationAndConstructor("name"); + InstantiationAndConstructor instance = new InstantiationAndConstructor("name"); + log.info("instance={}", instance); } + // 反射实例化对象方式 实际上调用的空构造器 @Test - public void testInitByNewInstance() throws IllegalAccessException, InstantiationException { + public void testInitByReflect1() throws IllegalAccessException, InstantiationException { InstantiationAndConstructor.class.newInstance(); } @Test public void testInitByReflect() throws ReflectiveOperationException { - Constructor constructor = InstantiationAndConstructor.class.getConstructor(String.class); + Constructor constructor = + InstantiationAndConstructor.class.getConstructor(String.class); - String name = "use reflect"; + String name = "get constructor by reflect"; InstantiationAndConstructor domain = constructor.newInstance(name); assertThat(domain.getName(), equalTo(name)); + + Method d = InstantiationAndConstructor.class.getMethod("d"); + d.invoke(new Object()); } @Test @@ -54,16 +61,19 @@ public void testInitByClone() throws CloneNotSupportedException { @Test public void testInitByDeserialize() throws IOException, ClassNotFoundException { - InstantiationAndConstructor targetObject = new InstantiationAndConstructor("name"); + InstantiationAndConstructor origin = new InstantiationAndConstructor("name"); + // 输出 ByteArrayOutputStream byteOutput = new ByteArrayOutputStream(); ObjectOutputStream output = new ObjectOutputStream(byteOutput); - output.writeObject(targetObject); + output.writeObject(origin); + // 输入 ByteArrayInputStream byteInput = new ByteArrayInputStream(byteOutput.toByteArray()); - ObjectInputStream input = new ObjectInputStream(byteInput); - InstantiationAndConstructor result = (InstantiationAndConstructor) input.readObject(); - assertThat(result.getName(), equalTo("name")); + + InstantiationAndConstructor deserialize = (InstantiationAndConstructor) input.readObject(); + assertThat(deserialize.getName(), equalTo("name")); + } } diff --git a/question/src/test/java/com/github/kuangcp/situation/asm/cglib/SimpleTransformerTest.java b/question/src/test/java/com/github/kuangcp/situation/asm/cglib/SimpleTransformerTest.java index 117cdad0..a45dbc52 100644 --- a/question/src/test/java/com/github/kuangcp/situation/asm/cglib/SimpleTransformerTest.java +++ b/question/src/test/java/com/github/kuangcp/situation/asm/cglib/SimpleTransformerTest.java @@ -1,24 +1,27 @@ package com.github.kuangcp.situation.asm.cglib; import lombok.extern.slf4j.Slf4j; -import org.junit.Before; import org.junit.Test; /** * @author https://github.com/kuangcp - * @date 2019-06-13 09:13 */ @Slf4j public class SimpleTransformerTest { - @Before - public void setUp() { - - } - @Test public void testInit() { - SimpleTransformer instance = SimpleTransformer.INSTANCE; - log.info("instance={}", instance); + // 虽然该类属性是 static final 但是第一次引用的时候才进行加载和初始化 + new Thread(() -> { + try { + SimpleTransformer instance = SimpleTransformer.INSTANCE; + log.info("instance={}", instance); + } catch (Exception e) { + log.error(e.getMessage(), e); + } + }).start(); + + // 第二次调用就直接报错 NoClassDefFoundError + log.info("instance={}", SimpleTransformer.INSTANCE); } } \ No newline at end of file From d5e287e850d90bc84c6d079821d53584fb489ef6 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Sun, 16 Jun 2019 15:34:36 +0800 Subject: [PATCH 054/476] +) add flink module --- .../serialize/customserialize/Myth.java | 43 ++------ .../customserialize/MythSerialize.java | 5 +- dependency.gradle | 5 + flink/build.gradle | 4 + .../github/kuangcp/hi/PartsSpuStatistic.java | 97 +++++++++++++++++++ .../github/kuangcp/hi/domain/CalculateVO.java | 9 ++ .../kuangcp/hi/util/HDFSOutputFormat.java | 51 ++++++++++ .../kuangcp/hi/util/SourceProvider.java | 27 ++++++ flink/src/main/resources/log4j.properties | 9 ++ .../main/resources/online/config.properties | 46 +++++++++ .../main/resources/test32/config.properties | 48 +++++++++ .../main/resources/test33/config.properties | 28 ++++++ .../main/resources/test36/config.properties | 28 ++++++ .../main/resources/test37/config.properties | 37 +++++++ .../kuangcp/hi/PartsSpuStatisticTest.java | 75 ++++++++++++++ .../com/github/kuangcp/hi/ConsumerDemo.java | 2 +- .../com/github/kuangcp/hi/ProducerDemo.java | 6 +- .../ProductStatisticJobCommand.java | 2 +- .../{dto => domain}/ProductStatisticSpan.java | 2 +- .../hi/{dto => domain}/StartCommand.java | 2 +- .../hi/{dto => domain}/StatisticSpan.java | 2 +- .../com/github/kuangcp/math/Martingale.java | 47 +++++++++ .../github/kuangcp/math/MartingaleTest.java | 20 ++++ settings.gradle | 1 + 24 files changed, 549 insertions(+), 47 deletions(-) create mode 100644 flink/build.gradle create mode 100644 flink/src/main/java/com/github/kuangcp/hi/PartsSpuStatistic.java create mode 100644 flink/src/main/java/com/github/kuangcp/hi/domain/CalculateVO.java create mode 100644 flink/src/main/java/com/github/kuangcp/hi/util/HDFSOutputFormat.java create mode 100644 flink/src/main/java/com/github/kuangcp/hi/util/SourceProvider.java create mode 100644 flink/src/main/resources/log4j.properties create mode 100644 flink/src/main/resources/online/config.properties create mode 100644 flink/src/main/resources/test32/config.properties create mode 100644 flink/src/main/resources/test33/config.properties create mode 100644 flink/src/main/resources/test36/config.properties create mode 100644 flink/src/main/resources/test37/config.properties create mode 100644 flink/src/test/java/com/github/kuangcp/hi/PartsSpuStatisticTest.java rename kafka/src/main/java/com/github/kuangcp/hi/{dto => domain}/ProductStatisticJobCommand.java (93%) rename kafka/src/main/java/com/github/kuangcp/hi/{dto => domain}/ProductStatisticSpan.java (96%) rename kafka/src/main/java/com/github/kuangcp/hi/{dto => domain}/StartCommand.java (88%) rename kafka/src/main/java/com/github/kuangcp/hi/{dto => domain}/StatisticSpan.java (85%) create mode 100644 question/src/main/java/com/github/kuangcp/math/Martingale.java create mode 100644 question/src/test/java/com/github/kuangcp/math/MartingaleTest.java diff --git a/class/src/main/java/com/github/kuangcp/serialize/customserialize/Myth.java b/class/src/main/java/com/github/kuangcp/serialize/customserialize/Myth.java index 1a99d839..d5dc5d91 100644 --- a/class/src/main/java/com/github/kuangcp/serialize/customserialize/Myth.java +++ b/class/src/main/java/com/github/kuangcp/serialize/customserialize/Myth.java @@ -1,46 +1,17 @@ package com.github.kuangcp.serialize.customserialize; +import lombok.Data; + /** * Created by https://github.com/kuangcp on 17-10-24 下午3:35 * * @author kuangcp */ -public class Myth { - private String name; - private String phone; - private Long test; - - public Long getTest() { - return test; - } - - public void setTest(Long test) { - this.test = test; - } - - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getPhone() { - return phone; - } +@Data +class Myth { - public void setPhone(String phone) { - this.phone = phone; - } + private String name; + private String phone; + private Long test; - @Override - public String toString() { - return "Myth{" + - "name='" + name + '\'' + - ", phone='" + phone + '\'' + - ", test=" + test + - '}'; - } } diff --git a/class/src/main/java/com/github/kuangcp/serialize/customserialize/MythSerialize.java b/class/src/main/java/com/github/kuangcp/serialize/customserialize/MythSerialize.java index 56df63eb..20abef23 100644 --- a/class/src/main/java/com/github/kuangcp/serialize/customserialize/MythSerialize.java +++ b/class/src/main/java/com/github/kuangcp/serialize/customserialize/MythSerialize.java @@ -12,9 +12,9 @@ * @author kuangcp */ @Slf4j -public class MythSerialize { +class MythSerialize { - public T in(Class target, InputStream inputStream) { + T in(Class target, InputStream inputStream) { T object = null; try { Reader reader = new InputStreamReader(inputStream); @@ -25,7 +25,6 @@ public T in(Class target, InputStream inputStream) { object = target.newInstance(); Method[] methods = target.getDeclaredMethods(); for (int i = 0; i < result.length; i += 3) { -// System.out.println("name:"+result[i+1]); for (Method method : methods) { if (method.getName().startsWith("set") && method.getName() .equals("set" + result[i + 1])) { diff --git a/dependency.gradle b/dependency.gradle index 2779e4c1..1e08021a 100644 --- a/dependency.gradle +++ b/dependency.gradle @@ -18,6 +18,7 @@ ext { , spring : '5.1.5.RELEASE' , aspectj : '1.9.0' , hadoop : '2.7.2' + , flink : '1.8.0' ] libs = [ // mine tool @@ -76,6 +77,10 @@ ext { // kafka , "kafka-clients" : "org.apache.kafka:kafka-clients:$ver.kafka" + + // flink + , "flink-java" : "org.apache.flink:flink-java:$ver.flink" + , "flink-clients" : "org.apache.flink:flink-clients_2.12:$ver.flink" // hadoop , "hadoop-common" : "org.apache.hadoop:hadoop-common:$ver.hadoop" diff --git a/flink/build.gradle b/flink/build.gradle new file mode 100644 index 00000000..8e9cdec8 --- /dev/null +++ b/flink/build.gradle @@ -0,0 +1,4 @@ +dependencies { + implementation libs['flink-java'] + implementation libs['flink-clients'] +} \ No newline at end of file diff --git a/flink/src/main/java/com/github/kuangcp/hi/PartsSpuStatistic.java b/flink/src/main/java/com/github/kuangcp/hi/PartsSpuStatistic.java new file mode 100644 index 00000000..a9530f7f --- /dev/null +++ b/flink/src/main/java/com/github/kuangcp/hi/PartsSpuStatistic.java @@ -0,0 +1,97 @@ +package com.github.kuangcp.hi; + +import com.github.kuangcp.hi.domain.CalculateVO; +import com.github.kuangcp.hi.util.HDFSOutputFormat; +import com.github.kuangcp.hi.util.SourceProvider; +import java.nio.charset.StandardCharsets; +import java.util.Base64; +import java.util.List; +import java.util.Objects; +import lombok.extern.slf4j.Slf4j; +import org.apache.flink.api.common.functions.MapFunction; +import org.apache.flink.api.java.DataSet; +import org.apache.flink.api.java.ExecutionEnvironment; +import org.apache.flink.api.java.operators.AggregateOperator; +import org.apache.flink.api.java.operators.DataSource; +import org.apache.flink.api.java.tuple.Tuple2; + +/** + * 将数据格式化并输出到HDFS + * + * @author kuangchengping@qipeipu.com + * @since 2019-05-16 16:26 + */ +@Slf4j +public class PartsSpuStatistic { + + private static final ExecutionEnvironment ENV; + + static { + ENV = ExecutionEnvironment.getExecutionEnvironment(); + } + + public static void main(String[] args) throws Exception { + try { + if (Objects.isNull(args) || args.length != 1) { + log.warn("invalid param: args={} {}", args, ""); + return; + } + + String originStr = new String(Base64.getDecoder().decode(args[0]), StandardCharsets.UTF_8); + log.info("command: originStr={}", originStr); + + calculateOrder(); + } catch (Throwable e) { + log.error(e.getMessage(), e); + } + // 必须是方法的最后一行 否则 Flink 的执行计划无法生成 + ENV.execute("PartsSpuStatistic"); + } + + @SuppressWarnings("deprecation") + static void calculateOrder() throws Exception { + + calculateAndAggregate(new CalculateVO()); + } + + /** + * 分页计算并聚合 + */ + private static void calculateAndAggregate(CalculateVO partitionVO) { + AggregateOperator> result = null; + + SourceProvider provider = new SourceProvider(partitionVO); + while (provider.hasNextPage()) { + List data = provider.generateResource(); + DataSource source = ENV.fromCollection(data); + + DataSet> counts = source + .filter(Objects::nonNull) + .map(new Mapper()) + .groupBy(0) + .sum(1); + + if (Objects.isNull(result)) { + result = counts.groupBy(0).sum(1); + } else { + DataSet> temp = result.union(counts); + result = temp.groupBy(0).sum(1); + } + } + + if (Objects.isNull(result)) { + return; + } + + result.output(new HDFSOutputFormat(partitionVO)); + } + + public static final class Mapper implements + MapFunction> { + + @Override + public Tuple2 map(String value) { + return new Tuple2<>(value, 1); + } + } +} diff --git a/flink/src/main/java/com/github/kuangcp/hi/domain/CalculateVO.java b/flink/src/main/java/com/github/kuangcp/hi/domain/CalculateVO.java new file mode 100644 index 00000000..cab9fe38 --- /dev/null +++ b/flink/src/main/java/com/github/kuangcp/hi/domain/CalculateVO.java @@ -0,0 +1,9 @@ +package com.github.kuangcp.hi.domain; + +/** + * @author https://github.com/kuangcp + * 2019-06-16 15:07 + */ +public class CalculateVO { + +} diff --git a/flink/src/main/java/com/github/kuangcp/hi/util/HDFSOutputFormat.java b/flink/src/main/java/com/github/kuangcp/hi/util/HDFSOutputFormat.java new file mode 100644 index 00000000..813379e3 --- /dev/null +++ b/flink/src/main/java/com/github/kuangcp/hi/util/HDFSOutputFormat.java @@ -0,0 +1,51 @@ +package com.github.kuangcp.hi.util; + +import com.github.kuangcp.hi.domain.CalculateVO; +import java.io.IOException; +import java.util.LinkedList; +import java.util.List; +import lombok.EqualsAndHashCode; +import lombok.extern.slf4j.Slf4j; +import org.apache.flink.api.common.io.RichOutputFormat; +import org.apache.flink.api.java.tuple.Tuple2; +import org.apache.flink.configuration.Configuration; + +/** + * @author kuangchengping@qipeipu.com + * @since 2019-05-30 20:36 + */ +@lombok.Data +@Slf4j +@EqualsAndHashCode(callSuper = true) +public class HDFSOutputFormat extends RichOutputFormat> { + + private CalculateVO partitionVO; + private long createTime = System.currentTimeMillis(); + private List> resultList = new LinkedList<>(); + + public HDFSOutputFormat(CalculateVO partitionVO) { + this.partitionVO = partitionVO; + } + + @Override + public void configure(Configuration parameters) { + } + + @Override + public void open(int taskNumber, int numTasks) throws IOException { + } + + @Override + public void writeRecord(Tuple2 record) throws IOException { + resultList.add(record); + } + + @Override + public void close() { + try { + } catch (Exception e) { + log.error("创建文件失败, HDFS 服务不可用"); + log.error(e.getMessage(), e); + } + } +} diff --git a/flink/src/main/java/com/github/kuangcp/hi/util/SourceProvider.java b/flink/src/main/java/com/github/kuangcp/hi/util/SourceProvider.java new file mode 100644 index 00000000..6d9ddcec --- /dev/null +++ b/flink/src/main/java/com/github/kuangcp/hi/util/SourceProvider.java @@ -0,0 +1,27 @@ +package com.github.kuangcp.hi.util; + +import com.github.kuangcp.hi.domain.CalculateVO; +import java.util.List; + +/** + * @author https://github.com/kuangcp + * 2019-06-16 15:12 + */ +public class SourceProvider { + + + private int count = 10; + + private int cursor; + + public SourceProvider(CalculateVO calculateVO) { + } + + public boolean hasNextPage() { + return cursor <= count; + } + + public List generateResource() { + return null; + } +} diff --git a/flink/src/main/resources/log4j.properties b/flink/src/main/resources/log4j.properties new file mode 100644 index 00000000..b4decd71 --- /dev/null +++ b/flink/src/main/resources/log4j.properties @@ -0,0 +1,9 @@ +log4j.rootLogger=INFO, stdout + +# Direct log messages to stdout +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.Target=System.out +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} [%t] %-5p %c{1}:%L - %m%n + +log4j.logger.com.baturu.ofc.batch = debug diff --git a/flink/src/main/resources/online/config.properties b/flink/src/main/resources/online/config.properties new file mode 100644 index 00000000..3d88f42a --- /dev/null +++ b/flink/src/main/resources/online/config.properties @@ -0,0 +1,46 @@ +kafka.bootstrap.servers=10.1.1.176:9092,10.1.1.177:9092,10.1.1.182:9092 +kafka.zookeeper.connect=10.1.1.105:2181,10.1.1.112:2181,10.1.1.117:2181 +kafka.group.id=ofc-batch-online + +druid.initialSize=3 +druid.minIdle=3 +druid.maxActive=20 +druid.maxWait=60000 +druid.timeBetweenEvictionRunsMillis=60000 +druid.minEvictableIdleTimeMillis=300000 +druid.validationQuery=SELECT 'x' +druid.testWhileIdle=true +druid.testOnBorrow=false +druid.testOnReturn=false +druid.filters=config +druid.driverClass=com.mysql.jdbc.Driver +druid.url=jdbc:mysql://10.1.1.62:3308/ofc?useSSL=false&useUnicode=true&characterEncoding=utf8&ApplicationName=ofc-service +druid.username=ofcser_ofc_rw +druid.password=LLWETtEKeFbaDRspSxjgxB1cyxVWWScxdytlZvrnjT/buuxRNWfwFDgDoihSY2w3vnsu2C6pGMdnXpBQKKdj+Q== +druid.properties.config.decrypt=true +druid.properties.config.decrypt.key=MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJg++yYLNUAF3kU0ouCnu2ka7axBo1fV6OAdDkOE9b1aKJrfv1x9xwjyGNWq3FPUSH+hWcdCK9lxIaDcnkkswUkCAwEAAQ== + +mongodb.uri=mongodb://ofcser_ofc_rw:q_jgwNUa3ntuskm0knqw@10.1.1.8:27017,10.1.1.58:27017,10.1.1.35:27017/ofc + +## batch +hdfs.server=hdfs://btr-dfs/ +hdfs.rootPath=/flink-batch + +## 保存2018年数据 +history.initialSize=3 +history.minIdle=3 +history.maxActive=20 +history.maxWait=60000 +history.timeBetweenEvictionRunsMillis=60000 +history.minEvictableIdleTimeMillis=300000 +history.validationQuery=SELECT 'x' +history.testWhileIdle=true +history.testOnBorrow=false +history.testOnReturn=false +history.filters=config +history.driverClass=com.mysql.jdbc.Driver +history.url=jdbc:mysql://192.168.0.253:3308/erp?useUnicode=true&characterEncoding=UTF-8&useOldAliasMetadataBehavior=true&zeroDateTimeBehavior=convertToNull +history.username=ops +history.password=gm5SUL+B3fMNCEO9uICnz8bs5hfZ3Gs+teSWEXfbPqR/n6OWbiPEVdfDBQWgZ7fj/4s5Q+Fs+VzkdjuOZ2nYW/riU7i9G2wtuzk5DmQqx5NJ4j0eEiVv1/8vrDMmTP2nz1C+yeSaQtfoihL1KD5ccaeR8y/P1SL37twRfi7QAx8= +history.properties.config.decrypt=true +history.properties.config.decrypt.key=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCYAUx8IzC60NZfpgjblDIft0gnZCsg6tNdu3K3vL8PeT6ruD++YMlGr/+IfmskLPH6Cexof/IlFfq3hSAGIu5mX3qihbP3bCt/aGyhDQtZ3EQfdHhsYdMlbx5b8FGca00aq0wagYuQ1ZfNMc8BnWMFfPGBnzpcn2cS+sM681+TxwIDAQAB diff --git a/flink/src/main/resources/test32/config.properties b/flink/src/main/resources/test32/config.properties new file mode 100644 index 00000000..53f1a5ed --- /dev/null +++ b/flink/src/main/resources/test32/config.properties @@ -0,0 +1,48 @@ +#kafka.bootstrap.servers=127.0.0.1:9092 +#kafka.zookeeper.connect=127.0.0.1:32768 +kafka.group.id=ofc-batch-online +kafka.bootstrap.servers=kafka-service:9092,kafka-service2:9092,kafka-service3:9092 +kafka.zookeeper.connect=zookeeper-service:2181 + +druid.initialSize=3 +druid.minIdle=3 +druid.maxActive=20 +druid.maxWait=60000 +druid.timeBetweenEvictionRunsMillis=60000 +druid.minEvictableIdleTimeMillis=300000 +druid.validationQuery=SELECT 'x' +druid.testWhileIdle=true +druid.testOnBorrow=false +druid.testOnReturn=false +druid.filters=config +druid.driverClass=com.mysql.jdbc.Driver +druid.url=jdbc:mysql://192.168.0.253:3308/ofc?useSSL=false&useUnicode=true&characterEncoding=utf8&ApplicationName=ofc-service +druid.username=ops +druid.password=vA/pz/hp/GWy2OI1dE9yH9rh1tk60XlmlXZePPg/jeOzRRfhGc2H+XnRjNGmQkBLGsQAbIPtffXPvc2/v02tNQ== +druid.properties.config.decrypt=true +druid.properties.config.decrypt.key=MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAL+lEjpcEjlpZt5aFXNu6dE6RcJ6t+pkGtGBBCite11vnYuBXkPZj6dliEziQrnGkWHR23tROm5gVN6OVrGYgOECAwEAAQ== + +mongodb.uri=mongodb://ofcser_ofc_rw:swhqEGie0fw3ivgbz[ba@172.16.8.3:27017,172.16.8.3:27018,172.16.8.3:27019/ofc + +## batch +hdfs.server=hdfs://172.16.16.51:8020 +hdfs.rootPath=/flink-batch + +## 保存2018年数据 +history.initialSize=3 +history.minIdle=3 +history.maxActive=20 +history.maxWait=60000 +history.timeBetweenEvictionRunsMillis=60000 +history.minEvictableIdleTimeMillis=300000 +history.validationQuery=SELECT 'x' +history.testWhileIdle=true +history.testOnBorrow=false +history.testOnReturn=false +history.filters=config +history.driverClass=com.mysql.jdbc.Driver +history.url=jdbc:mysql://192.168.0.253:3308/erp?useUnicode=true&characterEncoding=UTF-8&useOldAliasMetadataBehavior=true&zeroDateTimeBehavior=convertToNull +history.username=ops +history.password=gm5SUL+B3fMNCEO9uICnz8bs5hfZ3Gs+teSWEXfbPqR/n6OWbiPEVdfDBQWgZ7fj/4s5Q+Fs+VzkdjuOZ2nYW/riU7i9G2wtuzk5DmQqx5NJ4j0eEiVv1/8vrDMmTP2nz1C+yeSaQtfoihL1KD5ccaeR8y/P1SL37twRfi7QAx8= +history.properties.config.decrypt=true +history.properties.config.decrypt.key=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCYAUx8IzC60NZfpgjblDIft0gnZCsg6tNdu3K3vL8PeT6ruD++YMlGr/+IfmskLPH6Cexof/IlFfq3hSAGIu5mX3qihbP3bCt/aGyhDQtZ3EQfdHhsYdMlbx5b8FGca00aq0wagYuQ1ZfNMc8BnWMFfPGBnzpcn2cS+sM681+TxwIDAQAB diff --git a/flink/src/main/resources/test33/config.properties b/flink/src/main/resources/test33/config.properties new file mode 100644 index 00000000..e9d73424 --- /dev/null +++ b/flink/src/main/resources/test33/config.properties @@ -0,0 +1,28 @@ +#kafka.bootstrap.servers=127.0.0.1:9092 +#kafka.zookeeper.connect=127.0.0.1:32768 +kafka.group.id=ofc-batch-online +kafka.bootstrap.servers=172.16.16.125:9092,172.16.16.245:9092, 172.16.16.252:9092 +kafka.zookeeper.connect=172.16.17.53:2181 + +druid.initialSize=3 +druid.minIdle=3 +druid.maxActive=20 +druid.maxWait=60000 +druid.timeBetweenEvictionRunsMillis=60000 +druid.minEvictableIdleTimeMillis=300000 +druid.validationQuery=SELECT 'x' +druid.testWhileIdle=true +druid.testOnBorrow=false +druid.testOnReturn=false +druid.filters=config +druid.driverClass=com.mysql.jdbc.Driver +druid.url=jdbc:mysql://192.168.0.253:3308/ofc?useSSL=false&useUnicode=true&characterEncoding=utf8&ApplicationName=ofc-service +druid.username=ops +druid.password=vA/pz/hp/GWy2OI1dE9yH9rh1tk60XlmlXZePPg/jeOzRRfhGc2H+XnRjNGmQkBLGsQAbIPtffXPvc2/v02tNQ== +druid.properties.config.decrypt=true +druid.properties.config.decrypt.key=MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAL+lEjpcEjlpZt5aFXNu6dE6RcJ6t+pkGtGBBCite11vnYuBXkPZj6dliEziQrnGkWHR23tROm5gVN6OVrGYgOECAwEAAQ== + +mongodb.uri=mongodb://ofcser_ofc_rw:swhqEGie0fw3ivgbz[ba@172.16.8.3:27017,172.16.8.3:27018,172.16.8.3:27019/ofc + +hdfs.server=hdfs://172.16.16.80:8020 +hdfs.rootPath=/flink-batch/ diff --git a/flink/src/main/resources/test36/config.properties b/flink/src/main/resources/test36/config.properties new file mode 100644 index 00000000..af04c5e1 --- /dev/null +++ b/flink/src/main/resources/test36/config.properties @@ -0,0 +1,28 @@ +#kafka.bootstrap.servers=127.0.0.1:9092 +#kafka.zookeeper.connect=127.0.0.1:32768 +kafka.group.id=ofc-batch-online +kafka.bootstrap.servers=172.16.17.214:9092 +kafka.zookeeper.connect=172.16.17.218:2181 + +druid.initialSize=3 +druid.minIdle=3 +druid.maxActive=20 +druid.maxWait=60000 +druid.timeBetweenEvictionRunsMillis=60000 +druid.minEvictableIdleTimeMillis=300000 +druid.validationQuery=SELECT 'x' +druid.testWhileIdle=true +druid.testOnBorrow=false +druid.testOnReturn=false +druid.filters=config +druid.driverClass=com.mysql.jdbc.Driver +druid.url=jdbc:mysql://192.168.0.253:3308/ofc?useSSL=false&useUnicode=true&characterEncoding=utf8&ApplicationName=ofc-service +druid.username=ops +druid.password=vA/pz/hp/GWy2OI1dE9yH9rh1tk60XlmlXZePPg/jeOzRRfhGc2H+XnRjNGmQkBLGsQAbIPtffXPvc2/v02tNQ== +druid.properties.config.decrypt=true +druid.properties.config.decrypt.key=MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAL+lEjpcEjlpZt5aFXNu6dE6RcJ6t+pkGtGBBCite11vnYuBXkPZj6dliEziQrnGkWHR23tROm5gVN6OVrGYgOECAwEAAQ== + +mongodb.uri=mongodb://ofcser_ofc_rw:swhqEGie0fw3ivgbz[ba@172.16.8.3:27017,172.16.8.3:27018,172.16.8.3:27019/ofc + +hdfs.server=hdfs://172.16.16.80:8020 +hdfs.rootPath=/flink-batch/ diff --git a/flink/src/main/resources/test37/config.properties b/flink/src/main/resources/test37/config.properties new file mode 100644 index 00000000..620fba22 --- /dev/null +++ b/flink/src/main/resources/test37/config.properties @@ -0,0 +1,37 @@ +kafka.bootstrap.servers=172.16.16.106:9092,172.16.16.107:9092,172.16.18.98:9092 +kafka.zookeeper.connect=172.16.18.102:2181 + +kafka.group.id=stream-test +# \uFFFD\uFFFD\u02BC\uFFFD\uFFFD\u02B1\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u04F5\u0138\uFFFD\uFFFD\uFFFD +druid.initialSize=3 +# \uFFFD\uFFFD\u0421\uFFFD\uFFFD\uFFFD\u04F3\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD +druid.minIdle=3 +# \uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u04F3\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD +druid.maxActive=20 +# \uFFFD\uFFFD\u0221\uFFFD\uFFFD\uFFFD\uFFFD\u02B1\uFFFD\uFFFD\uFFFD\u0234\uFFFD\u02B1\uFFFD\u48EC\uFFFD\uFFFD\u03BB\uFFFD\uFFFD\uFFFD\uFFFD +druid.maxWait=60000 +# Destroy\uFFFD\u07F3\u033C\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u04F5\u013C\uFFFD\uFFFD\u02B1\uFFFD\uFFFD +druid.timeBetweenEvictionRunsMillis=60000 +# \uFFFD\uFFFD\uFFFD\u04F1\uFFFD\uFFFD\u05BF\uFFFD\uFFFD\u0436\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uECE4\u02B1\uFFFD\uFFFD +druid.minEvictableIdleTimeMillis=300000 +# \uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u01F7\uFFFD\uFFFD\uFFFD\u0427\uFFFD\uFFFDsql +druid.validationQuery=SELECT 'x' +# \u05B4\uFFFD\uFFFDvalidationQuery\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u01F7\uFFFD\uFFFD\uFFFD\u0427 +druid.testWhileIdle=true +# \uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u02B1\u05B4\uFFFD\uFFFDvalidationQuery\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u01F7\uFFFD\uFFFD\uFFFD\u0427 +druid.testOnBorrow=false +# \uFFFD\u9EF9\uFFFD\uFFFD\uFFFD\uFFFD\u02B1\u05B4\uFFFD\uFFFDvalidationQuery\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u01F7\uFFFD\uFFFD\uFFFD\u0427 +druid.testOnReturn=false +# \uFFFD\uFFFD\u02BEDruid\uFFFD\uFFFD\uFFFD\uFFFD\u0534\uFFFD\uFFFD\u04AA\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u077F\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u043D\uFFFD\uFFFD\uFFFD +druid.filters=config +druid.driverClass=com.mysql.jdbc.Driver +druid.url=jdbc:mysql://192.168.0.253:3308/ofc?useSSL=false&useUnicode=true&characterEncoding=utf8&ApplicationName=ofc-service +druid.username=ops +# \uFFFD\uFFFD\uFFFD\u073A\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD +druid.password=vA/pz/hp/GWy2OI1dE9yH9rh1tk60XlmlXZePPg/jeOzRRfhGc2H+XnRjNGmQkBLGsQAbIPtffXPvc2/v02tNQ== +druid.properties.config.decrypt=true +druid.properties.config.decrypt.key=MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAL+lEjpcEjlpZt5aFXNu6dE6RcJ6t+pkGtGBBCite11vnYuBXkPZj6dliEziQrnGkWHR23tROm5gVN6OVrGYgOECAwEAAQ== +mongodb.uri=mongodb://ofcser_ofc_rw:swhqEGie0fw3ivgbz[ba@172.16.8.3:27017,172.16.8.3:27018,172.16.8.3:27019/ofc + +hdfs.server=hdfs://172.16.16.80:8020 +hdfs.rootPath=/flink-batch/ diff --git a/flink/src/test/java/com/github/kuangcp/hi/PartsSpuStatisticTest.java b/flink/src/test/java/com/github/kuangcp/hi/PartsSpuStatisticTest.java new file mode 100644 index 00000000..e1df0609 --- /dev/null +++ b/flink/src/test/java/com/github/kuangcp/hi/PartsSpuStatisticTest.java @@ -0,0 +1,75 @@ +//package com.github.kuangcp.hi; +// +//import com.github.kuangcp.hi.util.SplitJobCommand; +//import com.baturu.ofc.constant.statistic.ProductStatisticJobCommand; +//import com.baturu.ofc.constant.statistic.ProductStatisticSpan; +//import com.baturu.ofc.util.JsonMapper; +//import org.junit.Test; +//import org.springframework.http.HttpEntity; +//import org.springframework.http.ResponseEntity; +//import org.springframework.web.client.RestTemplate; +// +//import java.nio.charset.StandardCharsets; +//import java.time.LocalDateTime; +//import java.util.Base64; +//import java.util.HashMap; +//import java.util.HashSet; +// +//import static com.github.kuangcp.hi.PartsSpuStatistic.calculateOrder; +// +///** +// * @author kuangchengping@qipeipu.com +// * @since 2019-05-21 14:59 +// */ +//public class PartsSpuStatisticTest { +// +// @Test +// public void testCalculate() throws Exception { +// HashSet spans = new HashSet<>(); +// spans.add(ProductStatisticSpan.MONTH); +// +// ProductStatisticJobCommand msg = ProductStatisticJobCommand.builder() +// .productStatisticSpan(spans) +// .startTime(SplitJobCommand.toDate(LocalDateTime.now().withMonth(1))) +// .endTime(SplitJobCommand.toDate(LocalDateTime.now().withMonth(4))) +// .build(); +// +// calculateOrder(msg); +// } +// +// @Test +// public void testJson() { +// HashSet spans = new HashSet<>(); +// spans.add(ProductStatisticSpan.MONTH); +// +// ProductStatisticJobCommand msg = ProductStatisticJobCommand.builder() +// .id("12") +// .productStatisticSpan(spans) +// .startTime(SplitJobCommand.toDate(LocalDateTime.now().withMonth(1))) +// .endTime(SplitJobCommand.toDate(LocalDateTime.now().withMonth(3))) +// .build(); +// String json = JsonMapper.obj2String(msg); +// +// byte[] result = Base64.getEncoder().encode(json.getBytes()); +// System.out.println(new String(result, StandardCharsets.UTF_8)); +// +// byte[] origin = Base64.getDecoder().decode(result); +// System.out.println(new String(origin, StandardCharsets.UTF_8)); +// } +// +// @Test +// public void testRest() { +// String BASE_URL = "http://127.0.0.1:8081/jars/"; +// String JAR_NAME = "6b74c981-ed07-4ad3-97e0-5e47f570ca49_btr-ofc-batch-test32-jar-with-dependencies.jar"; +// +// String msg = "eyJpZCI6IjEyIiwicHJvZHVjdFN0YXRpc3RpY1NwYW4iOlsiTU9OVEgiXSwic3RhcnRUaW1lIjoxNTQ4OTE0MTY3NzcxLCJlbmRUaW1lIjoxNTU0MDExNzY3ODExfQ=="; +// String url = BASE_URL + JAR_NAME + "/run?entry-class=com.baturu.ofc.batch.PartsSpuStatistic&program-args=" + msg; +//// String url = BASE_URL + JAR_NAME + "/run?entry-class=com.baturu.ofc.batch.word.WordCount"; +// +// RestTemplate restTemplate = new RestTemplate(); +// HttpEntity httpEntity = new HttpEntity<>(new HashMap<>()); +// +// ResponseEntity request = restTemplate.postForEntity(url, httpEntity, String.class); +// System.out.println(request.getBody()); +// } +//} \ No newline at end of file diff --git a/kafka/src/main/java/com/github/kuangcp/hi/ConsumerDemo.java b/kafka/src/main/java/com/github/kuangcp/hi/ConsumerDemo.java index 641b9c8c..3a856fce 100644 --- a/kafka/src/main/java/com/github/kuangcp/hi/ConsumerDemo.java +++ b/kafka/src/main/java/com/github/kuangcp/hi/ConsumerDemo.java @@ -4,7 +4,7 @@ import static com.github.kuangcp.hi.Constants.KAFKA_SERVER; import com.fasterxml.jackson.databind.ObjectMapper; -import com.github.kuangcp.hi.dto.StartCommand; +import com.github.kuangcp.hi.domain.StartCommand; import java.io.IOException; import java.time.Duration; import java.util.Collections; diff --git a/kafka/src/main/java/com/github/kuangcp/hi/ProducerDemo.java b/kafka/src/main/java/com/github/kuangcp/hi/ProducerDemo.java index a7e487cf..0b51bed2 100644 --- a/kafka/src/main/java/com/github/kuangcp/hi/ProducerDemo.java +++ b/kafka/src/main/java/com/github/kuangcp/hi/ProducerDemo.java @@ -5,9 +5,9 @@ import static com.github.kuangcp.hi.Constants.START_TOPIC; import com.fasterxml.jackson.databind.ObjectMapper; -import com.github.kuangcp.hi.dto.ProductStatisticJobCommand; -import com.github.kuangcp.hi.dto.ProductStatisticSpan; -import com.github.kuangcp.hi.dto.StartCommand; +import com.github.kuangcp.hi.domain.ProductStatisticJobCommand; +import com.github.kuangcp.hi.domain.ProductStatisticSpan; +import com.github.kuangcp.hi.domain.StartCommand; import com.github.kuangcp.io.ResourceTool; import java.io.IOException; import java.util.Date; diff --git a/kafka/src/main/java/com/github/kuangcp/hi/dto/ProductStatisticJobCommand.java b/kafka/src/main/java/com/github/kuangcp/hi/domain/ProductStatisticJobCommand.java similarity index 93% rename from kafka/src/main/java/com/github/kuangcp/hi/dto/ProductStatisticJobCommand.java rename to kafka/src/main/java/com/github/kuangcp/hi/domain/ProductStatisticJobCommand.java index ee018139..7024b1f9 100644 --- a/kafka/src/main/java/com/github/kuangcp/hi/dto/ProductStatisticJobCommand.java +++ b/kafka/src/main/java/com/github/kuangcp/hi/domain/ProductStatisticJobCommand.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.hi.dto; +package com.github.kuangcp.hi.domain; import lombok.AllArgsConstructor; import lombok.Builder; diff --git a/kafka/src/main/java/com/github/kuangcp/hi/dto/ProductStatisticSpan.java b/kafka/src/main/java/com/github/kuangcp/hi/domain/ProductStatisticSpan.java similarity index 96% rename from kafka/src/main/java/com/github/kuangcp/hi/dto/ProductStatisticSpan.java rename to kafka/src/main/java/com/github/kuangcp/hi/domain/ProductStatisticSpan.java index 073a3370..7d092e87 100644 --- a/kafka/src/main/java/com/github/kuangcp/hi/dto/ProductStatisticSpan.java +++ b/kafka/src/main/java/com/github/kuangcp/hi/domain/ProductStatisticSpan.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.hi.dto; +package com.github.kuangcp.hi.domain; import lombok.Getter; diff --git a/kafka/src/main/java/com/github/kuangcp/hi/dto/StartCommand.java b/kafka/src/main/java/com/github/kuangcp/hi/domain/StartCommand.java similarity index 88% rename from kafka/src/main/java/com/github/kuangcp/hi/dto/StartCommand.java rename to kafka/src/main/java/com/github/kuangcp/hi/domain/StartCommand.java index a278cc9e..588b191e 100644 --- a/kafka/src/main/java/com/github/kuangcp/hi/dto/StartCommand.java +++ b/kafka/src/main/java/com/github/kuangcp/hi/domain/StartCommand.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.hi.dto; +package com.github.kuangcp.hi.domain; import java.util.Date; import lombok.AllArgsConstructor; diff --git a/kafka/src/main/java/com/github/kuangcp/hi/dto/StatisticSpan.java b/kafka/src/main/java/com/github/kuangcp/hi/domain/StatisticSpan.java similarity index 85% rename from kafka/src/main/java/com/github/kuangcp/hi/dto/StatisticSpan.java rename to kafka/src/main/java/com/github/kuangcp/hi/domain/StatisticSpan.java index b1ba2676..96f409da 100644 --- a/kafka/src/main/java/com/github/kuangcp/hi/dto/StatisticSpan.java +++ b/kafka/src/main/java/com/github/kuangcp/hi/domain/StatisticSpan.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.hi.dto; +package com.github.kuangcp.hi.domain; public interface StatisticSpan { diff --git a/question/src/main/java/com/github/kuangcp/math/Martingale.java b/question/src/main/java/com/github/kuangcp/math/Martingale.java new file mode 100644 index 00000000..a0564fe1 --- /dev/null +++ b/question/src/main/java/com/github/kuangcp/math/Martingale.java @@ -0,0 +1,47 @@ +package com.github.kuangcp.math; + +import java.util.concurrent.ThreadLocalRandom; +import lombok.extern.slf4j.Slf4j; + +/** + * @author https://github.com/kuangcp + * 2019-06-16 11:19 + */ +@Slf4j +class Martingale { + + /** + * 一种赌博方式, 如果输了就加倍投入, 初始本金为1 + * 乍一看, 只要赢了一次, 前面的投入就都回本了, 且盈利 1, 但是前提是具有足量的资金 + * 但是赌场的资金是远比你的资金雄厚的, 所以赌徒久赌必输, 这还是建立在胜率5成的基础上 + * + * @param principal 本金 + * @param count 赌博次数 + * @return 剩余本金 + */ + long martingale(long principal, int count) { + if (count < 0) { + return principal; + } + long origin = principal; + long start = 1L; + for (int i = 0; i < count; i++) { + if (principal < start) { + log.warn("本金不足: principal={} except={}", principal, start); + return principal; + } + + principal -= start; + int temp = ThreadLocalRandom.current().nextInt(0, 100); + // 假定输的概率 五成 + if (temp > 50) { + log.info("亏损:{} 当前变动:{}", start, (principal - origin)); + start *= 2; + } else { + log.info("盈利:{} 当前变动:{}", start, (principal - origin)); + principal += start * 2; + } + } + return principal; + } +} diff --git a/question/src/test/java/com/github/kuangcp/math/MartingaleTest.java b/question/src/test/java/com/github/kuangcp/math/MartingaleTest.java new file mode 100644 index 00000000..565afc66 --- /dev/null +++ b/question/src/test/java/com/github/kuangcp/math/MartingaleTest.java @@ -0,0 +1,20 @@ +package com.github.kuangcp.math; + +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; + +/** + * @author https://github.com/kuangcp + * 2019-06-16 11:36 + */ +@Slf4j +public class MartingaleTest { + + private Martingale martingale = new Martingale(); + + @Test + public void testMartingale() { + long result = martingale.martingale(99999999L, 2000); + log.info("result={}", result); + } +} diff --git a/settings.gradle b/settings.gradle index bcc5c668..1935f692 100644 --- a/settings.gradle +++ b/settings.gradle @@ -17,4 +17,5 @@ include( , 'spring' , 'kafka' , 'hadoop' + , 'flink' ) From ac3574444ba3a914ab3c00ec8effaae6747bff04 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Sun, 16 Jun 2019 19:45:14 +0800 Subject: [PATCH 055/476] +) complete flink batch demo --- flink/build.gradle | 18 ++++++- ...SpuStatistic.java => SimpleStatistic.java} | 32 ++++--------- .../github/kuangcp/hi/domain/CalculateVO.java | 9 ---- ...{HDFSOutputFormat.java => SimpleSink.java} | 18 ++++--- .../kuangcp/hi/util/SourceProvider.java | 21 ++++++-- .../main/resources/online/config.properties | 46 ------------------ .../main/resources/test32/config.properties | 48 ------------------- .../main/resources/test33/config.properties | 28 ----------- .../main/resources/test36/config.properties | 28 ----------- .../main/resources/test37/config.properties | 37 -------------- flink/upload.sh | 7 +++ 11 files changed, 62 insertions(+), 230 deletions(-) rename flink/src/main/java/com/github/kuangcp/hi/{PartsSpuStatistic.java => SimpleStatistic.java} (69%) delete mode 100644 flink/src/main/java/com/github/kuangcp/hi/domain/CalculateVO.java rename flink/src/main/java/com/github/kuangcp/hi/util/{HDFSOutputFormat.java => SimpleSink.java} (67%) delete mode 100644 flink/src/main/resources/online/config.properties delete mode 100644 flink/src/main/resources/test32/config.properties delete mode 100644 flink/src/main/resources/test33/config.properties delete mode 100644 flink/src/main/resources/test36/config.properties delete mode 100644 flink/src/main/resources/test37/config.properties create mode 100755 flink/upload.sh diff --git a/flink/build.gradle b/flink/build.gradle index 8e9cdec8..05b9fe60 100644 --- a/flink/build.gradle +++ b/flink/build.gradle @@ -1,4 +1,20 @@ +version = '1.0.0-SNAPSHOT' + +apply plugin: 'java' +apply plugin: 'application' + dependencies { implementation libs['flink-java'] implementation libs['flink-clients'] -} \ No newline at end of file +} + +task uberJar(type: Jar) { + archiveClassifier = 'all-dependency' + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll {it.name.endsWith('jar')}.collect {zipTree(it)} + } +} diff --git a/flink/src/main/java/com/github/kuangcp/hi/PartsSpuStatistic.java b/flink/src/main/java/com/github/kuangcp/hi/SimpleStatistic.java similarity index 69% rename from flink/src/main/java/com/github/kuangcp/hi/PartsSpuStatistic.java rename to flink/src/main/java/com/github/kuangcp/hi/SimpleStatistic.java index a9530f7f..febfd0e3 100644 --- a/flink/src/main/java/com/github/kuangcp/hi/PartsSpuStatistic.java +++ b/flink/src/main/java/com/github/kuangcp/hi/SimpleStatistic.java @@ -1,10 +1,7 @@ package com.github.kuangcp.hi; -import com.github.kuangcp.hi.domain.CalculateVO; -import com.github.kuangcp.hi.util.HDFSOutputFormat; +import com.github.kuangcp.hi.util.SimpleSink; import com.github.kuangcp.hi.util.SourceProvider; -import java.nio.charset.StandardCharsets; -import java.util.Base64; import java.util.List; import java.util.Objects; import lombok.extern.slf4j.Slf4j; @@ -22,9 +19,10 @@ * @since 2019-05-16 16:26 */ @Slf4j -public class PartsSpuStatistic { +public class SimpleStatistic { private static final ExecutionEnvironment ENV; + private static final int taskNum = 5; static { ENV = ExecutionEnvironment.getExecutionEnvironment(); @@ -32,15 +30,9 @@ public class PartsSpuStatistic { public static void main(String[] args) throws Exception { try { - if (Objects.isNull(args) || args.length != 1) { - log.warn("invalid param: args={} {}", args, ""); - return; + for (int i = 0; i < taskNum; i++) { + calculateAndAggregate("batch-" + i); } - - String originStr = new String(Base64.getDecoder().decode(args[0]), StandardCharsets.UTF_8); - log.info("command: originStr={}", originStr); - - calculateOrder(); } catch (Throwable e) { log.error(e.getMessage(), e); } @@ -48,29 +40,25 @@ public static void main(String[] args) throws Exception { ENV.execute("PartsSpuStatistic"); } - @SuppressWarnings("deprecation") - static void calculateOrder() throws Exception { - - calculateAndAggregate(new CalculateVO()); - } - /** * 分页计算并聚合 */ - private static void calculateAndAggregate(CalculateVO partitionVO) { + private static void calculateAndAggregate(String batchId) { AggregateOperator> result = null; - SourceProvider provider = new SourceProvider(partitionVO); + SourceProvider provider = new SourceProvider(batchId); while (provider.hasNextPage()) { List data = provider.generateResource(); DataSource source = ENV.fromCollection(data); + // 合并窗口数据 DataSet> counts = source .filter(Objects::nonNull) .map(new Mapper()) .groupBy(0) .sum(1); + // 当前窗口和历史数据合并 if (Objects.isNull(result)) { result = counts.groupBy(0).sum(1); } else { @@ -83,7 +71,7 @@ private static void calculateAndAggregate(CalculateVO partitionVO) { return; } - result.output(new HDFSOutputFormat(partitionVO)); + result.output(new SimpleSink(batchId)); } public static final class Mapper implements diff --git a/flink/src/main/java/com/github/kuangcp/hi/domain/CalculateVO.java b/flink/src/main/java/com/github/kuangcp/hi/domain/CalculateVO.java deleted file mode 100644 index cab9fe38..00000000 --- a/flink/src/main/java/com/github/kuangcp/hi/domain/CalculateVO.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.github.kuangcp.hi.domain; - -/** - * @author https://github.com/kuangcp - * 2019-06-16 15:07 - */ -public class CalculateVO { - -} diff --git a/flink/src/main/java/com/github/kuangcp/hi/util/HDFSOutputFormat.java b/flink/src/main/java/com/github/kuangcp/hi/util/SimpleSink.java similarity index 67% rename from flink/src/main/java/com/github/kuangcp/hi/util/HDFSOutputFormat.java rename to flink/src/main/java/com/github/kuangcp/hi/util/SimpleSink.java index 813379e3..9f1447a1 100644 --- a/flink/src/main/java/com/github/kuangcp/hi/util/HDFSOutputFormat.java +++ b/flink/src/main/java/com/github/kuangcp/hi/util/SimpleSink.java @@ -1,6 +1,5 @@ package com.github.kuangcp.hi.util; -import com.github.kuangcp.hi.domain.CalculateVO; import java.io.IOException; import java.util.LinkedList; import java.util.List; @@ -17,14 +16,15 @@ @lombok.Data @Slf4j @EqualsAndHashCode(callSuper = true) -public class HDFSOutputFormat extends RichOutputFormat> { +public class SimpleSink extends RichOutputFormat> { - private CalculateVO partitionVO; + private String name; private long createTime = System.currentTimeMillis(); + // 如果给属性加上 transient 修饰, 就会报错 因为无法同步数据 private List> resultList = new LinkedList<>(); - public HDFSOutputFormat(CalculateVO partitionVO) { - this.partitionVO = partitionVO; + public SimpleSink(String name) { + this.name = name; } @Override @@ -43,8 +43,14 @@ public void writeRecord(Tuple2 record) throws IOException { @Override public void close() { try { + log.info("size={}", resultList.size()); + for (Tuple2 tuple : resultList) { + String name = tuple.f0; + Integer count = tuple.f1; + + System.out.println("FinalResult " + name + " " + count); + } } catch (Exception e) { - log.error("创建文件失败, HDFS 服务不可用"); log.error(e.getMessage(), e); } } diff --git a/flink/src/main/java/com/github/kuangcp/hi/util/SourceProvider.java b/flink/src/main/java/com/github/kuangcp/hi/util/SourceProvider.java index 6d9ddcec..636be796 100644 --- a/flink/src/main/java/com/github/kuangcp/hi/util/SourceProvider.java +++ b/flink/src/main/java/com/github/kuangcp/hi/util/SourceProvider.java @@ -1,27 +1,38 @@ package com.github.kuangcp.hi.util; -import com.github.kuangcp.hi.domain.CalculateVO; +import java.time.Month; import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import lombok.extern.slf4j.Slf4j; /** * @author https://github.com/kuangcp * 2019-06-16 15:12 */ +@Slf4j public class SourceProvider { + private String id; - private int count = 10; + private static final int pageNum = 8; + private static final int pageSize = 20; private int cursor; - public SourceProvider(CalculateVO calculateVO) { + public SourceProvider(String id) { + this.id = id; } public boolean hasNextPage() { - return cursor <= count; + return cursor < pageNum; } public List generateResource() { - return null; + cursor++; + log.info("source: id={} cursor={}", id, cursor); + return IntStream.rangeClosed(1, pageSize) + .mapToObj(i -> Month.of((i + cursor) % 12 + 1).toString()) + .collect(Collectors.toList()); } } diff --git a/flink/src/main/resources/online/config.properties b/flink/src/main/resources/online/config.properties deleted file mode 100644 index 3d88f42a..00000000 --- a/flink/src/main/resources/online/config.properties +++ /dev/null @@ -1,46 +0,0 @@ -kafka.bootstrap.servers=10.1.1.176:9092,10.1.1.177:9092,10.1.1.182:9092 -kafka.zookeeper.connect=10.1.1.105:2181,10.1.1.112:2181,10.1.1.117:2181 -kafka.group.id=ofc-batch-online - -druid.initialSize=3 -druid.minIdle=3 -druid.maxActive=20 -druid.maxWait=60000 -druid.timeBetweenEvictionRunsMillis=60000 -druid.minEvictableIdleTimeMillis=300000 -druid.validationQuery=SELECT 'x' -druid.testWhileIdle=true -druid.testOnBorrow=false -druid.testOnReturn=false -druid.filters=config -druid.driverClass=com.mysql.jdbc.Driver -druid.url=jdbc:mysql://10.1.1.62:3308/ofc?useSSL=false&useUnicode=true&characterEncoding=utf8&ApplicationName=ofc-service -druid.username=ofcser_ofc_rw -druid.password=LLWETtEKeFbaDRspSxjgxB1cyxVWWScxdytlZvrnjT/buuxRNWfwFDgDoihSY2w3vnsu2C6pGMdnXpBQKKdj+Q== -druid.properties.config.decrypt=true -druid.properties.config.decrypt.key=MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJg++yYLNUAF3kU0ouCnu2ka7axBo1fV6OAdDkOE9b1aKJrfv1x9xwjyGNWq3FPUSH+hWcdCK9lxIaDcnkkswUkCAwEAAQ== - -mongodb.uri=mongodb://ofcser_ofc_rw:q_jgwNUa3ntuskm0knqw@10.1.1.8:27017,10.1.1.58:27017,10.1.1.35:27017/ofc - -## batch -hdfs.server=hdfs://btr-dfs/ -hdfs.rootPath=/flink-batch - -## 保存2018年数据 -history.initialSize=3 -history.minIdle=3 -history.maxActive=20 -history.maxWait=60000 -history.timeBetweenEvictionRunsMillis=60000 -history.minEvictableIdleTimeMillis=300000 -history.validationQuery=SELECT 'x' -history.testWhileIdle=true -history.testOnBorrow=false -history.testOnReturn=false -history.filters=config -history.driverClass=com.mysql.jdbc.Driver -history.url=jdbc:mysql://192.168.0.253:3308/erp?useUnicode=true&characterEncoding=UTF-8&useOldAliasMetadataBehavior=true&zeroDateTimeBehavior=convertToNull -history.username=ops -history.password=gm5SUL+B3fMNCEO9uICnz8bs5hfZ3Gs+teSWEXfbPqR/n6OWbiPEVdfDBQWgZ7fj/4s5Q+Fs+VzkdjuOZ2nYW/riU7i9G2wtuzk5DmQqx5NJ4j0eEiVv1/8vrDMmTP2nz1C+yeSaQtfoihL1KD5ccaeR8y/P1SL37twRfi7QAx8= -history.properties.config.decrypt=true -history.properties.config.decrypt.key=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCYAUx8IzC60NZfpgjblDIft0gnZCsg6tNdu3K3vL8PeT6ruD++YMlGr/+IfmskLPH6Cexof/IlFfq3hSAGIu5mX3qihbP3bCt/aGyhDQtZ3EQfdHhsYdMlbx5b8FGca00aq0wagYuQ1ZfNMc8BnWMFfPGBnzpcn2cS+sM681+TxwIDAQAB diff --git a/flink/src/main/resources/test32/config.properties b/flink/src/main/resources/test32/config.properties deleted file mode 100644 index 53f1a5ed..00000000 --- a/flink/src/main/resources/test32/config.properties +++ /dev/null @@ -1,48 +0,0 @@ -#kafka.bootstrap.servers=127.0.0.1:9092 -#kafka.zookeeper.connect=127.0.0.1:32768 -kafka.group.id=ofc-batch-online -kafka.bootstrap.servers=kafka-service:9092,kafka-service2:9092,kafka-service3:9092 -kafka.zookeeper.connect=zookeeper-service:2181 - -druid.initialSize=3 -druid.minIdle=3 -druid.maxActive=20 -druid.maxWait=60000 -druid.timeBetweenEvictionRunsMillis=60000 -druid.minEvictableIdleTimeMillis=300000 -druid.validationQuery=SELECT 'x' -druid.testWhileIdle=true -druid.testOnBorrow=false -druid.testOnReturn=false -druid.filters=config -druid.driverClass=com.mysql.jdbc.Driver -druid.url=jdbc:mysql://192.168.0.253:3308/ofc?useSSL=false&useUnicode=true&characterEncoding=utf8&ApplicationName=ofc-service -druid.username=ops -druid.password=vA/pz/hp/GWy2OI1dE9yH9rh1tk60XlmlXZePPg/jeOzRRfhGc2H+XnRjNGmQkBLGsQAbIPtffXPvc2/v02tNQ== -druid.properties.config.decrypt=true -druid.properties.config.decrypt.key=MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAL+lEjpcEjlpZt5aFXNu6dE6RcJ6t+pkGtGBBCite11vnYuBXkPZj6dliEziQrnGkWHR23tROm5gVN6OVrGYgOECAwEAAQ== - -mongodb.uri=mongodb://ofcser_ofc_rw:swhqEGie0fw3ivgbz[ba@172.16.8.3:27017,172.16.8.3:27018,172.16.8.3:27019/ofc - -## batch -hdfs.server=hdfs://172.16.16.51:8020 -hdfs.rootPath=/flink-batch - -## 保存2018年数据 -history.initialSize=3 -history.minIdle=3 -history.maxActive=20 -history.maxWait=60000 -history.timeBetweenEvictionRunsMillis=60000 -history.minEvictableIdleTimeMillis=300000 -history.validationQuery=SELECT 'x' -history.testWhileIdle=true -history.testOnBorrow=false -history.testOnReturn=false -history.filters=config -history.driverClass=com.mysql.jdbc.Driver -history.url=jdbc:mysql://192.168.0.253:3308/erp?useUnicode=true&characterEncoding=UTF-8&useOldAliasMetadataBehavior=true&zeroDateTimeBehavior=convertToNull -history.username=ops -history.password=gm5SUL+B3fMNCEO9uICnz8bs5hfZ3Gs+teSWEXfbPqR/n6OWbiPEVdfDBQWgZ7fj/4s5Q+Fs+VzkdjuOZ2nYW/riU7i9G2wtuzk5DmQqx5NJ4j0eEiVv1/8vrDMmTP2nz1C+yeSaQtfoihL1KD5ccaeR8y/P1SL37twRfi7QAx8= -history.properties.config.decrypt=true -history.properties.config.decrypt.key=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCYAUx8IzC60NZfpgjblDIft0gnZCsg6tNdu3K3vL8PeT6ruD++YMlGr/+IfmskLPH6Cexof/IlFfq3hSAGIu5mX3qihbP3bCt/aGyhDQtZ3EQfdHhsYdMlbx5b8FGca00aq0wagYuQ1ZfNMc8BnWMFfPGBnzpcn2cS+sM681+TxwIDAQAB diff --git a/flink/src/main/resources/test33/config.properties b/flink/src/main/resources/test33/config.properties deleted file mode 100644 index e9d73424..00000000 --- a/flink/src/main/resources/test33/config.properties +++ /dev/null @@ -1,28 +0,0 @@ -#kafka.bootstrap.servers=127.0.0.1:9092 -#kafka.zookeeper.connect=127.0.0.1:32768 -kafka.group.id=ofc-batch-online -kafka.bootstrap.servers=172.16.16.125:9092,172.16.16.245:9092, 172.16.16.252:9092 -kafka.zookeeper.connect=172.16.17.53:2181 - -druid.initialSize=3 -druid.minIdle=3 -druid.maxActive=20 -druid.maxWait=60000 -druid.timeBetweenEvictionRunsMillis=60000 -druid.minEvictableIdleTimeMillis=300000 -druid.validationQuery=SELECT 'x' -druid.testWhileIdle=true -druid.testOnBorrow=false -druid.testOnReturn=false -druid.filters=config -druid.driverClass=com.mysql.jdbc.Driver -druid.url=jdbc:mysql://192.168.0.253:3308/ofc?useSSL=false&useUnicode=true&characterEncoding=utf8&ApplicationName=ofc-service -druid.username=ops -druid.password=vA/pz/hp/GWy2OI1dE9yH9rh1tk60XlmlXZePPg/jeOzRRfhGc2H+XnRjNGmQkBLGsQAbIPtffXPvc2/v02tNQ== -druid.properties.config.decrypt=true -druid.properties.config.decrypt.key=MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAL+lEjpcEjlpZt5aFXNu6dE6RcJ6t+pkGtGBBCite11vnYuBXkPZj6dliEziQrnGkWHR23tROm5gVN6OVrGYgOECAwEAAQ== - -mongodb.uri=mongodb://ofcser_ofc_rw:swhqEGie0fw3ivgbz[ba@172.16.8.3:27017,172.16.8.3:27018,172.16.8.3:27019/ofc - -hdfs.server=hdfs://172.16.16.80:8020 -hdfs.rootPath=/flink-batch/ diff --git a/flink/src/main/resources/test36/config.properties b/flink/src/main/resources/test36/config.properties deleted file mode 100644 index af04c5e1..00000000 --- a/flink/src/main/resources/test36/config.properties +++ /dev/null @@ -1,28 +0,0 @@ -#kafka.bootstrap.servers=127.0.0.1:9092 -#kafka.zookeeper.connect=127.0.0.1:32768 -kafka.group.id=ofc-batch-online -kafka.bootstrap.servers=172.16.17.214:9092 -kafka.zookeeper.connect=172.16.17.218:2181 - -druid.initialSize=3 -druid.minIdle=3 -druid.maxActive=20 -druid.maxWait=60000 -druid.timeBetweenEvictionRunsMillis=60000 -druid.minEvictableIdleTimeMillis=300000 -druid.validationQuery=SELECT 'x' -druid.testWhileIdle=true -druid.testOnBorrow=false -druid.testOnReturn=false -druid.filters=config -druid.driverClass=com.mysql.jdbc.Driver -druid.url=jdbc:mysql://192.168.0.253:3308/ofc?useSSL=false&useUnicode=true&characterEncoding=utf8&ApplicationName=ofc-service -druid.username=ops -druid.password=vA/pz/hp/GWy2OI1dE9yH9rh1tk60XlmlXZePPg/jeOzRRfhGc2H+XnRjNGmQkBLGsQAbIPtffXPvc2/v02tNQ== -druid.properties.config.decrypt=true -druid.properties.config.decrypt.key=MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAL+lEjpcEjlpZt5aFXNu6dE6RcJ6t+pkGtGBBCite11vnYuBXkPZj6dliEziQrnGkWHR23tROm5gVN6OVrGYgOECAwEAAQ== - -mongodb.uri=mongodb://ofcser_ofc_rw:swhqEGie0fw3ivgbz[ba@172.16.8.3:27017,172.16.8.3:27018,172.16.8.3:27019/ofc - -hdfs.server=hdfs://172.16.16.80:8020 -hdfs.rootPath=/flink-batch/ diff --git a/flink/src/main/resources/test37/config.properties b/flink/src/main/resources/test37/config.properties deleted file mode 100644 index 620fba22..00000000 --- a/flink/src/main/resources/test37/config.properties +++ /dev/null @@ -1,37 +0,0 @@ -kafka.bootstrap.servers=172.16.16.106:9092,172.16.16.107:9092,172.16.18.98:9092 -kafka.zookeeper.connect=172.16.18.102:2181 - -kafka.group.id=stream-test -# \uFFFD\uFFFD\u02BC\uFFFD\uFFFD\u02B1\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u04F5\u0138\uFFFD\uFFFD\uFFFD -druid.initialSize=3 -# \uFFFD\uFFFD\u0421\uFFFD\uFFFD\uFFFD\u04F3\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD -druid.minIdle=3 -# \uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u04F3\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD -druid.maxActive=20 -# \uFFFD\uFFFD\u0221\uFFFD\uFFFD\uFFFD\uFFFD\u02B1\uFFFD\uFFFD\uFFFD\u0234\uFFFD\u02B1\uFFFD\u48EC\uFFFD\uFFFD\u03BB\uFFFD\uFFFD\uFFFD\uFFFD -druid.maxWait=60000 -# Destroy\uFFFD\u07F3\u033C\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u04F5\u013C\uFFFD\uFFFD\u02B1\uFFFD\uFFFD -druid.timeBetweenEvictionRunsMillis=60000 -# \uFFFD\uFFFD\uFFFD\u04F1\uFFFD\uFFFD\u05BF\uFFFD\uFFFD\u0436\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uECE4\u02B1\uFFFD\uFFFD -druid.minEvictableIdleTimeMillis=300000 -# \uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u01F7\uFFFD\uFFFD\uFFFD\u0427\uFFFD\uFFFDsql -druid.validationQuery=SELECT 'x' -# \u05B4\uFFFD\uFFFDvalidationQuery\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u01F7\uFFFD\uFFFD\uFFFD\u0427 -druid.testWhileIdle=true -# \uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u02B1\u05B4\uFFFD\uFFFDvalidationQuery\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u01F7\uFFFD\uFFFD\uFFFD\u0427 -druid.testOnBorrow=false -# \uFFFD\u9EF9\uFFFD\uFFFD\uFFFD\uFFFD\u02B1\u05B4\uFFFD\uFFFDvalidationQuery\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u01F7\uFFFD\uFFFD\uFFFD\u0427 -druid.testOnReturn=false -# \uFFFD\uFFFD\u02BEDruid\uFFFD\uFFFD\uFFFD\uFFFD\u0534\uFFFD\uFFFD\u04AA\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u077F\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u043D\uFFFD\uFFFD\uFFFD -druid.filters=config -druid.driverClass=com.mysql.jdbc.Driver -druid.url=jdbc:mysql://192.168.0.253:3308/ofc?useSSL=false&useUnicode=true&characterEncoding=utf8&ApplicationName=ofc-service -druid.username=ops -# \uFFFD\uFFFD\uFFFD\u073A\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD -druid.password=vA/pz/hp/GWy2OI1dE9yH9rh1tk60XlmlXZePPg/jeOzRRfhGc2H+XnRjNGmQkBLGsQAbIPtffXPvc2/v02tNQ== -druid.properties.config.decrypt=true -druid.properties.config.decrypt.key=MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAL+lEjpcEjlpZt5aFXNu6dE6RcJ6t+pkGtGBBCite11vnYuBXkPZj6dliEziQrnGkWHR23tROm5gVN6OVrGYgOECAwEAAQ== -mongodb.uri=mongodb://ofcser_ofc_rw:swhqEGie0fw3ivgbz[ba@172.16.8.3:27017,172.16.8.3:27018,172.16.8.3:27019/ofc - -hdfs.server=hdfs://172.16.16.80:8020 -hdfs.rootPath=/flink-batch/ diff --git a/flink/upload.sh b/flink/upload.sh new file mode 100755 index 00000000..0f641516 --- /dev/null +++ b/flink/upload.sh @@ -0,0 +1,7 @@ +file=$(find . -iname "*.jar*") +echo $file + +host=http://127.0.0.1:8081 + +curl -X POST -H "Expect:" -F "jarfile=@$file" $host/jars/upload + From a03a4b7da478e24fea2d7d9851385f589886a696 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Mon, 17 Jun 2019 01:31:42 +0800 Subject: [PATCH 056/476] *) update license --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index ca9bda13..9578352c 100755 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2018 kuangcp +Copyright (c) 2019 kuangcp Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From 2680b035a604501e822ece400e20d0deb3b5fe4b Mon Sep 17 00:00:00 2001 From: kcp Date: Tue, 18 Jun 2019 14:10:59 +0800 Subject: [PATCH 057/476] +) run batch by post 1. remove util package --- flink/run.sh | 6 ++++++ .../kuangcp/hi/{util => }/SimpleSink.java | 12 +++-------- .../SourceProvider.java => SimpleSource.java} | 10 ++++----- .../github/kuangcp/hi/SimpleStatistic.java | 21 ++++++++++++------- 4 files changed, 28 insertions(+), 21 deletions(-) create mode 100644 flink/run.sh rename flink/src/main/java/com/github/kuangcp/hi/{util => }/SimpleSink.java (78%) rename flink/src/main/java/com/github/kuangcp/hi/{util/SourceProvider.java => SimpleSource.java} (78%) diff --git a/flink/run.sh b/flink/run.sh new file mode 100644 index 00000000..a248e97f --- /dev/null +++ b/flink/run.sh @@ -0,0 +1,6 @@ +jarId=$1 + +host=http://127.0.0.1:8081 + +curl -X POST $host/jars/$jarId/run\?entry-class\=com.github.kuangcp.hi.SimpleStatistic + diff --git a/flink/src/main/java/com/github/kuangcp/hi/util/SimpleSink.java b/flink/src/main/java/com/github/kuangcp/hi/SimpleSink.java similarity index 78% rename from flink/src/main/java/com/github/kuangcp/hi/util/SimpleSink.java rename to flink/src/main/java/com/github/kuangcp/hi/SimpleSink.java index 9f1447a1..f9520c32 100644 --- a/flink/src/main/java/com/github/kuangcp/hi/util/SimpleSink.java +++ b/flink/src/main/java/com/github/kuangcp/hi/SimpleSink.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.hi.util; +package com.github.kuangcp.hi; import java.io.IOException; import java.util.LinkedList; @@ -23,7 +23,7 @@ public class SimpleSink extends RichOutputFormat> { // 如果给属性加上 transient 修饰, 就会报错 因为无法同步数据 private List> resultList = new LinkedList<>(); - public SimpleSink(String name) { + SimpleSink(String name) { this.name = name; } @@ -43,13 +43,7 @@ public void writeRecord(Tuple2 record) throws IOException { @Override public void close() { try { - log.info("size={}", resultList.size()); - for (Tuple2 tuple : resultList) { - String name = tuple.f0; - Integer count = tuple.f1; - - System.out.println("FinalResult " + name + " " + count); - } + resultList.forEach(t -> log.info("name={} count={}", t.f0, t.f1)); } catch (Exception e) { log.error(e.getMessage(), e); } diff --git a/flink/src/main/java/com/github/kuangcp/hi/util/SourceProvider.java b/flink/src/main/java/com/github/kuangcp/hi/SimpleSource.java similarity index 78% rename from flink/src/main/java/com/github/kuangcp/hi/util/SourceProvider.java rename to flink/src/main/java/com/github/kuangcp/hi/SimpleSource.java index 636be796..a45f6bce 100644 --- a/flink/src/main/java/com/github/kuangcp/hi/util/SourceProvider.java +++ b/flink/src/main/java/com/github/kuangcp/hi/SimpleSource.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.hi.util; +package com.github.kuangcp.hi; import java.time.Month; import java.util.List; @@ -11,7 +11,7 @@ * 2019-06-16 15:12 */ @Slf4j -public class SourceProvider { +class SimpleSource { private String id; @@ -20,15 +20,15 @@ public class SourceProvider { private int cursor; - public SourceProvider(String id) { + SimpleSource(String id) { this.id = id; } - public boolean hasNextPage() { + boolean hasNextPage() { return cursor < pageNum; } - public List generateResource() { + List generateResource() { cursor++; log.info("source: id={} cursor={}", id, cursor); return IntStream.rangeClosed(1, pageSize) diff --git a/flink/src/main/java/com/github/kuangcp/hi/SimpleStatistic.java b/flink/src/main/java/com/github/kuangcp/hi/SimpleStatistic.java index febfd0e3..757bd0a9 100644 --- a/flink/src/main/java/com/github/kuangcp/hi/SimpleStatistic.java +++ b/flink/src/main/java/com/github/kuangcp/hi/SimpleStatistic.java @@ -1,10 +1,9 @@ package com.github.kuangcp.hi; -import com.github.kuangcp.hi.util.SimpleSink; -import com.github.kuangcp.hi.util.SourceProvider; import java.util.List; import java.util.Objects; import lombok.extern.slf4j.Slf4j; +import org.apache.flink.api.common.JobExecutionResult; import org.apache.flink.api.common.functions.MapFunction; import org.apache.flink.api.java.DataSet; import org.apache.flink.api.java.ExecutionEnvironment; @@ -22,7 +21,7 @@ public class SimpleStatistic { private static final ExecutionEnvironment ENV; - private static final int taskNum = 5; + private static final int taskNum = 1; static { ENV = ExecutionEnvironment.getExecutionEnvironment(); @@ -36,8 +35,16 @@ public static void main(String[] args) throws Exception { } catch (Throwable e) { log.error(e.getMessage(), e); } - // 必须是方法的最后一行 否则 Flink 的执行计划无法生成 ENV.execute("PartsSpuStatistic"); + + JobExecutionResult lastJobExecutionResult = ENV.getLastJobExecutionResult(); + lastJobExecutionResult.getAllAccumulatorResults() + .forEach((k, v) -> log.error("failed : k={} {}", k, v)); + if (lastJobExecutionResult.getAllAccumulatorResults().isEmpty()) { + log.info("run success"); + } else { + log.error("run failed"); + } } /** @@ -46,19 +53,19 @@ public static void main(String[] args) throws Exception { private static void calculateAndAggregate(String batchId) { AggregateOperator> result = null; - SourceProvider provider = new SourceProvider(batchId); + SimpleSource provider = new SimpleSource(batchId); while (provider.hasNextPage()) { List data = provider.generateResource(); DataSource source = ENV.fromCollection(data); - // 合并窗口数据 + // 合并窗口内数据 DataSet> counts = source .filter(Objects::nonNull) .map(new Mapper()) .groupBy(0) .sum(1); - // 当前窗口和历史数据合并 + // 当前窗口内和历史数据合并 if (Objects.isNull(result)) { result = counts.groupBy(0).sum(1); } else { From 53b14e50d98e7836102385accdd35862f23c7cbf Mon Sep 17 00:00:00 2001 From: kcp Date: Thu, 20 Jun 2019 21:04:44 +0800 Subject: [PATCH 058/476] *) fixed package with jar --- .../java/syntax/integer/IntegerCacheTest.java | 21 +++++++++++++++++++ flink/build.gradle | 11 ++++++++-- .../com/github/kuangcp/hi/SimpleSink.java | 2 +- .../github/kuangcp/hi/SimpleStatistic.java | 4 +--- .../stream/list/HandleExceptionTest.java | 15 ++----------- .../com/github/kuangcp/time/CalendarTest.java | 13 +----------- .../com/github/kuangcp/time/InstantTest.java | 2 +- .../kuangcp/time/LocalDateTimeTest.java | 4 ++-- 8 files changed, 38 insertions(+), 34 deletions(-) diff --git a/class/src/test/java/syntax/integer/IntegerCacheTest.java b/class/src/test/java/syntax/integer/IntegerCacheTest.java index 52ad8c67..4a27a93e 100644 --- a/class/src/test/java/syntax/integer/IntegerCacheTest.java +++ b/class/src/test/java/syntax/integer/IntegerCacheTest.java @@ -1,5 +1,7 @@ package syntax.integer; +import java.util.Objects; +import java.util.concurrent.ThreadLocalRandom; import org.junit.Test; /** @@ -26,4 +28,23 @@ public void testInappropriateWithBox() { } System.out.println(sum); } + + // 自动拆装箱 NPE问题 + @Test(expected = NullPointerException.class) + public void testBoxWithNPE() { + Integer num = null; + if (num == 1) { + System.out.println(); + } + } + + @Test + public void testAvoidBoxWithNPE() { + Integer num = ThreadLocalRandom.current().nextInt(100) > 50 ? null : 1; + if (Objects.equals(num, 1)) { + System.out.println("less"); + } else { + System.out.println("more"); + } + } } diff --git a/flink/build.gradle b/flink/build.gradle index 05b9fe60..e93f9755 100644 --- a/flink/build.gradle +++ b/flink/build.gradle @@ -8,13 +8,20 @@ dependencies { implementation libs['flink-clients'] } +application { + mainClassName = "com.github.kuangcp.hi.SimpleStatistic" +} + + task uberJar(type: Jar) { archiveClassifier = 'all-dependency' from sourceSets.main.output - dependsOn configurations.runtimeClasspath from { - configurations.runtimeClasspath.findAll {it.name.endsWith('jar')}.collect {zipTree(it)} + configurations.runtimeClasspath.findAll { it.name.endsWith('jar') }.collect { zipTree(it) } + } + manifest { + attributes 'Main-Class': 'com.github.kuangcp.hi.SimpleStatistic' } } diff --git a/flink/src/main/java/com/github/kuangcp/hi/SimpleSink.java b/flink/src/main/java/com/github/kuangcp/hi/SimpleSink.java index f9520c32..d328ebc7 100644 --- a/flink/src/main/java/com/github/kuangcp/hi/SimpleSink.java +++ b/flink/src/main/java/com/github/kuangcp/hi/SimpleSink.java @@ -10,7 +10,7 @@ import org.apache.flink.configuration.Configuration; /** - * @author kuangchengping@qipeipu.com + * @author https://github.com/kuangcp * @since 2019-05-30 20:36 */ @lombok.Data diff --git a/flink/src/main/java/com/github/kuangcp/hi/SimpleStatistic.java b/flink/src/main/java/com/github/kuangcp/hi/SimpleStatistic.java index 757bd0a9..4775dfa8 100644 --- a/flink/src/main/java/com/github/kuangcp/hi/SimpleStatistic.java +++ b/flink/src/main/java/com/github/kuangcp/hi/SimpleStatistic.java @@ -12,9 +12,7 @@ import org.apache.flink.api.java.tuple.Tuple2; /** - * 将数据格式化并输出到HDFS - * - * @author kuangchengping@qipeipu.com + * @author https://github.com/kuangcp * @since 2019-05-16 16:26 */ @Slf4j diff --git a/java8/src/test/java/com/github/kuangcp/stream/list/HandleExceptionTest.java b/java8/src/test/java/com/github/kuangcp/stream/list/HandleExceptionTest.java index fd169fa4..a01914e1 100644 --- a/java8/src/test/java/com/github/kuangcp/stream/list/HandleExceptionTest.java +++ b/java8/src/test/java/com/github/kuangcp/stream/list/HandleExceptionTest.java @@ -1,8 +1,6 @@ package com.github.kuangcp.stream.list; -import java.util.ArrayList; -import java.util.List; -import org.junit.Before; +import java.util.stream.IntStream; import org.junit.Test; /** @@ -10,19 +8,10 @@ */ public class HandleExceptionTest { - private List data = new ArrayList<>(); - - @Before - public void init() { - for (int i = 0; i < 10; i++) { - data.add(i); - } - } - // NPE in stream @Test(expected = NullPointerException.class) public void testException() { - data.forEach(s -> { + IntStream.rangeClosed(1, 10).forEach(s -> { System.out.println(s); if (s > 7) { throw new NullPointerException("Oops "); diff --git a/java8/src/test/java/com/github/kuangcp/time/CalendarTest.java b/java8/src/test/java/com/github/kuangcp/time/CalendarTest.java index 3ef27a4b..a9fbb1d4 100644 --- a/java8/src/test/java/com/github/kuangcp/time/CalendarTest.java +++ b/java8/src/test/java/com/github/kuangcp/time/CalendarTest.java @@ -5,8 +5,8 @@ import org.junit.Test; /** - * https://github.com/kuangcp * Calendar 对象 + * * @author kuangcp on 18-8-4-下午10:16 */ @Slf4j @@ -21,17 +21,6 @@ public void testInit() { cal.set(Calendar.MINUTE, 0); cal.set(Calendar.MILLISECOND, 0); -// java.util.GregorianCalendar[time=?, -// areFieldsSet=false,areAllFieldsSet=true, -// lenient=true,zone=sun.util.calendar.ZoneInfo[id="Asia/Shanghai", -// offset=28800000,dstSavings=0,useDaylight=false,transitions=19, -// lastRule=null], -// firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1, -// YEAR=2018,MONTH=7,WEEK_OF_YEAR=31,WEEK_OF_MONTH=1,DAY_OF_MONTH=-16, -// DAY_OF_YEAR=216,DAY_OF_WEEK=7,DAY_OF_WEEK_IN_MONTH=1,AM_PM=1, -// HOUR=10,HOUR_OF_DAY=0,MINUTE=0,SECOND=0,MILLISECOND=0, -// ZONE_OFFSET=28800000,DST_OFFSET=0] - log.debug("date: calendar={}", cal); } } diff --git a/java8/src/test/java/com/github/kuangcp/time/InstantTest.java b/java8/src/test/java/com/github/kuangcp/time/InstantTest.java index 7df3795b..56ed704a 100644 --- a/java8/src/test/java/com/github/kuangcp/time/InstantTest.java +++ b/java8/src/test/java/com/github/kuangcp/time/InstantTest.java @@ -5,7 +5,7 @@ import org.junit.Test; /** - * https://github.com/kuangcp + * 用于表示一个瞬时 * * @author kuangcp on 18-8-4-下午11:40 */ diff --git a/java8/src/test/java/com/github/kuangcp/time/LocalDateTimeTest.java b/java8/src/test/java/com/github/kuangcp/time/LocalDateTimeTest.java index e928912a..b094611c 100644 --- a/java8/src/test/java/com/github/kuangcp/time/LocalDateTimeTest.java +++ b/java8/src/test/java/com/github/kuangcp/time/LocalDateTimeTest.java @@ -6,14 +6,13 @@ /** * Created by https://github.com/kuangcp - * Java8 中新类 LocalDateTime + * Java8 时间类 LocalDateTime * * @author kuangcp */ @Slf4j public class LocalDateTimeTest { - @Test public void testGet() { LocalDateTime dateTime = LocalDateTime.now(); @@ -23,4 +22,5 @@ public void testGet() { log.debug("{} {}", dateTime.getDayOfWeek(), dateTime.getDayOfYear()); } + } From 089f487e8087889a0c41901b8c7a36419aceb0df Mon Sep 17 00:00:00 2001 From: kcp Date: Fri, 21 Jun 2019 18:13:53 +0800 Subject: [PATCH 059/476] +) create shell script --- dependency.gradle | 1 + flink/build.gradle | 2 +- flink/host.conf | 2 ++ flink/run.sh | 3 +-- .../com/github/kuangcp/hi/SimpleSink.java | 2 +- .../github/kuangcp/hi/SimpleStatistic.java | 4 ++++ .../github/kuangcp/hi/SimpleWaterMarker.java | 23 +++++++++++++++++++ flink/upload.sh | 3 +-- flink/uploadThenRun.sh | 8 +++++++ 9 files changed, 42 insertions(+), 6 deletions(-) create mode 100644 flink/host.conf mode change 100644 => 100755 flink/run.sh create mode 100644 flink/src/main/java/com/github/kuangcp/hi/SimpleWaterMarker.java create mode 100755 flink/uploadThenRun.sh diff --git a/dependency.gradle b/dependency.gradle index 1e08021a..1e784db2 100644 --- a/dependency.gradle +++ b/dependency.gradle @@ -81,6 +81,7 @@ ext { // flink , "flink-java" : "org.apache.flink:flink-java:$ver.flink" , "flink-clients" : "org.apache.flink:flink-clients_2.12:$ver.flink" + , "flink-streaming-java" : "org.apache.flink:flink-streaming-java_2.12:$ver.flink" // hadoop , "hadoop-common" : "org.apache.hadoop:hadoop-common:$ver.hadoop" diff --git a/flink/build.gradle b/flink/build.gradle index e93f9755..b09cd8ac 100644 --- a/flink/build.gradle +++ b/flink/build.gradle @@ -6,13 +6,13 @@ apply plugin: 'application' dependencies { implementation libs['flink-java'] implementation libs['flink-clients'] + implementation libs['flink-streaming-java'] } application { mainClassName = "com.github.kuangcp.hi.SimpleStatistic" } - task uberJar(type: Jar) { archiveClassifier = 'all-dependency' diff --git a/flink/host.conf b/flink/host.conf new file mode 100644 index 00000000..b80c9610 --- /dev/null +++ b/flink/host.conf @@ -0,0 +1,2 @@ +host=http://127.0.0.1:8081 +# host=http://127.0.0.1:8081 diff --git a/flink/run.sh b/flink/run.sh old mode 100644 new mode 100755 index a248e97f..369101ea --- a/flink/run.sh +++ b/flink/run.sh @@ -1,6 +1,5 @@ +. ./host.conf jarId=$1 -host=http://127.0.0.1:8081 - curl -X POST $host/jars/$jarId/run\?entry-class\=com.github.kuangcp.hi.SimpleStatistic diff --git a/flink/src/main/java/com/github/kuangcp/hi/SimpleSink.java b/flink/src/main/java/com/github/kuangcp/hi/SimpleSink.java index d328ebc7..1cd7d880 100644 --- a/flink/src/main/java/com/github/kuangcp/hi/SimpleSink.java +++ b/flink/src/main/java/com/github/kuangcp/hi/SimpleSink.java @@ -43,7 +43,7 @@ public void writeRecord(Tuple2 record) throws IOException { @Override public void close() { try { - resultList.forEach(t -> log.info("name={} count={}", t.f0, t.f1)); + resultList.forEach(t -> log.info("close by sink: name={} count={}", t.f0, t.f1)); } catch (Exception e) { log.error(e.getMessage(), e); } diff --git a/flink/src/main/java/com/github/kuangcp/hi/SimpleStatistic.java b/flink/src/main/java/com/github/kuangcp/hi/SimpleStatistic.java index 4775dfa8..fcc212db 100644 --- a/flink/src/main/java/com/github/kuangcp/hi/SimpleStatistic.java +++ b/flink/src/main/java/com/github/kuangcp/hi/SimpleStatistic.java @@ -26,6 +26,7 @@ public class SimpleStatistic { } public static void main(String[] args) throws Exception { + log.info("start calculate"); try { for (int i = 0; i < taskNum; i++) { calculateAndAggregate("batch-" + i); @@ -33,6 +34,8 @@ public static void main(String[] args) throws Exception { } catch (Throwable e) { log.error(e.getMessage(), e); } + + ENV.getConfig().setAutoWatermarkInterval(1000); ENV.execute("PartsSpuStatistic"); JobExecutionResult lastJobExecutionResult = ENV.getLastJobExecutionResult(); @@ -70,6 +73,7 @@ private static void calculateAndAggregate(String batchId) { DataSet> temp = result.union(counts); result = temp.groupBy(0).sum(1); } + log.info("aggregate once"); } if (Objects.isNull(result)) { diff --git a/flink/src/main/java/com/github/kuangcp/hi/SimpleWaterMarker.java b/flink/src/main/java/com/github/kuangcp/hi/SimpleWaterMarker.java new file mode 100644 index 00000000..0bb44adb --- /dev/null +++ b/flink/src/main/java/com/github/kuangcp/hi/SimpleWaterMarker.java @@ -0,0 +1,23 @@ +package com.github.kuangcp.hi; + +import javax.annotation.Nullable; +import org.apache.flink.streaming.api.functions.AssignerWithPeriodicWatermarks; +import org.apache.flink.streaming.api.watermark.Watermark; + +/** + * @author https://github.com/kuangcp + * @date 2019-06-21 11:47 + */ +public class SimpleWaterMarker implements AssignerWithPeriodicWatermarks { + + @Nullable + @Override + public Watermark getCurrentWatermark() { + return null; + } + + @Override + public long extractTimestamp(Object element, long previousElementTimestamp) { + return 0; + } +} diff --git a/flink/upload.sh b/flink/upload.sh index 0f641516..4a320dd3 100755 --- a/flink/upload.sh +++ b/flink/upload.sh @@ -1,7 +1,6 @@ +. ./host.conf file=$(find . -iname "*.jar*") echo $file -host=http://127.0.0.1:8081 - curl -X POST -H "Expect:" -F "jarfile=@$file" $host/jars/upload diff --git a/flink/uploadThenRun.sh b/flink/uploadThenRun.sh new file mode 100755 index 00000000..347d1bbc --- /dev/null +++ b/flink/uploadThenRun.sh @@ -0,0 +1,8 @@ +result=$(./upload.sh) + +temp=${result#*flink-web-upload/} +jarId=${temp%%\"*} + +echo "start run: " $jarId +./run.sh $jarId + From 15a3c7dd40c4b0c77f3be47f80f2e719db773cb4 Mon Sep 17 00:00:00 2001 From: kcp Date: Sat, 22 Jun 2019 17:58:35 +0800 Subject: [PATCH 060/476] *) attempt to exit --- .gitignore | 1 + flink/src/main/java/com/github/kuangcp/hi/SimpleStatistic.java | 3 +++ 2 files changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index 4a0ddc46..003ebd9b 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ target/ .idea/ *.iml out/ +*.ipr # eclipse # bin/ diff --git a/flink/src/main/java/com/github/kuangcp/hi/SimpleStatistic.java b/flink/src/main/java/com/github/kuangcp/hi/SimpleStatistic.java index fcc212db..41dd02fb 100644 --- a/flink/src/main/java/com/github/kuangcp/hi/SimpleStatistic.java +++ b/flink/src/main/java/com/github/kuangcp/hi/SimpleStatistic.java @@ -2,6 +2,7 @@ import java.util.List; import java.util.Objects; + import lombok.extern.slf4j.Slf4j; import org.apache.flink.api.common.JobExecutionResult; import org.apache.flink.api.common.functions.MapFunction; @@ -46,6 +47,8 @@ public static void main(String[] args) throws Exception { } else { log.error("run failed"); } + + System.exit(1); } /** From 5f3e273155b6d980f8cd143a4ad415b00731d655 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Sun, 23 Jun 2019 16:16:07 +0800 Subject: [PATCH 061/476] +) add java 11 module --- README.md | 2 +- .../InstantiationAndConstructor.java | 17 ++++- .../InstantiationAndConstructorTest.java | 20 +++-- flink/{uploadThenRun.sh => bootstrap.sh} | 0 .../com/github/kuangcp/hi/SimpleSink.java | 2 + .../com/github/kuangcp/hi/SimpleSource.java | 2 + .../github/kuangcp/hi/SimpleStatistic.java | 3 +- .../kuangcp/hi/PartsSpuStatisticTest.java | 75 ------------------- java11/build.gradle | 2 + .../com/github/kuangcp/string/StringTest.java | 41 ++++++++++ settings.gradle | 1 + 11 files changed, 76 insertions(+), 89 deletions(-) rename flink/{uploadThenRun.sh => bootstrap.sh} (100%) delete mode 100644 flink/src/test/java/com/github/kuangcp/hi/PartsSpuStatisticTest.java create mode 100644 java11/build.gradle create mode 100644 java11/src/test/java/com/github/kuangcp/string/StringTest.java diff --git a/README.md b/README.md index f0a116a1..cc4b3cdc 100644 --- a/README.md +++ b/README.md @@ -30,4 +30,4 @@ > [tutorials](https://github.com/eugenp/tutorials) `baeldung` > [Java 学习笔记](https://github.com/brianway/java-learning) > [demo](https://gitee.com/code4everything/demo) - +> [sparrow](https://github.com/david1228/sparrow) diff --git a/class/src/main/java/com/github/kuangcp/instantiation/InstantiationAndConstructor.java b/class/src/main/java/com/github/kuangcp/instantiation/InstantiationAndConstructor.java index 053f1646..a00136b8 100644 --- a/class/src/main/java/com/github/kuangcp/instantiation/InstantiationAndConstructor.java +++ b/class/src/main/java/com/github/kuangcp/instantiation/InstantiationAndConstructor.java @@ -5,6 +5,8 @@ import lombok.extern.slf4j.Slf4j; /** + * 测试 实例化对象和构造器之间的关系 + * * @author kuangcp on 3/9/19-5:46 PM */ @Data @@ -13,18 +15,25 @@ class InstantiationAndConstructor implements Serializable, Cloneable { private String name; + static { + log.info("invoke static init block"); + } + + { + log.info("invoke init block"); + } + public InstantiationAndConstructor() { log.info("invoke empty constructor"); } public InstantiationAndConstructor(String name) { this.name = name; - log.info("invoke constructor: name={}", name); + log.info("invoke constructor(name): name={}", name); } @Override - protected Object clone() throws CloneNotSupportedException { - return super.clone(); + protected InstantiationAndConstructor clone() throws CloneNotSupportedException { + return (InstantiationAndConstructor) super.clone(); } - } diff --git a/class/src/test/java/com/github/kuangcp/instantiation/InstantiationAndConstructorTest.java b/class/src/test/java/com/github/kuangcp/instantiation/InstantiationAndConstructorTest.java index d99f6ff4..c3cf1882 100644 --- a/class/src/test/java/com/github/kuangcp/instantiation/InstantiationAndConstructorTest.java +++ b/class/src/test/java/com/github/kuangcp/instantiation/InstantiationAndConstructorTest.java @@ -42,33 +42,39 @@ public void testInitByReflect() throws ReflectiveOperationException { String name = "get constructor by reflect"; InstantiationAndConstructor domain = constructor.newInstance(name); - assertThat(domain.getName(), equalTo(name)); - Method d = InstantiationAndConstructor.class.getMethod("d"); - d.invoke(new Object()); + Method method = InstantiationAndConstructor.class.getMethod("setName", String.class); + name = "by method"; + method.invoke(domain, name); + assertThat(domain.getName(), equalTo(name)); } @Test public void testInitByClone() throws CloneNotSupportedException { - InstantiationAndConstructor target = new InstantiationAndConstructor(); + String name = "clone"; + InstantiationAndConstructor target = new InstantiationAndConstructor(name); - Object clone = target.clone(); + log.info("start clone"); + InstantiationAndConstructor clone = target.clone(); assertThat(target, equalTo(clone)); assertThat(clone == target, equalTo(false)); + assertThat(clone.getName(), equalTo("clone")); } @Test public void testInitByDeserialize() throws IOException, ClassNotFoundException { InstantiationAndConstructor origin = new InstantiationAndConstructor("name"); - // 输出 + // 序列化 + log.info("start serialize"); ByteArrayOutputStream byteOutput = new ByteArrayOutputStream(); ObjectOutputStream output = new ObjectOutputStream(byteOutput); output.writeObject(origin); - // 输入 + // 反序列化 + log.info("start deserialize"); ByteArrayInputStream byteInput = new ByteArrayInputStream(byteOutput.toByteArray()); ObjectInputStream input = new ObjectInputStream(byteInput); diff --git a/flink/uploadThenRun.sh b/flink/bootstrap.sh similarity index 100% rename from flink/uploadThenRun.sh rename to flink/bootstrap.sh diff --git a/flink/src/main/java/com/github/kuangcp/hi/SimpleSink.java b/flink/src/main/java/com/github/kuangcp/hi/SimpleSink.java index 1cd7d880..58f3970a 100644 --- a/flink/src/main/java/com/github/kuangcp/hi/SimpleSink.java +++ b/flink/src/main/java/com/github/kuangcp/hi/SimpleSink.java @@ -10,6 +10,8 @@ import org.apache.flink.configuration.Configuration; /** + * 模拟输出到文件 + * * @author https://github.com/kuangcp * @since 2019-05-30 20:36 */ diff --git a/flink/src/main/java/com/github/kuangcp/hi/SimpleSource.java b/flink/src/main/java/com/github/kuangcp/hi/SimpleSource.java index a45f6bce..60909277 100644 --- a/flink/src/main/java/com/github/kuangcp/hi/SimpleSource.java +++ b/flink/src/main/java/com/github/kuangcp/hi/SimpleSource.java @@ -7,6 +7,8 @@ import lombok.extern.slf4j.Slf4j; /** + * 模拟数据库分页查询 + * * @author https://github.com/kuangcp * 2019-06-16 15:12 */ diff --git a/flink/src/main/java/com/github/kuangcp/hi/SimpleStatistic.java b/flink/src/main/java/com/github/kuangcp/hi/SimpleStatistic.java index 41dd02fb..d58ff61c 100644 --- a/flink/src/main/java/com/github/kuangcp/hi/SimpleStatistic.java +++ b/flink/src/main/java/com/github/kuangcp/hi/SimpleStatistic.java @@ -37,6 +37,7 @@ public static void main(String[] args) throws Exception { } ENV.getConfig().setAutoWatermarkInterval(1000); + ENV.execute("PartsSpuStatistic"); JobExecutionResult lastJobExecutionResult = ENV.getLastJobExecutionResult(); @@ -47,8 +48,6 @@ public static void main(String[] args) throws Exception { } else { log.error("run failed"); } - - System.exit(1); } /** diff --git a/flink/src/test/java/com/github/kuangcp/hi/PartsSpuStatisticTest.java b/flink/src/test/java/com/github/kuangcp/hi/PartsSpuStatisticTest.java deleted file mode 100644 index e1df0609..00000000 --- a/flink/src/test/java/com/github/kuangcp/hi/PartsSpuStatisticTest.java +++ /dev/null @@ -1,75 +0,0 @@ -//package com.github.kuangcp.hi; -// -//import com.github.kuangcp.hi.util.SplitJobCommand; -//import com.baturu.ofc.constant.statistic.ProductStatisticJobCommand; -//import com.baturu.ofc.constant.statistic.ProductStatisticSpan; -//import com.baturu.ofc.util.JsonMapper; -//import org.junit.Test; -//import org.springframework.http.HttpEntity; -//import org.springframework.http.ResponseEntity; -//import org.springframework.web.client.RestTemplate; -// -//import java.nio.charset.StandardCharsets; -//import java.time.LocalDateTime; -//import java.util.Base64; -//import java.util.HashMap; -//import java.util.HashSet; -// -//import static com.github.kuangcp.hi.PartsSpuStatistic.calculateOrder; -// -///** -// * @author kuangchengping@qipeipu.com -// * @since 2019-05-21 14:59 -// */ -//public class PartsSpuStatisticTest { -// -// @Test -// public void testCalculate() throws Exception { -// HashSet spans = new HashSet<>(); -// spans.add(ProductStatisticSpan.MONTH); -// -// ProductStatisticJobCommand msg = ProductStatisticJobCommand.builder() -// .productStatisticSpan(spans) -// .startTime(SplitJobCommand.toDate(LocalDateTime.now().withMonth(1))) -// .endTime(SplitJobCommand.toDate(LocalDateTime.now().withMonth(4))) -// .build(); -// -// calculateOrder(msg); -// } -// -// @Test -// public void testJson() { -// HashSet spans = new HashSet<>(); -// spans.add(ProductStatisticSpan.MONTH); -// -// ProductStatisticJobCommand msg = ProductStatisticJobCommand.builder() -// .id("12") -// .productStatisticSpan(spans) -// .startTime(SplitJobCommand.toDate(LocalDateTime.now().withMonth(1))) -// .endTime(SplitJobCommand.toDate(LocalDateTime.now().withMonth(3))) -// .build(); -// String json = JsonMapper.obj2String(msg); -// -// byte[] result = Base64.getEncoder().encode(json.getBytes()); -// System.out.println(new String(result, StandardCharsets.UTF_8)); -// -// byte[] origin = Base64.getDecoder().decode(result); -// System.out.println(new String(origin, StandardCharsets.UTF_8)); -// } -// -// @Test -// public void testRest() { -// String BASE_URL = "http://127.0.0.1:8081/jars/"; -// String JAR_NAME = "6b74c981-ed07-4ad3-97e0-5e47f570ca49_btr-ofc-batch-test32-jar-with-dependencies.jar"; -// -// String msg = "eyJpZCI6IjEyIiwicHJvZHVjdFN0YXRpc3RpY1NwYW4iOlsiTU9OVEgiXSwic3RhcnRUaW1lIjoxNTQ4OTE0MTY3NzcxLCJlbmRUaW1lIjoxNTU0MDExNzY3ODExfQ=="; -// String url = BASE_URL + JAR_NAME + "/run?entry-class=com.baturu.ofc.batch.PartsSpuStatistic&program-args=" + msg; -//// String url = BASE_URL + JAR_NAME + "/run?entry-class=com.baturu.ofc.batch.word.WordCount"; -// -// RestTemplate restTemplate = new RestTemplate(); -// HttpEntity httpEntity = new HttpEntity<>(new HashMap<>()); -// -// ResponseEntity request = restTemplate.postForEntity(url, httpEntity, String.class); -// System.out.println(request.getBody()); -// } -//} \ No newline at end of file diff --git a/java11/build.gradle b/java11/build.gradle new file mode 100644 index 00000000..13f473e4 --- /dev/null +++ b/java11/build.gradle @@ -0,0 +1,2 @@ +sourceCompatibility = 1.11 +targetCompatibility = 1.11 diff --git a/java11/src/test/java/com/github/kuangcp/string/StringTest.java b/java11/src/test/java/com/github/kuangcp/string/StringTest.java new file mode 100644 index 00000000..2ea7fcd3 --- /dev/null +++ b/java11/src/test/java/com/github/kuangcp/string/StringTest.java @@ -0,0 +1,41 @@ +package com.github.kuangcp.string; + +import static org.hamcrest.Matchers.equalTo; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + +import java.util.Objects; +import java.util.stream.Collectors; +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; + +/** + * @author https://github.com/Kuangcp on 2019-06-23 15:38 + */ +@Slf4j +public class StringTest { + + @Test + public void testSimple() { + assertTrue(" ".isBlank()); + + assertThat(" Java ".strip(), equalTo("Java")); + + assertThat(" Java ".stripTrailing(), equalTo(" Java")); + assertThat(" Java ".stripLeading(), equalTo("Java ")); + + assertThat("Java".repeat(3), equalTo("JavaJavaJava")); + + assertThat("A\nB\nC".lines().count(), equalTo(3L)); + } + + @Test + public void testStream() { + String result = "1\n222\n\t23\t".lines() + .filter(Objects::nonNull) + .filter(v -> v.length() == 1) + .collect(Collectors.joining(",")); + + log.info("result={}", result); + } +} diff --git a/settings.gradle b/settings.gradle index 1935f692..ae75afe6 100644 --- a/settings.gradle +++ b/settings.gradle @@ -10,6 +10,7 @@ include( , 'question' , 'pattern' , 'java8' + , 'java11' , 'concurrency' , 'test' , 'guava' From 12748615a307bd5ab51a39d5793db9a1cd3e0823 Mon Sep 17 00:00:00 2001 From: kcp Date: Fri, 5 Jul 2019 11:53:27 +0800 Subject: [PATCH 062/476] +) compare flatmap and forEach --- build.gradle | 2 +- dependency.gradle | 2 +- ...ToMapTest.java => CollectorToMapTest.java} | 4 +- .../github/kuangcp/stream/FlatMapTest.java | 41 +++++++++++++++++++ .../{list => }/HandleExceptionTest.java | 2 +- 5 files changed, 46 insertions(+), 5 deletions(-) rename java8/src/test/java/com/github/kuangcp/stream/{map/ToMapTest.java => CollectorToMapTest.java} (94%) create mode 100644 java8/src/test/java/com/github/kuangcp/stream/FlatMapTest.java rename java8/src/test/java/com/github/kuangcp/stream/{list => }/HandleExceptionTest.java (91%) diff --git a/build.gradle b/build.gradle index 323ce7c2..b48709d7 100644 --- a/build.gradle +++ b/build.gradle @@ -1,7 +1,7 @@ apply from: 'dependency.gradle' // parent and child project all use this config -allprojects { +subprojects { apply plugin: 'java' apply plugin: 'maven' diff --git a/dependency.gradle b/dependency.gradle index 1e784db2..aada9ff5 100644 --- a/dependency.gradle +++ b/dependency.gradle @@ -10,7 +10,7 @@ ext { , mail : '1.4.7' , jackson : '2.9.5' , hamcrest: '1.3' - , kcp_tool: '1.0.3' + , kcp_tool: '1.0.4' , netty : '4.1.35.Final' , guava : '27.1-jre' , jmh : '1.2.1' diff --git a/java8/src/test/java/com/github/kuangcp/stream/map/ToMapTest.java b/java8/src/test/java/com/github/kuangcp/stream/CollectorToMapTest.java similarity index 94% rename from java8/src/test/java/com/github/kuangcp/stream/map/ToMapTest.java rename to java8/src/test/java/com/github/kuangcp/stream/CollectorToMapTest.java index 00607add..006867d1 100644 --- a/java8/src/test/java/com/github/kuangcp/stream/map/ToMapTest.java +++ b/java8/src/test/java/com/github/kuangcp/stream/CollectorToMapTest.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.stream.map; +package com.github.kuangcp.stream; import java.util.Arrays; import java.util.List; @@ -14,7 +14,7 @@ * @date 2019-05-13 10:34 */ @Slf4j -public class ToMapTest { +public class CollectorToMapTest { @Data @Builder diff --git a/java8/src/test/java/com/github/kuangcp/stream/FlatMapTest.java b/java8/src/test/java/com/github/kuangcp/stream/FlatMapTest.java new file mode 100644 index 00000000..ce5514ca --- /dev/null +++ b/java8/src/test/java/com/github/kuangcp/stream/FlatMapTest.java @@ -0,0 +1,41 @@ +package com.github.kuangcp.stream; + +import com.github.kuangcp.time.GetRunTime; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import org.junit.Test; + +/** + * @author kuangchengping@qipeipu.com + * @since 2019-07-05 11:07 + */ +public class FlatMapTest { + + /** + * test flatmap + */ + @Test + public void testFlatMap() { + GetRunTime runTime = new GetRunTime(); + Map> map = new HashMap<>(); + for (int i = 0; i < 10000; i++) { + map.put(i + "", Arrays.asList("1", "2", "3", i + "")); + } + + runTime.startCount(); + List result = map.entrySet().stream() + .flatMap(v -> v.getValue().stream()) + .collect(Collectors.toList()); + runTime.endCountOneLine(); + + List results = new ArrayList<>(); + + runTime.startCount(); + map.forEach((k, v) -> results.addAll(v)); + runTime.endCountOneLine(); + } +} diff --git a/java8/src/test/java/com/github/kuangcp/stream/list/HandleExceptionTest.java b/java8/src/test/java/com/github/kuangcp/stream/HandleExceptionTest.java similarity index 91% rename from java8/src/test/java/com/github/kuangcp/stream/list/HandleExceptionTest.java rename to java8/src/test/java/com/github/kuangcp/stream/HandleExceptionTest.java index a01914e1..e5e9e3d1 100644 --- a/java8/src/test/java/com/github/kuangcp/stream/list/HandleExceptionTest.java +++ b/java8/src/test/java/com/github/kuangcp/stream/HandleExceptionTest.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.stream.list; +package com.github.kuangcp.stream; import java.util.stream.IntStream; import org.junit.Test; From ff7322921b40ae3730c7b261d83e9682300193f0 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Sat, 6 Jul 2019 23:17:49 +0800 Subject: [PATCH 063/476] *) format --- .../kuangcp/stream/CollectorToMapTest.java | 40 +++++++++++-------- .../github/kuangcp/stream/FlatMapTest.java | 7 ++++ .../kuangcp/stream/HandleExceptionTest.java | 13 +++--- 3 files changed, 37 insertions(+), 23 deletions(-) diff --git a/java8/src/test/java/com/github/kuangcp/stream/CollectorToMapTest.java b/java8/src/test/java/com/github/kuangcp/stream/CollectorToMapTest.java index 006867d1..ce0391f0 100644 --- a/java8/src/test/java/com/github/kuangcp/stream/CollectorToMapTest.java +++ b/java8/src/test/java/com/github/kuangcp/stream/CollectorToMapTest.java @@ -11,40 +11,46 @@ /** * @author https://github.com/kuangcp - * @date 2019-05-13 10:34 */ @Slf4j public class CollectorToMapTest { - @Data - @Builder - static class Phone { - - String name; - int price; - } - + /** + * 如果没有声明 重复key的策略, 就会直接抛出异常 + */ @SuppressWarnings("ResultOfMethodCallIgnored") @Test(expected = IllegalStateException.class) public void testDuplicated() { - List phones = getPhones(); + List phones = generatePhones(); phones.stream().collect(Collectors.toMap(Phone::getName, Phone::getPrice)); } + /** + * 发生重复后, 后来的值覆盖前面的值, 如果 (v1, v2) -> v1 则是忽略后来的值 + */ @Test public void testDuplicatedWithHandled() { - List phones = getPhones(); - - // 发生重复后, 后来的值覆盖前面的值, 如果 (v1, v2) -> v1 则是忽略后来的值 - Map result = phones.stream() + Map result = generatePhones().stream() .collect(Collectors.toMap(Phone::getName, Phone::getPrice, (v1, v2) -> v2)); + result.forEach((k, v) -> log.info("{} {}", k, v)); } - private List getPhones() { - return Arrays.asList(Phone.builder().name("a").price(1).build(), + private List generatePhones() { + return Arrays.asList( + Phone.builder().name("a").price(1).build(), Phone.builder().name("b").price(2).build(), - Phone.builder().name("a").price(3).build()); + Phone.builder().name("a").price(3).build() + ); } + + @Data + @Builder + static class Phone { + + String name; + int price; + } + } diff --git a/java8/src/test/java/com/github/kuangcp/stream/FlatMapTest.java b/java8/src/test/java/com/github/kuangcp/stream/FlatMapTest.java index ce5514ca..535887ee 100644 --- a/java8/src/test/java/com/github/kuangcp/stream/FlatMapTest.java +++ b/java8/src/test/java/com/github/kuangcp/stream/FlatMapTest.java @@ -38,4 +38,11 @@ public void testFlatMap() { map.forEach((k, v) -> results.addAll(v)); runTime.endCountOneLine(); } + + @Test + public void testDouble() { + double d = 23; + Object ob = d; + System.out.println(ob); + } } diff --git a/java8/src/test/java/com/github/kuangcp/stream/HandleExceptionTest.java b/java8/src/test/java/com/github/kuangcp/stream/HandleExceptionTest.java index e5e9e3d1..5614fffc 100644 --- a/java8/src/test/java/com/github/kuangcp/stream/HandleExceptionTest.java +++ b/java8/src/test/java/com/github/kuangcp/stream/HandleExceptionTest.java @@ -11,11 +11,12 @@ public class HandleExceptionTest { // NPE in stream @Test(expected = NullPointerException.class) public void testException() { - IntStream.rangeClosed(1, 10).forEach(s -> { - System.out.println(s); - if (s > 7) { - throw new NullPointerException("Oops "); - } - }); + IntStream.rangeClosed(1, 10) + .forEach(s -> { + System.out.println(s); + if (s > 7) { + throw new NullPointerException("Oops "); + } + }); } } From f7c384cdfd2009282c650a93876118eac51835c8 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Sun, 7 Jul 2019 22:56:11 +0800 Subject: [PATCH 064/476] *) use jdk 11 --- class/build.gradle | 3 +++ .../kuangcp/serialize/customserialize/MythSerialize.java | 6 +++--- class/src/main/java/jvm/oom/DirectMemoryOOM.java | 1 + 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/class/build.gradle b/class/build.gradle index b6cfd307..83ebfa23 100644 --- a/class/build.gradle +++ b/class/build.gradle @@ -1,3 +1,6 @@ +sourceCompatibility = 1.8 +targetCompatibility = 1.8 + dependencies { implementation libs['fastjson'] implementation libs['gson'] diff --git a/class/src/main/java/com/github/kuangcp/serialize/customserialize/MythSerialize.java b/class/src/main/java/com/github/kuangcp/serialize/customserialize/MythSerialize.java index 20abef23..7b19e742 100644 --- a/class/src/main/java/com/github/kuangcp/serialize/customserialize/MythSerialize.java +++ b/class/src/main/java/com/github/kuangcp/serialize/customserialize/MythSerialize.java @@ -22,7 +22,7 @@ T in(Class target, InputStream inputStream) { String line = bufferedReader.readLine(); line = line.substring(1, line.length() - 1); String[] result = line.split("[,:]"); - object = target.newInstance(); + object = target.getDeclaredConstructor().newInstance(); Method[] methods = target.getDeclaredMethods(); for (int i = 0; i < result.length; i += 3) { for (Method method : methods) { @@ -46,8 +46,8 @@ T in(Class target, InputStream inputStream) { } } } - } catch (InstantiationException | IOException | IllegalAccessException | InvocationTargetException e) { - e.printStackTrace(); + } catch (InstantiationException | IOException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { + log.error(e.getMessage(), e); } return object; } diff --git a/class/src/main/java/jvm/oom/DirectMemoryOOM.java b/class/src/main/java/jvm/oom/DirectMemoryOOM.java index 5b84978c..111485bb 100644 --- a/class/src/main/java/jvm/oom/DirectMemoryOOM.java +++ b/class/src/main/java/jvm/oom/DirectMemoryOOM.java @@ -7,6 +7,7 @@ * -Xmx20M -XX:MaxDirectMemorySize=10M * * TODO 瞬间内存占满 似乎该参数在8中失效了 + * TODO Java11 中删除了 sun 包, 需要找到替代方式获取直接内存 * * @author kuangcp on 4/4/19-12:29 AM */ From 1c2150847c4e226137eee726854ec2523d5b7813 Mon Sep 17 00:00:00 2001 From: kcp Date: Mon, 8 Jul 2019 10:36:37 +0800 Subject: [PATCH 065/476] +) add assert --- .../java/com/github/kuangcp/stream/FlatMapTest.java | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/java8/src/test/java/com/github/kuangcp/stream/FlatMapTest.java b/java8/src/test/java/com/github/kuangcp/stream/FlatMapTest.java index ce5514ca..a57c6b74 100644 --- a/java8/src/test/java/com/github/kuangcp/stream/FlatMapTest.java +++ b/java8/src/test/java/com/github/kuangcp/stream/FlatMapTest.java @@ -1,11 +1,15 @@ package com.github.kuangcp.stream; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.junit.Assert.assertThat; + import com.github.kuangcp.time.GetRunTime; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.stream.Collectors; import org.junit.Test; @@ -20,22 +24,27 @@ public class FlatMapTest { */ @Test public void testFlatMap() { + int amount = 1000000; GetRunTime runTime = new GetRunTime(); Map> map = new HashMap<>(); - for (int i = 0; i < 10000; i++) { + + for (int i = 0; i < amount; i++) { map.put(i + "", Arrays.asList("1", "2", "3", i + "")); } + // flatmap runTime.startCount(); List result = map.entrySet().stream() .flatMap(v -> v.getValue().stream()) .collect(Collectors.toList()); runTime.endCountOneLine(); + assertThat(result.size(), equalTo(4 * amount)); + // forEach addAll List results = new ArrayList<>(); - runTime.startCount(); map.forEach((k, v) -> results.addAll(v)); runTime.endCountOneLine(); + assertThat(results.size(), equalTo(4 * amount)); } } From 62ced1d9efd8531e7d9795b84d70057439d6bce2 Mon Sep 17 00:00:00 2001 From: kcp Date: Mon, 8 Jul 2019 10:47:38 +0800 Subject: [PATCH 066/476] -) remove Unsafe --- class/src/main/java/jvm/oom/DirectMemoryOOM.java | 15 ++++++--------- .../com/github/kuangcp/hi/SimpleStatistic.java | 2 +- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/class/src/main/java/jvm/oom/DirectMemoryOOM.java b/class/src/main/java/jvm/oom/DirectMemoryOOM.java index 111485bb..b7bc1b1a 100644 --- a/class/src/main/java/jvm/oom/DirectMemoryOOM.java +++ b/class/src/main/java/jvm/oom/DirectMemoryOOM.java @@ -1,8 +1,5 @@ package jvm.oom; -import java.lang.reflect.Field; -import sun.misc.Unsafe; - /** * -Xmx20M -XX:MaxDirectMemorySize=10M * @@ -16,11 +13,11 @@ public class DirectMemoryOOM { private static final int unit = 1024 * 1024; public static void main(String[] args) throws IllegalAccessException { - Field field = Unsafe.class.getDeclaredFields()[0]; - field.setAccessible(true); - Unsafe unsafe = (Unsafe) field.get(null); - while (true) { - unsafe.allocateMemory(unit); - } +// Field field = Unsafe.class.getDeclaredFields()[0]; +// field.setAccessible(true); +// Unsafe unsafe = (Unsafe) field.get(null); +// while (true) { +// unsafe.allocateMemory(unit); +// } } } diff --git a/flink/src/main/java/com/github/kuangcp/hi/SimpleStatistic.java b/flink/src/main/java/com/github/kuangcp/hi/SimpleStatistic.java index d58ff61c..8188dd90 100644 --- a/flink/src/main/java/com/github/kuangcp/hi/SimpleStatistic.java +++ b/flink/src/main/java/com/github/kuangcp/hi/SimpleStatistic.java @@ -38,7 +38,7 @@ public static void main(String[] args) throws Exception { ENV.getConfig().setAutoWatermarkInterval(1000); - ENV.execute("PartsSpuStatistic"); + ENV.execute("SimpleStatistic"); JobExecutionResult lastJobExecutionResult = ENV.getLastJobExecutionResult(); lastJobExecutionResult.getAllAccumulatorResults() From cf88a65a8fc6d4645e011f4e931752533f1c08d2 Mon Sep 17 00:00:00 2001 From: kcp Date: Mon, 8 Jul 2019 15:33:41 +0800 Subject: [PATCH 067/476] x) cancel attempt to get job status --- .../github/kuangcp/hi/SimpleJobListener.java | 29 +++++++++++++++++++ .../com/github/kuangcp/hi/SimpleSource.java | 20 ++++++++++--- .../github/kuangcp/hi/SimpleStatistic.java | 21 ++++---------- .../github/kuangcp/hi/SimpleWaterMarker.java | 23 --------------- 4 files changed, 51 insertions(+), 42 deletions(-) create mode 100644 flink/src/main/java/com/github/kuangcp/hi/SimpleJobListener.java delete mode 100644 flink/src/main/java/com/github/kuangcp/hi/SimpleWaterMarker.java diff --git a/flink/src/main/java/com/github/kuangcp/hi/SimpleJobListener.java b/flink/src/main/java/com/github/kuangcp/hi/SimpleJobListener.java new file mode 100644 index 00000000..0782bf5f --- /dev/null +++ b/flink/src/main/java/com/github/kuangcp/hi/SimpleJobListener.java @@ -0,0 +1,29 @@ +package com.github.kuangcp.hi; + +import static org.apache.flink.runtime.jobgraph.JobStatus.FINISHED; + +import org.apache.flink.api.common.JobID; +import org.apache.flink.runtime.executiongraph.ExecutionGraph; +import org.apache.flink.runtime.executiongraph.JobStatusListener; +import org.apache.flink.runtime.jobgraph.JobStatus; + +/** + * 如果该对象能注入到 下面类的属性上, 问题就简单一些了 + * + * @author kuangchengping@qipeipu.com + * @see ExecutionGraph jobStatusListeners + * @since 2019-07-08 11:22 + */ +@Deprecated +public class SimpleJobListener implements JobStatusListener { + + @Override + public void jobStatusChanges(JobID jobId, JobStatus newJobStatus, long timestamp, + Throwable error) { + System.out.println(jobId); + + if (FINISHED.equals(newJobStatus)) { + System.out.println("complete"); + } + } +} diff --git a/flink/src/main/java/com/github/kuangcp/hi/SimpleSource.java b/flink/src/main/java/com/github/kuangcp/hi/SimpleSource.java index 60909277..e5957cbd 100644 --- a/flink/src/main/java/com/github/kuangcp/hi/SimpleSource.java +++ b/flink/src/main/java/com/github/kuangcp/hi/SimpleSource.java @@ -2,6 +2,7 @@ import java.time.Month; import java.util.List; +import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import java.util.stream.IntStream; import lombok.extern.slf4j.Slf4j; @@ -9,16 +10,15 @@ /** * 模拟数据库分页查询 * - * @author https://github.com/kuangcp - * 2019-06-16 15:12 + * @author https://github.com/kuangcp 2019-06-16 15:12 */ @Slf4j class SimpleSource { private String id; - private static final int pageNum = 8; - private static final int pageSize = 20; + private static final int pageNum = 40; + private static final int pageSize = 500; private int cursor; @@ -31,10 +31,22 @@ boolean hasNextPage() { } List generateResource() { + delayTime(); cursor++; log.info("source: id={} cursor={}", id, cursor); return IntStream.rangeClosed(1, pageSize) .mapToObj(i -> Month.of((i + cursor) % 12 + 1).toString()) .collect(Collectors.toList()); } + + /** + * 延长Flink执行时间 + */ + private void delayTime() { + try { + TimeUnit.MILLISECONDS.sleep(100); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } } diff --git a/flink/src/main/java/com/github/kuangcp/hi/SimpleStatistic.java b/flink/src/main/java/com/github/kuangcp/hi/SimpleStatistic.java index 8188dd90..29afd40f 100644 --- a/flink/src/main/java/com/github/kuangcp/hi/SimpleStatistic.java +++ b/flink/src/main/java/com/github/kuangcp/hi/SimpleStatistic.java @@ -3,6 +3,7 @@ import java.util.List; import java.util.Objects; +import java.util.concurrent.TimeUnit; import lombok.extern.slf4j.Slf4j; import org.apache.flink.api.common.JobExecutionResult; import org.apache.flink.api.common.functions.MapFunction; @@ -13,6 +14,8 @@ import org.apache.flink.api.java.tuple.Tuple2; /** + * Batch 的 调用是通过 REST API的, 只能在发起请求的地方依据返回值来判断是否执行完成和成功 + * * @author https://github.com/kuangcp * @since 2019-05-16 16:26 */ @@ -36,18 +39,7 @@ public static void main(String[] args) throws Exception { log.error(e.getMessage(), e); } - ENV.getConfig().setAutoWatermarkInterval(1000); - ENV.execute("SimpleStatistic"); - - JobExecutionResult lastJobExecutionResult = ENV.getLastJobExecutionResult(); - lastJobExecutionResult.getAllAccumulatorResults() - .forEach((k, v) -> log.error("failed : k={} {}", k, v)); - if (lastJobExecutionResult.getAllAccumulatorResults().isEmpty()) { - log.info("run success"); - } else { - log.error("run failed"); - } } /** @@ -70,12 +62,11 @@ private static void calculateAndAggregate(String batchId) { // 当前窗口内和历史数据合并 if (Objects.isNull(result)) { - result = counts.groupBy(0).sum(1); + result = counts.groupBy(0).sum(1).name("cache"); } else { - DataSet> temp = result.union(counts); - result = temp.groupBy(0).sum(1); + DataSet> temp = result.union(counts).name("union cache"); + result = temp.groupBy(0).sum(1).name("merge cache"); } - log.info("aggregate once"); } if (Objects.isNull(result)) { diff --git a/flink/src/main/java/com/github/kuangcp/hi/SimpleWaterMarker.java b/flink/src/main/java/com/github/kuangcp/hi/SimpleWaterMarker.java deleted file mode 100644 index 0bb44adb..00000000 --- a/flink/src/main/java/com/github/kuangcp/hi/SimpleWaterMarker.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.github.kuangcp.hi; - -import javax.annotation.Nullable; -import org.apache.flink.streaming.api.functions.AssignerWithPeriodicWatermarks; -import org.apache.flink.streaming.api.watermark.Watermark; - -/** - * @author https://github.com/kuangcp - * @date 2019-06-21 11:47 - */ -public class SimpleWaterMarker implements AssignerWithPeriodicWatermarks { - - @Nullable - @Override - public Watermark getCurrentWatermark() { - return null; - } - - @Override - public long extractTimestamp(Object element, long previousElementTimestamp) { - return 0; - } -} From b5b1e450f9ee5c9526555cb858741e4779f485db Mon Sep 17 00:00:00 2001 From: kcp Date: Mon, 8 Jul 2019 17:02:49 +0800 Subject: [PATCH 068/476] x) extract common cofig --- algorithms/src/main/resources/logback.xml | 82 ------------------ build.gradle | 1 + class/src/main/resources/logback.xml | 83 ------------------ .../com/github/kuangcp/map/HashMapTest.java | 64 ++++++++++++-- common-config/Readme.md | 1 + .../src/main/resources/logback.xml | 0 concurrency/src/test/resources/logback.xml | 83 ------------------ gui/src/main/resources/logback.xml | 82 ------------------ hadoop/src/main/resources/logback.xml | 84 ------------------- kafka/src/main/resources/logback.xml | 84 ------------------- netty/src/main/resources/logback.xml | 82 ------------------ network/src/main/resources/logback.xml | 82 ------------------ pattern/src/main/resources/logback.xml | 82 ------------------ settings.gradle | 3 +- spring/src/main/resources/logback.xml | 82 ------------------ 15 files changed, 60 insertions(+), 835 deletions(-) delete mode 100644 algorithms/src/main/resources/logback.xml delete mode 100644 class/src/main/resources/logback.xml create mode 100644 common-config/Readme.md rename {generic => common-config}/src/main/resources/logback.xml (100%) delete mode 100644 concurrency/src/test/resources/logback.xml delete mode 100644 gui/src/main/resources/logback.xml delete mode 100644 hadoop/src/main/resources/logback.xml delete mode 100644 kafka/src/main/resources/logback.xml delete mode 100644 netty/src/main/resources/logback.xml delete mode 100644 network/src/main/resources/logback.xml delete mode 100644 pattern/src/main/resources/logback.xml delete mode 100644 spring/src/main/resources/logback.xml diff --git a/algorithms/src/main/resources/logback.xml b/algorithms/src/main/resources/logback.xml deleted file mode 100644 index 35ff2b46..00000000 --- a/algorithms/src/main/resources/logback.xml +++ /dev/null @@ -1,82 +0,0 @@ - - - - - - - - %d{HH:mm:ss.SSS} %highlight(%-5level) [%thread] %logger{36}:%yellow(%-3L) %msg%n - - - DEBUG - - - - - - ${log.base}debug.log - true - - ${log.base}debug.%d{yyyy-MM-dd}.log - - - %d{MM-dd HH:mm:ss.SSS} |-%-5level |%thread| %logger{36}:%L - %msg%n - - - DEBUG - - - - - - ${log.base}warn.log - true - - ${log.base}warn.%d{yyyy-MM-dd}.log - - - %d{MM-dd HH:mm:ss.SSS} |-%-5level %logger{36}:%L - %msg%n - - - WARN - - - - - - ${log.base}info.log - true - - ${log.base}info.%d{yyyy-MM-dd}.log - - - %d{MM-dd HH:mm:ss.SSS} |-%-5level %logger{36}:%L - %msg%n - - - INFO - - - - - - ${log.base}error.log - true - - ${log.base}error.%d{yyyy-MM-dd}.log - - - %d{MM-dd HH:mm:ss.SSS} | %thread |-%-5level %logger{36}:%L - %msg%n - - - ERROR - - - - - - - - - - - \ No newline at end of file diff --git a/build.gradle b/build.gradle index b48709d7..36145578 100644 --- a/build.gradle +++ b/build.gradle @@ -43,5 +43,6 @@ subprojects { testImplementation libs['junit'] testImplementation libs['hamcrest-core'] testImplementation libs['hamcrest-lib'] + implementation project(":common-config") } } diff --git a/class/src/main/resources/logback.xml b/class/src/main/resources/logback.xml deleted file mode 100644 index c32ae9fe..00000000 --- a/class/src/main/resources/logback.xml +++ /dev/null @@ -1,83 +0,0 @@ - - - - - - - - %d{HH:mm:ss.SSS} %highlight(%-5level) [%thread] %logger{36}:%yellow(%-3L) %msg%n - - - - DEBUG - - - - - - ${log.base}debug.log - true - - ${log.base}debug.%d{yyyy-MM-dd}.log - - - %d{MM-dd HH:mm:ss.SSS} |-%-5level |%thread| %logger{36}:%L - %msg%n - - - DEBUG - - - - - - ${log.base}warn.log - true - - ${log.base}warn.%d{yyyy-MM-dd}.log - - - %d{MM-dd HH:mm:ss.SSS} |-%-5level %logger{36}:%L - %msg%n - - - WARN - - - - - - ${log.base}info.log - true - - ${log.base}info.%d{yyyy-MM-dd}.log - - - %d{MM-dd HH:mm:ss.SSS} |-%-5level %logger{36}:%L - %msg%n - - - INFO - - - - - - ${log.base}error.log - true - - ${log.base}error.%d{yyyy-MM-dd}.log - - - %d{MM-dd HH:mm:ss.SSS} | %thread |-%-5level %logger{36}:%L - %msg%n - - - ERROR - - - - - - - - - - - \ No newline at end of file diff --git a/collection/src/test/java/com/github/kuangcp/map/HashMapTest.java b/collection/src/test/java/com/github/kuangcp/map/HashMapTest.java index d8b689c9..9f8cc3b6 100644 --- a/collection/src/test/java/com/github/kuangcp/map/HashMapTest.java +++ b/collection/src/test/java/com/github/kuangcp/map/HashMapTest.java @@ -1,23 +1,30 @@ package com.github.kuangcp.map; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.MatcherAssert.assertThat; + import java.util.HashMap; +import java.util.HashSet; import java.util.Map; +import java.util.Objects; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.NoArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.junit.Test; /** * @author kuangcp on 2019-04-16 10:22 AM */ +@Slf4j public class HashMapTest { @Builder @NoArgsConstructor @AllArgsConstructor - static class Foo { + static class Banana { - private String name; + private String tag; @Override public int hashCode() { @@ -26,7 +33,7 @@ public int hashCode() { @Override public String toString() { - return name; + return tag; } } @@ -35,16 +42,57 @@ public String toString() { */ @Test public void testSameHashCode() { - Map map = new HashMap<>(); + Map map = new HashMap<>(); for (int i = 0; i < 10; i++) { - map.put(new Foo(), 1); + map.put(new Banana(), 1); } - Foo you = Foo.builder().name("you").build(); - map.put(you, 12); - map.remove(new Foo()); + Banana banana = Banana.builder().tag("banana").build(); + map.put(banana, 12); + map.remove(new Banana()); System.out.println("size=" + map.size()); map.keySet().forEach(System.out::println); } + + /** + * 注意 keySet 返回的是 Map 中的存储结构, 如果改变 keySet, 就会影响到原有的Map + */ + @Test + public void testKeySet() { + Map dict1 = new HashMap<>(); + Map dict2 = new HashMap<>(); + + dict1.put("1", "5"); + dict1.put("2", "5"); + dict1.put("3", "5"); + + dict2.put("4", "5"); + dict2.put("3", "5"); + dict2.put("2", "5"); + + log.info("new set"); + + HashSet common = new HashSet<>(dict1.keySet()); + common.retainAll(dict2.keySet()); + assertThat(common.size(), equalTo(2)); + assertThat(dict1.size(), equalTo(3)); + assertThat(dict2.size(), equalTo(3)); + showMap(dict1); + + log.info("origin set"); + dict1.keySet().retainAll(dict2.keySet()); + assertThat(dict1.size(), equalTo(2)); + assertThat(dict2.size(), equalTo(3)); + + showMap(dict1); + } + + private void showMap(Map map) { + if (Objects.isNull(map)) { + return; + } + + map.forEach((k, v) -> log.info("k={} v={}", k, v)); + } } diff --git a/common-config/Readme.md b/common-config/Readme.md new file mode 100644 index 00000000..37c4b4d2 --- /dev/null +++ b/common-config/Readme.md @@ -0,0 +1 @@ +# 公共的配置文件 diff --git a/generic/src/main/resources/logback.xml b/common-config/src/main/resources/logback.xml similarity index 100% rename from generic/src/main/resources/logback.xml rename to common-config/src/main/resources/logback.xml diff --git a/concurrency/src/test/resources/logback.xml b/concurrency/src/test/resources/logback.xml deleted file mode 100644 index c32ae9fe..00000000 --- a/concurrency/src/test/resources/logback.xml +++ /dev/null @@ -1,83 +0,0 @@ - - - - - - - - %d{HH:mm:ss.SSS} %highlight(%-5level) [%thread] %logger{36}:%yellow(%-3L) %msg%n - - - - DEBUG - - - - - - ${log.base}debug.log - true - - ${log.base}debug.%d{yyyy-MM-dd}.log - - - %d{MM-dd HH:mm:ss.SSS} |-%-5level |%thread| %logger{36}:%L - %msg%n - - - DEBUG - - - - - - ${log.base}warn.log - true - - ${log.base}warn.%d{yyyy-MM-dd}.log - - - %d{MM-dd HH:mm:ss.SSS} |-%-5level %logger{36}:%L - %msg%n - - - WARN - - - - - - ${log.base}info.log - true - - ${log.base}info.%d{yyyy-MM-dd}.log - - - %d{MM-dd HH:mm:ss.SSS} |-%-5level %logger{36}:%L - %msg%n - - - INFO - - - - - - ${log.base}error.log - true - - ${log.base}error.%d{yyyy-MM-dd}.log - - - %d{MM-dd HH:mm:ss.SSS} | %thread |-%-5level %logger{36}:%L - %msg%n - - - ERROR - - - - - - - - - - - \ No newline at end of file diff --git a/gui/src/main/resources/logback.xml b/gui/src/main/resources/logback.xml deleted file mode 100644 index 5e375431..00000000 --- a/gui/src/main/resources/logback.xml +++ /dev/null @@ -1,82 +0,0 @@ - - - - - - - - %d{HH:mm:ss.SSS} %highlight(%-5level) [%thread] %logger{30}:%yellow(%-3L) %msg%n - - - DEBUG - - - - - - ${log.base}debug.log - true - - ${log.base}debug.%d{yyyy-MM-dd}.log - - - %d{MM-dd HH:mm:ss.SSS} |-%-5level |%thread| %logger{36}:%L - %msg%n - - - DEBUG - - - - - - ${log.base}warn.log - true - - ${log.base}warn.%d{yyyy-MM-dd}.log - - - %d{MM-dd HH:mm:ss.SSS} |-%-5level %logger{36}:%L - %msg%n - - - WARN - - - - - - ${log.base}info.log - true - - ${log.base}info.%d{yyyy-MM-dd}.log - - - %d{MM-dd HH:mm:ss.SSS} |-%-5level %logger{36}:%L - %msg%n - - - INFO - - - - - - ${log.base}error.log - true - - ${log.base}error.%d{yyyy-MM-dd}.log - - - %d{MM-dd HH:mm:ss.SSS} | %thread |-%-5level %logger{36}:%L - %msg%n - - - ERROR - - - - - - - - - - - \ No newline at end of file diff --git a/hadoop/src/main/resources/logback.xml b/hadoop/src/main/resources/logback.xml deleted file mode 100644 index 49ed46df..00000000 --- a/hadoop/src/main/resources/logback.xml +++ /dev/null @@ -1,84 +0,0 @@ - - - - - - - - %d{HH:mm:ss.SSS} %highlight(%-5level) [%thread] %logger{30}:%yellow(%-3L) %msg%n - - - DEBUG - - - - - - ${log.base}debug.log - true - - ${log.base}debug.%d{yyyy-MM-dd}.log - - - %d{MM-dd HH:mm:ss.SSS} |-%-5level |%thread| %logger{36}:%L - %msg%n - - - DEBUG - - - - - - ${log.base}warn.log - true - - ${log.base}warn.%d{yyyy-MM-dd}.log - - - %d{MM-dd HH:mm:ss.SSS} |-%-5level %logger{36}:%L - %msg%n - - - WARN - - - - - - ${log.base}info.log - true - - ${log.base}info.%d{yyyy-MM-dd}.log - - - %d{MM-dd HH:mm:ss.SSS} |-%-5level %logger{36}:%L - %msg%n - - - INFO - - - - - - ${log.base}error.log - true - - ${log.base}error.%d{yyyy-MM-dd}.log - - - %d{MM-dd HH:mm:ss.SSS} | %thread |-%-5level %logger{36}:%L - %msg%n - - - ERROR - - - - - - - - - - - - - \ No newline at end of file diff --git a/kafka/src/main/resources/logback.xml b/kafka/src/main/resources/logback.xml deleted file mode 100644 index 49ed46df..00000000 --- a/kafka/src/main/resources/logback.xml +++ /dev/null @@ -1,84 +0,0 @@ - - - - - - - - %d{HH:mm:ss.SSS} %highlight(%-5level) [%thread] %logger{30}:%yellow(%-3L) %msg%n - - - DEBUG - - - - - - ${log.base}debug.log - true - - ${log.base}debug.%d{yyyy-MM-dd}.log - - - %d{MM-dd HH:mm:ss.SSS} |-%-5level |%thread| %logger{36}:%L - %msg%n - - - DEBUG - - - - - - ${log.base}warn.log - true - - ${log.base}warn.%d{yyyy-MM-dd}.log - - - %d{MM-dd HH:mm:ss.SSS} |-%-5level %logger{36}:%L - %msg%n - - - WARN - - - - - - ${log.base}info.log - true - - ${log.base}info.%d{yyyy-MM-dd}.log - - - %d{MM-dd HH:mm:ss.SSS} |-%-5level %logger{36}:%L - %msg%n - - - INFO - - - - - - ${log.base}error.log - true - - ${log.base}error.%d{yyyy-MM-dd}.log - - - %d{MM-dd HH:mm:ss.SSS} | %thread |-%-5level %logger{36}:%L - %msg%n - - - ERROR - - - - - - - - - - - - - \ No newline at end of file diff --git a/netty/src/main/resources/logback.xml b/netty/src/main/resources/logback.xml deleted file mode 100644 index 5e375431..00000000 --- a/netty/src/main/resources/logback.xml +++ /dev/null @@ -1,82 +0,0 @@ - - - - - - - - %d{HH:mm:ss.SSS} %highlight(%-5level) [%thread] %logger{30}:%yellow(%-3L) %msg%n - - - DEBUG - - - - - - ${log.base}debug.log - true - - ${log.base}debug.%d{yyyy-MM-dd}.log - - - %d{MM-dd HH:mm:ss.SSS} |-%-5level |%thread| %logger{36}:%L - %msg%n - - - DEBUG - - - - - - ${log.base}warn.log - true - - ${log.base}warn.%d{yyyy-MM-dd}.log - - - %d{MM-dd HH:mm:ss.SSS} |-%-5level %logger{36}:%L - %msg%n - - - WARN - - - - - - ${log.base}info.log - true - - ${log.base}info.%d{yyyy-MM-dd}.log - - - %d{MM-dd HH:mm:ss.SSS} |-%-5level %logger{36}:%L - %msg%n - - - INFO - - - - - - ${log.base}error.log - true - - ${log.base}error.%d{yyyy-MM-dd}.log - - - %d{MM-dd HH:mm:ss.SSS} | %thread |-%-5level %logger{36}:%L - %msg%n - - - ERROR - - - - - - - - - - - \ No newline at end of file diff --git a/network/src/main/resources/logback.xml b/network/src/main/resources/logback.xml deleted file mode 100644 index 5e375431..00000000 --- a/network/src/main/resources/logback.xml +++ /dev/null @@ -1,82 +0,0 @@ - - - - - - - - %d{HH:mm:ss.SSS} %highlight(%-5level) [%thread] %logger{30}:%yellow(%-3L) %msg%n - - - DEBUG - - - - - - ${log.base}debug.log - true - - ${log.base}debug.%d{yyyy-MM-dd}.log - - - %d{MM-dd HH:mm:ss.SSS} |-%-5level |%thread| %logger{36}:%L - %msg%n - - - DEBUG - - - - - - ${log.base}warn.log - true - - ${log.base}warn.%d{yyyy-MM-dd}.log - - - %d{MM-dd HH:mm:ss.SSS} |-%-5level %logger{36}:%L - %msg%n - - - WARN - - - - - - ${log.base}info.log - true - - ${log.base}info.%d{yyyy-MM-dd}.log - - - %d{MM-dd HH:mm:ss.SSS} |-%-5level %logger{36}:%L - %msg%n - - - INFO - - - - - - ${log.base}error.log - true - - ${log.base}error.%d{yyyy-MM-dd}.log - - - %d{MM-dd HH:mm:ss.SSS} | %thread |-%-5level %logger{36}:%L - %msg%n - - - ERROR - - - - - - - - - - - \ No newline at end of file diff --git a/pattern/src/main/resources/logback.xml b/pattern/src/main/resources/logback.xml deleted file mode 100644 index 5e375431..00000000 --- a/pattern/src/main/resources/logback.xml +++ /dev/null @@ -1,82 +0,0 @@ - - - - - - - - %d{HH:mm:ss.SSS} %highlight(%-5level) [%thread] %logger{30}:%yellow(%-3L) %msg%n - - - DEBUG - - - - - - ${log.base}debug.log - true - - ${log.base}debug.%d{yyyy-MM-dd}.log - - - %d{MM-dd HH:mm:ss.SSS} |-%-5level |%thread| %logger{36}:%L - %msg%n - - - DEBUG - - - - - - ${log.base}warn.log - true - - ${log.base}warn.%d{yyyy-MM-dd}.log - - - %d{MM-dd HH:mm:ss.SSS} |-%-5level %logger{36}:%L - %msg%n - - - WARN - - - - - - ${log.base}info.log - true - - ${log.base}info.%d{yyyy-MM-dd}.log - - - %d{MM-dd HH:mm:ss.SSS} |-%-5level %logger{36}:%L - %msg%n - - - INFO - - - - - - ${log.base}error.log - true - - ${log.base}error.%d{yyyy-MM-dd}.log - - - %d{MM-dd HH:mm:ss.SSS} | %thread |-%-5level %logger{36}:%L - %msg%n - - - ERROR - - - - - - - - - - - \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index ae75afe6..bcac6460 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,6 +1,7 @@ rootProject.name = 'JavaBase' include( - 'io' + 'common-config' + , 'io' , 'generic' , 'collection' , 'class' diff --git a/spring/src/main/resources/logback.xml b/spring/src/main/resources/logback.xml deleted file mode 100644 index 5e375431..00000000 --- a/spring/src/main/resources/logback.xml +++ /dev/null @@ -1,82 +0,0 @@ - - - - - - - - %d{HH:mm:ss.SSS} %highlight(%-5level) [%thread] %logger{30}:%yellow(%-3L) %msg%n - - - DEBUG - - - - - - ${log.base}debug.log - true - - ${log.base}debug.%d{yyyy-MM-dd}.log - - - %d{MM-dd HH:mm:ss.SSS} |-%-5level |%thread| %logger{36}:%L - %msg%n - - - DEBUG - - - - - - ${log.base}warn.log - true - - ${log.base}warn.%d{yyyy-MM-dd}.log - - - %d{MM-dd HH:mm:ss.SSS} |-%-5level %logger{36}:%L - %msg%n - - - WARN - - - - - - ${log.base}info.log - true - - ${log.base}info.%d{yyyy-MM-dd}.log - - - %d{MM-dd HH:mm:ss.SSS} |-%-5level %logger{36}:%L - %msg%n - - - INFO - - - - - - ${log.base}error.log - true - - ${log.base}error.%d{yyyy-MM-dd}.log - - - %d{MM-dd HH:mm:ss.SSS} | %thread |-%-5level %logger{36}:%L - %msg%n - - - ERROR - - - - - - - - - - - \ No newline at end of file From df88c5b7286152331342008ac3fb535912e54a5e Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Mon, 8 Jul 2019 23:35:28 +0800 Subject: [PATCH 069/476] +) start learn map fail fast --- .../list/ConcurrentModificationTest.java | 81 ++++++++++++------- ...t.java => ConcurrentModificationTest.java} | 55 +++++++++---- .../kuangcp/proxy/salary/aop/xml/Readme.md | 6 ++ ...224\250XML\345\256\236\347\216\260AOP.txt" | 3 - 4 files changed, 96 insertions(+), 49 deletions(-) rename collection/src/test/java/com/github/kuangcp/map/{CurrentModificationTest.java => ConcurrentModificationTest.java} (56%) create mode 100644 spring/src/test/java/com/github/kuangcp/proxy/salary/aop/xml/Readme.md delete mode 100644 "spring/src/test/java/com/github/kuangcp/proxy/salary/aop/xml/\344\275\277\347\224\250XML\345\256\236\347\216\260AOP.txt" diff --git a/collection/src/test/java/com/github/kuangcp/list/ConcurrentModificationTest.java b/collection/src/test/java/com/github/kuangcp/list/ConcurrentModificationTest.java index 86cf28cf..cc37023b 100644 --- a/collection/src/test/java/com/github/kuangcp/list/ConcurrentModificationTest.java +++ b/collection/src/test/java/com/github/kuangcp/list/ConcurrentModificationTest.java @@ -28,8 +28,12 @@ public class ConcurrentModificationTest { // 经测试 ArrayList LinkedList Vector 行为均一致 private List list = new ArrayList<>(); - // 虽然不会抛出异常, 但是会有bug, 因为list的长度随着remove在变小, - // 但是 遍历用的index没有随之变化, 就会出现元素被跳过没有被索引到的情况 + /** + * 错误 + * + * 虽然不会抛出异常, 但是有bug, 因为list的长度随着remove在变小, + * 但是 遍历用的index没有随之变化, 就会出现元素被跳过没有被索引到的情况 + */ @Test public void testNormal() { for (int i = 0; i < list.size(); i++) { @@ -41,7 +45,41 @@ public void testNormal() { assertThat(list.size(), equalTo(6)); } - // 这样写就没有任何错误了, 但是显然的这代码性能低下, 可读性差 + /** + * 错误 + * + * 反编译后的代码, forEach 实际上是语法糖, next 方法必然抛出异常 + * + *
+   * Iterator var1 = this.list.iterator();
+   * while(var1.hasNext()) {
+   *   String x = (String)var1.next();
+   *   if (x.equals("del")) {
+   *     this.list.remove(x);
+   *   }
+   * }
+   * 
+ */ + @Test(expected = ConcurrentModificationException.class) + public void testForEach() { + for (String x : list) { + if (x.equals("del")) { + // 关键在于这里, 这个remove是使用迭代器遍历list, 找到目标后再复制原数组将对应的对象移除 + // 其中 next() 方法调用了 checkForComodification 方法 + // 由于 remove的调用增加了 modCount 所以在移除了一次后就抛出异常了 + list.remove(x); + log.info("remove one"); + // 如果这里加上 break语句 就没有异常, 这是因为修改了 modCount 但是不会执行到 next 方法 +// break; + } + } + } + + /** + * 正确 + * + * 但是显然的性能低下, 可读性差 + */ @Test public void testNormalCorrect() { while (true) { @@ -52,6 +90,8 @@ public void testNormalCorrect() { break; } } + + // 没有需要移除的元素 if (i == list.size()) { break; } @@ -60,41 +100,22 @@ public void testNormalCorrect() { assertThat(list.size(), equalTo(4)); } - // 反编译后的方法, forEach 实际上是语法糖 - // Iterator var1 = this.list.iterator(); - // while(var1.hasNext()) { - // String x = (String)var1.next(); - // if (x.equals("del")) { - // this.list.remove(x); - // } - // } - @Test(expected = ConcurrentModificationException.class) - public void testForEach() { - for (String x : list) { - if (x.equals("del")) { - // 关键在于这里, 这个remove是遍历list, 找到一致的再复制数组将对应的对象移除 - // 其中调用了 checkForComodification 方法, 由于 remove的调用增加了 modCount 所以就抛出异常了 - list.remove(x); - // 如果这里加上 break语句 就没有异常, 这是因为修改了 modCount 但是不会执行到 next 方法 -// break; - } - } - } - + /** + * 正确 + */ @Test public void testIterator() { Iterator it = list.iterator(); while (it.hasNext()) { String x = it.next(); if (x.equals("del")) { - // remove中有 expectedModCount = modCount; 避免了 ConcurrentModificationException + // remove中有 expectedModCount = modCount; 保证了两个值的一致性 避免了 ConcurrentModificationException it.remove(); } } assertThat(list.size(), equalTo(4)); - // 以上在Java8中可以简写为 - // list.removeIf(x -> x.equals("del")); + // 以上代码块在Java8中可以简写为 list.removeIf(x -> x.equals("del")); } @Before @@ -104,15 +125,15 @@ public void before() { list.add(i + ""); list.add("del"); } - for (int i = 0; i < 3; i++) { - list.add("del"); - } + + list.add("del"); list.add("test"); list.add("del"); } @After public void after() { + log.info("result: "); list.forEach(System.out::println); } } diff --git a/collection/src/test/java/com/github/kuangcp/map/CurrentModificationTest.java b/collection/src/test/java/com/github/kuangcp/map/ConcurrentModificationTest.java similarity index 56% rename from collection/src/test/java/com/github/kuangcp/map/CurrentModificationTest.java rename to collection/src/test/java/com/github/kuangcp/map/ConcurrentModificationTest.java index 200be612..ad716cfb 100644 --- a/collection/src/test/java/com/github/kuangcp/map/CurrentModificationTest.java +++ b/collection/src/test/java/com/github/kuangcp/map/ConcurrentModificationTest.java @@ -1,6 +1,5 @@ package com.github.kuangcp.map; -import java.util.ConcurrentModificationException; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -8,26 +7,30 @@ import org.junit.Test; /** + * TODO 完成 + * * @author kuangcp on 19-1-16-上午9:07 */ @Slf4j -public class CurrentModificationTest { +public class ConcurrentModificationTest { - // 使用HashMap 并发地发生修改(新增,删除)和读操作就会引发 ConcurrentModificationException - // 使用 ConcurrentHashMap 就不会 - // TODO 为什么 - // TODO 使用了ConcurrentHash 后避免了异常, 但是每次迭代的, 是最新的数据么 - @Test(expected = ConcurrentModificationException.class) + /** + * 异常时而发生时而不发生, 因为不像List, Map是无序的, HashMap中的实现是在需要做修改操作前缓存 modCount, 修改后再比对缓存值是否 + * TODO ? + */ +// @Test(expected = ConcurrentModificationException.class) + @Test public void testHashMap() { Map map = new HashMap<>(); - log.info("foreach"); - testReadAndModifyMapByForEach(map); +// log.info("foreach"); +// testReadAndModifyMapByForEach(map); log.info("lambda"); readAndModifyMapByLambda(map); } + // TODO 使用了ConcurrentHash 后避免了异常, 但是每次迭代的, 是最新的数据么 @Test public void testConcurrentHashMap() { Map map = new ConcurrentHashMap<>(); @@ -39,11 +42,14 @@ public void testConcurrentHashMap() { readAndModifyMapByLambda(map); } + /** + * 一条线程增加 主线程 forEach 读取 + */ private void testReadAndModifyMapByForEach(Map map) { new Thread(() -> addItemToMap(map)).start(); for (int i = 0; i < 100; i++) { - log.info("read : i={}", i); + log.info("read total: i={} size={}", i, map.size()); try { Thread.sleep(100); } catch (InterruptedException e) { @@ -51,35 +57,52 @@ private void testReadAndModifyMapByForEach(Map map) { } for (String value : map.values()) { - value.notify(); + String temp = value.replace("1", "ddd"); } } } + /** + * 一条线程增加 主线程通过 Lambda 表达式读取 + */ private void readAndModifyMapByLambda(Map map) { new Thread(() -> addItemToMap(map)).start(); + new Thread(() -> removeItemFromMap(map)).start(); - for (int i = 0; i < 100; i++) { - log.info("read : i={}", i); + for (int i = 0; i < 300; i++) { + log.info("read total: i={} size={}", i, map.size()); try { - Thread.sleep(100); + Thread.sleep(30); } catch (InterruptedException e) { e.printStackTrace(); } map.values().forEach(value -> { + String temp = value.replace("1", "ddd"); }); } } private void addItemToMap(Map map) { - for (int i = 0; i < 100; i++) { + for (int i = 0; i < 300; i++) { log.info("addItemToMap: i={}", i); try { - Thread.sleep(100); + Thread.sleep(30); } catch (InterruptedException e) { e.printStackTrace(); } map.put("t " + i, "1"); } } + + private void removeItemFromMap(Map map) { + for (int i = 0; i < 300; i++) { + log.info("addItemToMap: i={}", i); + try { + Thread.sleep(30); + } catch (InterruptedException e) { + e.printStackTrace(); + } + map.remove("t " + i); + } + } } diff --git a/spring/src/test/java/com/github/kuangcp/proxy/salary/aop/xml/Readme.md b/spring/src/test/java/com/github/kuangcp/proxy/salary/aop/xml/Readme.md new file mode 100644 index 00000000..e21d0444 --- /dev/null +++ b/spring/src/test/java/com/github/kuangcp/proxy/salary/aop/xml/Readme.md @@ -0,0 +1,6 @@ +# 使用 XML 实现 AOP + +如果目标类实现了接口,则spring容器会采用jdkproxy,如果目标类没有实现接口 + +则spring容器会采用 cglibproxy + diff --git "a/spring/src/test/java/com/github/kuangcp/proxy/salary/aop/xml/\344\275\277\347\224\250XML\345\256\236\347\216\260AOP.txt" "b/spring/src/test/java/com/github/kuangcp/proxy/salary/aop/xml/\344\275\277\347\224\250XML\345\256\236\347\216\260AOP.txt" deleted file mode 100644 index 675ac5c9..00000000 --- "a/spring/src/test/java/com/github/kuangcp/proxy/salary/aop/xml/\344\275\277\347\224\250XML\345\256\236\347\216\260AOP.txt" +++ /dev/null @@ -1,3 +0,0 @@ -说明: - 如果目标类实现了接口,则spring容器会采用jdkproxy,如果目标类没有实现接口, - 则spring容器会采用 cglibproxy \ No newline at end of file From 3d6c782c2e3e2a72456b50949a1f7a1bcd602029 Mon Sep 17 00:00:00 2001 From: kcp Date: Tue, 9 Jul 2019 17:51:46 +0800 Subject: [PATCH 070/476] *) fixed confuse --- .../github/kuangcp/time/LocalDateTest.java | 24 +++++++++++++++++++ .../com/github/kuangcp/hi/ConsumerDemo.java | 7 +++--- .../github/kuangcp/hi/ConsumerDemoTest.java | 4 ++-- 3 files changed, 30 insertions(+), 5 deletions(-) create mode 100644 java8/src/test/java/com/github/kuangcp/time/LocalDateTest.java diff --git a/java8/src/test/java/com/github/kuangcp/time/LocalDateTest.java b/java8/src/test/java/com/github/kuangcp/time/LocalDateTest.java new file mode 100644 index 00000000..fc077d89 --- /dev/null +++ b/java8/src/test/java/com/github/kuangcp/time/LocalDateTest.java @@ -0,0 +1,24 @@ +package com.github.kuangcp.time; + +import java.time.LocalDate; +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; + +/** + * @author kuangchengping@qipeipu.com + * @since 2019-07-09 17:10 + */ +@Slf4j +public class LocalDateTest { + + @Test + public void testCompare() { + LocalDate now = LocalDate.now(); + for (int i = 1; i < 10; i++) { + LocalDate newDate = now.plusDays(i); + boolean isAfter = newDate.isAfter(LocalDate.of(2019, 7, 15)); + + log.info("newDate={} isAfter={}", newDate, isAfter); + } + } +} diff --git a/kafka/src/main/java/com/github/kuangcp/hi/ConsumerDemo.java b/kafka/src/main/java/com/github/kuangcp/hi/ConsumerDemo.java index 3a856fce..13e40928 100644 --- a/kafka/src/main/java/com/github/kuangcp/hi/ConsumerDemo.java +++ b/kafka/src/main/java/com/github/kuangcp/hi/ConsumerDemo.java @@ -4,6 +4,7 @@ import static com.github.kuangcp.hi.Constants.KAFKA_SERVER; import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.kuangcp.hi.domain.ProductStatisticJobCommand; import com.github.kuangcp.hi.domain.StartCommand; import java.io.IOException; import java.time.Duration; @@ -37,14 +38,14 @@ static void receiveHi() { } } - static void receiveStart() throws IOException { + static void receiveCommand() throws IOException { KafkaConsumer kafkaConsumer = new KafkaConsumer<>(properties); - kafkaConsumer.subscribe(Collections.singletonList("OFC_PRODUCT_STATISTIC_JOB_DISPATCHING")); + kafkaConsumer.subscribe(Collections.singletonList(Constants.COMMAND_TOPIC)); while (true) { ConsumerRecords records = kafkaConsumer.poll(Duration.ofMillis(100)); for (ConsumerRecord record : records) { log.info("offset = {}, value = {}", record.offset(), record.value()); - StartCommand command = mapper.readValue(record.value(), StartCommand.class); + ProductStatisticJobCommand command = mapper.readValue(record.value(), ProductStatisticJobCommand.class); System.out.println(command); } } diff --git a/kafka/src/test/java/com/github/kuangcp/hi/ConsumerDemoTest.java b/kafka/src/test/java/com/github/kuangcp/hi/ConsumerDemoTest.java index 2a7835cd..a86b1f4d 100644 --- a/kafka/src/test/java/com/github/kuangcp/hi/ConsumerDemoTest.java +++ b/kafka/src/test/java/com/github/kuangcp/hi/ConsumerDemoTest.java @@ -16,7 +16,7 @@ public void testReceiveHi() { @Test - public void testReceiveStart() throws IOException { - ConsumerDemo.receiveStart(); + public void testReceiveCommand() throws IOException { + ConsumerDemo.receiveCommand(); } } From 12f54a6156b0ae71642818a69eee1c059f6c66a1 Mon Sep 17 00:00:00 2001 From: kcp Date: Tue, 9 Jul 2019 20:34:45 +0800 Subject: [PATCH 071/476] *) fix send msg --- .../java/com/github/kuangcp/hi/Constants.java | 2 ++ .../com/github/kuangcp/hi/ProducerDemo.java | 21 ++++++++++++------- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/kafka/src/main/java/com/github/kuangcp/hi/Constants.java b/kafka/src/main/java/com/github/kuangcp/hi/Constants.java index 02eb0026..8a1c0ae3 100644 --- a/kafka/src/main/java/com/github/kuangcp/hi/Constants.java +++ b/kafka/src/main/java/com/github/kuangcp/hi/Constants.java @@ -12,4 +12,6 @@ public interface Constants { String HI_TOPIC = "Hi"; String START_TOPIC = "start"; + + String COMMAND_TOPIC = "OFC_PRODUCT_STATISTIC_JOB_DISPATCHING"; } diff --git a/kafka/src/main/java/com/github/kuangcp/hi/ProducerDemo.java b/kafka/src/main/java/com/github/kuangcp/hi/ProducerDemo.java index 0b51bed2..ae93a55e 100644 --- a/kafka/src/main/java/com/github/kuangcp/hi/ProducerDemo.java +++ b/kafka/src/main/java/com/github/kuangcp/hi/ProducerDemo.java @@ -10,9 +10,12 @@ import com.github.kuangcp.hi.domain.StartCommand; import com.github.kuangcp.io.ResourceTool; import java.io.IOException; +import java.time.LocalDateTime; +import java.time.ZoneId; import java.util.Date; import java.util.HashSet; import java.util.Properties; +import java.util.UUID; import lombok.extern.slf4j.Slf4j; import org.apache.kafka.clients.producer.KafkaProducer; import org.apache.kafka.clients.producer.Producer; @@ -74,23 +77,24 @@ static void sendCommand() { Producer producer = null; HashSet spans = new HashSet<>(); spans.add(ProductStatisticSpan.MONTH); - spans.add(ProductStatisticSpan.YEAR); try { producer = new KafkaProducer<>(properties); - for (int i = 0; i < 20; i++) { + for (int i = 0; i < 2; i++) { ProductStatisticJobCommand msg = ProductStatisticJobCommand.builder() - .startTime(new Date()).endTime(new Date()).id("32131" + i).productStatisticSpan(spans) + .id(UUID.randomUUID().toString() + "_test_" + i) + .startTime(toDate(LocalDateTime.now().withMonth(1))) + .endTime(toDate(LocalDateTime.now().withMonth(3))) + .productStatisticSpan(spans) .build(); - ProducerRecord record = new ProducerRecord<>( - "OFC_PRODUCT_STATISTIC_JOB_DISPATCHING", mapper.writeValueAsString(msg)); + ProducerRecord record = new ProducerRecord<>(Constants.COMMAND_TOPIC, + mapper.writeValueAsString(msg)); long time = System.currentTimeMillis(); producer.send(record, (metadata, e) -> { long elapsedTime = System.currentTimeMillis() - time; if (metadata != null) { - System.out.printf("sent record(key=%s value=%s) " + - "meta(partition=%d, offset=%d) time=%d\n", + log.info("sent record(key={} value={}) meta(partition={}, offset={}) time={}", record.key(), record.value(), metadata.partition(), metadata.offset(), elapsedTime); } else { @@ -109,6 +113,9 @@ static void sendCommand() { } } + public static Date toDate(LocalDateTime dateTime) { + return Date.from(dateTime.atZone(ZoneId.systemDefault()).toInstant()); + } private static Properties getConf() { Properties properties = new Properties(); From 7b55b3c8aa9fc41ed8e5894c5b3f508e4b60c7ea Mon Sep 17 00:00:00 2001 From: kcp Date: Wed, 10 Jul 2019 20:47:30 +0800 Subject: [PATCH 072/476] +) learn double check --- .../base/method/EqualsAndHashCodeTest.java | 16 +++++++--------- .../github/kuangcp/singleton/DoubleCheck.java | 19 +++++++++++++++---- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/class/src/test/java/syntax/base/method/EqualsAndHashCodeTest.java b/class/src/test/java/syntax/base/method/EqualsAndHashCodeTest.java index eb4b9242..c46d29b7 100644 --- a/class/src/test/java/syntax/base/method/EqualsAndHashCodeTest.java +++ b/class/src/test/java/syntax/base/method/EqualsAndHashCodeTest.java @@ -11,9 +11,9 @@ import org.junit.Test; /** - * @author kuangcp on 3/5/19-11:17 AM - * 值类 必须重写 equals 使得比较时比较的是所有属性的值而不是地址 - * 其他普通类 不重写 + * @author kuangcp + * + * 值类 必须重写 equals 使得比较时比较的是所有属性的值而不是地址 其他普通类 不重写 */ @Slf4j public class EqualsAndHashCodeTest { @@ -33,7 +33,8 @@ private class Target extends Flyable { private int age; } - // 若 hashCode 相等,不一定 equals; 若equals,hashCode一定相等 + // 若 hashCode 相等,则 不一定满足 equals; + // 若满足 equals, 则 hashCode一定相等 @Test public void testEquals() { Target target = new Target("you", 3); @@ -45,8 +46,6 @@ public void testEquals() { assertThat(target.hashCode(), equalTo(you.hashCode())); } -/////////////////////////////////////////// - class Electronics { String name; @@ -61,7 +60,7 @@ class Electronics { @Override public boolean equals(Object obj) { // 如果改成 用 类去判断 就能避免问题 -// if(Objects.nonNull(obj) && obj.getClass() == this.getClass()){ + // if(Objects.nonNull(obj) && obj.getClass() == this.getClass()) if (Objects.nonNull(obj) && obj instanceof Electronics) { Electronics phone = (Electronics) obj; return phone.name.equals(name); @@ -79,7 +78,7 @@ class Phone extends Electronics { private int number; - public Phone(int number, String name) { + Phone(int number, String name) { this.number = number; this.name = name; } @@ -111,7 +110,6 @@ public void testEqualPhone() { assertThat(electronics.equals(a), equalTo(true)); assertThat(a.equals(b), equalTo(false)); - } } diff --git a/pattern/src/main/java/com/github/kuangcp/singleton/DoubleCheck.java b/pattern/src/main/java/com/github/kuangcp/singleton/DoubleCheck.java index 443fc981..2b64a6f9 100644 --- a/pattern/src/main/java/com/github/kuangcp/singleton/DoubleCheck.java +++ b/pattern/src/main/java/com/github/kuangcp/singleton/DoubleCheck.java @@ -1,12 +1,19 @@ package com.github.kuangcp.singleton; +import lombok.Data; + /** * 双重校验方式 线程不安全 * + * https://blog.csdn.net/topdeveloperr/article/details/81194654 + * * @author kuangcp on 2019-04-11 12:41 PM */ +@Data public class DoubleCheck { + private int num; + private static DoubleCheck singleton; private DoubleCheck() { @@ -16,12 +23,16 @@ public static DoubleCheck getInstance() { if (singleton == null) { synchronized (DoubleCheck.class) { // 因为 new 这个操作是分为三步 - // 1.分配内存空间, 2.初始化对象, 3.singleton 引用变量指向内存空间 - // 2 3 步可能发生指令重排序 + // 1.分配内存空间, + // 2.初始化对象, + // 3.singleton 引用变量指向内存空间 - // 线程A执行1、3后让出cpu,此时还未执行2,别的线程拿到cpu,发现instance不为null,直接返回使用,而此时 instance还未初始化。 + // 2,3 步可能发生指令重排序 - singleton = new DoubleCheck(); + // 线程A执行1、3后让出cpu,此时还未执行2,别的线程拿到cpu,发现instance不为null,直接返回使用,而此时 instance还未初始化。 + if (singleton == null) { + singleton = new DoubleCheck(); + } } } return singleton; From be63b7b2b089f95cdef1a08564e401e238925d11 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Sun, 14 Jul 2019 17:24:17 +0800 Subject: [PATCH 073/476] x) fixed dependency loop --- common-config/build.gradle | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 common-config/build.gradle diff --git a/common-config/build.gradle b/common-config/build.gradle new file mode 100644 index 00000000..9548d28e --- /dev/null +++ b/common-config/build.gradle @@ -0,0 +1,3 @@ +configurations { + compile.exclude module: 'common-config' +} \ No newline at end of file From 9468aaf2c65a96211bf95851c3f26f6702e3a639 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Sun, 14 Jul 2019 17:24:45 +0800 Subject: [PATCH 074/476] *) simplify Heap OOM --- class/src/main/java/jvm/oom/HeapOOM.java | 67 ++++++++++++++++++-- class/src/test/java/jvm/oom/HeapOOMTest.java | 53 ++++------------ 2 files changed, 73 insertions(+), 47 deletions(-) diff --git a/class/src/main/java/jvm/oom/HeapOOM.java b/class/src/main/java/jvm/oom/HeapOOM.java index d14b64da..bafac6dc 100644 --- a/class/src/main/java/jvm/oom/HeapOOM.java +++ b/class/src/main/java/jvm/oom/HeapOOM.java @@ -4,13 +4,11 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.WeakHashMap; import java.util.concurrent.TimeUnit; import lombok.extern.slf4j.Slf4j; /** - * -Xms5m -Xmx5m -XX:+HeapDumpOnOutOfMemoryError - * 当实例化 5 个 HeapOOM 对象后就OOM了 - * * @author kuangcp on 4/3/19-10:11 PM */ @Slf4j @@ -28,6 +26,10 @@ void createArray() { } } + /** + * 重写了hashCode 没有重写 equals + * 导致了该对象在Map这样的集合中作为Key使用时, 不能按预期的覆盖旧值而是共存 + */ static class Key { Integer id; @@ -36,7 +38,6 @@ static class Key { this.id = id; } - // 重写了hashCode 没有重写 equals 导致了该对象在Map这样的集合中作Key时, 不能按预期的覆盖旧值而是共存 @Override public int hashCode() { return id.hashCode(); @@ -44,9 +45,14 @@ public int hashCode() { } void createMap() { - Map m = new HashMap<>(); + Map map = new HashMap<>(); + testMap(map); + } + + private void testMap(Map m) { while (true) { for (int i = 0; i < 10000; i++) { + // 因此这个判断就失效了 if (!m.containsKey(new Key(i))) { m.put(new Key(i), "Number:" + i); } @@ -54,4 +60,55 @@ void createMap() { log.info("m.size()=" + m.size()); } } + + /** + * 因为 WeakMap 中 如果键没有被外部引用, 就有可能被GC, 当内存不够就会频繁GC, 也就不会OOM + */ + void createWeakMap() { + testMap(new WeakHashMap<>()); + } + + /** + * 变量逃逸和不逃逸 的区别 + * + * 当一个线程发生OOM, 如果能够及时释放内存(堆内存足够大,线程占用CPU不是很高等等), 是不会影响到其他线程的 + */ + public void otherThreadWithOOM() throws Exception { + // 如果将该集合定义在这里 就会被所有线程共享 被引用数会多, 不容易回收, 就容易引起全面的OOM 导致进程退出 +// List data = new ArrayList<>(); + + Thread allocateMemory = new Thread(() -> { + // 但是定义在这里, 就只会被该线程持有, OOM 也只是该线程, 只有小概率会影响到进程内其他线程 + List data = new ArrayList<>(); + while (true) { + // 因为当这里无法分配内存 OOM 后就出循环了, 对象也就可以被回收了 + data.add(new byte[1024 * 10]); + try { + TimeUnit.MILLISECONDS.sleep(4); + } catch (InterruptedException e) { + log.error(e.getMessage(), e); + } +// log.info("allocate memory"); + } + }); + + Thread timer = new Thread(() -> { + while (true) { + try { + TimeUnit.MILLISECONDS.sleep(300); + } catch (InterruptedException e) { + log.error(e.getMessage(), e); + } + log.info("timer"); + } + }); + + allocateMemory.setName("allocateMemory"); + timer.setName("timer"); + + allocateMemory.start(); + timer.start(); + allocateMemory.join(); + timer.join(); + } } diff --git a/class/src/test/java/jvm/oom/HeapOOMTest.java b/class/src/test/java/jvm/oom/HeapOOMTest.java index 6341570e..3a0d6eae 100644 --- a/class/src/test/java/jvm/oom/HeapOOMTest.java +++ b/class/src/test/java/jvm/oom/HeapOOMTest.java @@ -1,14 +1,11 @@ package jvm.oom; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.TimeUnit; import lombok.extern.slf4j.Slf4j; import org.junit.Ignore; import org.junit.Test; /** - * -Xms5m -Xmx5m + * -Xms30m -Xmx30m -XX:+PrintGCDetails * * @author kuangcp on 4/5/19-9:45 AM */ @@ -19,50 +16,22 @@ public class HeapOOMTest { private HeapOOM heapOOM = new HeapOOM(); @Test - public void testOOMWithOtherThread() throws Exception { - // 如果将该集合放在这里, 那么就不会被回收, 导致OOM 所有线程都玩完 - // List data = new ArrayList<>(); - - Thread main = new Thread(() -> { - List data = new ArrayList<>(); - while (true) { - // 因为当这里无法分配内存 OOM 后就出循环了, 对象也就可以被回收了 - data.add(new byte[1024 * 256]); - try { - TimeUnit.SECONDS.sleep(1); - } catch (InterruptedException e) { - e.printStackTrace(); - } - log.info("allocate memory"); - } - }); - - Thread thread = new Thread(() -> { - while (true) { - try { - TimeUnit.SECONDS.sleep(1); - } catch (InterruptedException e) { - e.printStackTrace(); - } - log.info("timer"); - } - }); - - main.start(); - thread.start(); + public void testCreateArray() { + heapOOM.createArray(); + } - main.join(); - thread.join(); + @Test + public void testCreateMap() { + heapOOM.createMap(); } @Test - public void testCreateArray() { - heapOOM.createArray(); + public void testCreateWeakMap() { + heapOOM.createWeakMap(); } @Test - @Ignore - public void testCreateMap() throws Exception { - heapOOM.createMap(); + public void testOtherThreadWithOOM() throws Exception { + heapOOM.otherThreadWithOOM(); } } \ No newline at end of file From 91a9b94fe8a73f8b713e0b0605a728ebb1d6d74a Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Sun, 14 Jul 2019 17:25:02 +0800 Subject: [PATCH 075/476] *) simplify Mock Map Data --- .../com/github/kuangcp/map/IterateMapTest.java | 12 ++---------- .../github/kuangcp/map/RandomGetValueTest.java | 16 ++++------------ 2 files changed, 6 insertions(+), 22 deletions(-) diff --git a/collection/src/test/java/com/github/kuangcp/map/IterateMapTest.java b/collection/src/test/java/com/github/kuangcp/map/IterateMapTest.java index 5396cfbb..695a4146 100644 --- a/collection/src/test/java/com/github/kuangcp/map/IterateMapTest.java +++ b/collection/src/test/java/com/github/kuangcp/map/IterateMapTest.java @@ -1,10 +1,9 @@ package com.github.kuangcp.map; -import java.util.HashMap; +import com.github.kuangcp.mock.map.MockMap; import java.util.Iterator; import java.util.Map; import lombok.extern.slf4j.Slf4j; -import org.junit.BeforeClass; import org.junit.Test; /** @@ -14,14 +13,7 @@ @Slf4j public class IterateMapTest { - private static Map map = new HashMap<>(); - - @BeforeClass - public static void init() { - map.put("1", "a"); - map.put("2", "b"); - map.put("3", "c"); - } + private static Map map = MockMap.mock(6, String.class, String.class); /** * 通过Map.keySet()遍历key和value,二次取值 diff --git a/collection/src/test/java/com/github/kuangcp/map/RandomGetValueTest.java b/collection/src/test/java/com/github/kuangcp/map/RandomGetValueTest.java index 7b5876e8..ef2b3195 100644 --- a/collection/src/test/java/com/github/kuangcp/map/RandomGetValueTest.java +++ b/collection/src/test/java/com/github/kuangcp/map/RandomGetValueTest.java @@ -3,11 +3,10 @@ import static org.hamcrest.CoreMatchers.hasItem; import static org.hamcrest.MatcherAssert.assertThat; -import java.util.HashMap; +import com.github.kuangcp.mock.map.MockMap; import java.util.Map; import java.util.concurrent.ThreadLocalRandom; import lombok.extern.slf4j.Slf4j; -import org.junit.Before; import org.junit.Test; /** @@ -17,21 +16,14 @@ @Slf4j public class RandomGetValueTest { - private Map data = new HashMap<>(); - - @Before - public void init() { - for (int i = 0; i < 3; i++) { - data.put("_" + i, "_" + i); - } - } + private Map data = MockMap.mock(5, 100, 10); @Test public void testRandomValue() { - String[] keys = new String[data.size()]; + Integer[] keys = new Integer[data.size()]; data.keySet().toArray(keys); int index = ThreadLocalRandom.current().nextInt(keys.length); - String result = data.get(keys[index]); + Integer result = data.get(keys[index]); log.debug("random: result={}", result); assertThat(data.values(), hasItem(result)); From e4c6f874d169efa51e3c71bd64376f589d533cb5 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Mon, 15 Jul 2019 00:17:35 +0800 Subject: [PATCH 076/476] +) -Xlog:gc --- class/src/main/java/jvm/oom/HeapOOM.java | 2 +- class/src/test/java/jvm/oom/HeapOOMTest.java | 1 + class/src/test/java/syntax/bytes/OperatorTest.java | 12 +++++++----- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/class/src/main/java/jvm/oom/HeapOOM.java b/class/src/main/java/jvm/oom/HeapOOM.java index bafac6dc..d1f854b9 100644 --- a/class/src/main/java/jvm/oom/HeapOOM.java +++ b/class/src/main/java/jvm/oom/HeapOOM.java @@ -95,7 +95,7 @@ public void otherThreadWithOOM() throws Exception { Thread timer = new Thread(() -> { while (true) { try { - TimeUnit.MILLISECONDS.sleep(300); + TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { log.error(e.getMessage(), e); } diff --git a/class/src/test/java/jvm/oom/HeapOOMTest.java b/class/src/test/java/jvm/oom/HeapOOMTest.java index 3a0d6eae..b7d3b093 100644 --- a/class/src/test/java/jvm/oom/HeapOOMTest.java +++ b/class/src/test/java/jvm/oom/HeapOOMTest.java @@ -6,6 +6,7 @@ /** * -Xms30m -Xmx30m -XX:+PrintGCDetails + * JDK9 以上 -Xlog:gc 更清晰 * * @author kuangcp on 4/5/19-9:45 AM */ diff --git a/class/src/test/java/syntax/bytes/OperatorTest.java b/class/src/test/java/syntax/bytes/OperatorTest.java index e39245b7..74c12a5e 100644 --- a/class/src/test/java/syntax/bytes/OperatorTest.java +++ b/class/src/test/java/syntax/bytes/OperatorTest.java @@ -25,13 +25,13 @@ public void testAdd() { } @Test - public void testToInt() { + public void testIntToByte() { int a = 13232; // 直接赋值 无法编译通过 byte b = (byte) a; // int 赋值 byte 会截断后8位, 由于是有符号的, 结果是 10110000 - // 原码则是 01010000 : 所以 b是-80 + // 原码是 01010000 : 所以 b是-80 log.info("{}: {} -> {}", b, ShowBinary.toBinary(a), ShowBinary.toBinary(b)); } @@ -42,8 +42,10 @@ public void testMod() { byte b = -4; // 取余操作 a % b => a - (a / b) * b - log.info("a % b = {} \n a / b = {}\n(a / b) * b = {}\n a - (a / b) * b = {}", - a % b, a / b, (a / b) * b, a - (a / b) * b); + log.info("a % b = {} ", a % b); + log.info("a / b = {}", a / b); + log.info("(a / b) * b = {}", (a / b) * b); + log.info("a - (a / b) * b = {}", a - (a / b) * b); } @Test @@ -57,4 +59,4 @@ public void testToString() { assertThat(result, equalTo("you")); } -} +} \ No newline at end of file From 32a49992c00930b33584f014965a51a4b8a51ee2 Mon Sep 17 00:00:00 2001 From: kcp Date: Mon, 15 Jul 2019 18:56:20 +0800 Subject: [PATCH 077/476] -) remove error author --- .../src/main/java/com/github/kuangcp/hi/SimpleJobListener.java | 3 +-- java8/src/test/java/com/github/kuangcp/stream/FlatMapTest.java | 3 +-- java8/src/test/java/com/github/kuangcp/time/LocalDateTest.java | 3 +-- .../main/java/com/github/kuangcp/singleton/DoubleCheck.java | 2 +- 4 files changed, 4 insertions(+), 7 deletions(-) diff --git a/flink/src/main/java/com/github/kuangcp/hi/SimpleJobListener.java b/flink/src/main/java/com/github/kuangcp/hi/SimpleJobListener.java index 0782bf5f..2e3ad108 100644 --- a/flink/src/main/java/com/github/kuangcp/hi/SimpleJobListener.java +++ b/flink/src/main/java/com/github/kuangcp/hi/SimpleJobListener.java @@ -10,9 +10,8 @@ /** * 如果该对象能注入到 下面类的属性上, 问题就简单一些了 * - * @author kuangchengping@qipeipu.com + * @author https://github.com/kuangcp on 2019-07-08 11:22 * @see ExecutionGraph jobStatusListeners - * @since 2019-07-08 11:22 */ @Deprecated public class SimpleJobListener implements JobStatusListener { diff --git a/java8/src/test/java/com/github/kuangcp/stream/FlatMapTest.java b/java8/src/test/java/com/github/kuangcp/stream/FlatMapTest.java index e9bc8d59..62e861a9 100644 --- a/java8/src/test/java/com/github/kuangcp/stream/FlatMapTest.java +++ b/java8/src/test/java/com/github/kuangcp/stream/FlatMapTest.java @@ -14,8 +14,7 @@ import org.junit.Test; /** - * @author kuangchengping@qipeipu.com - * @since 2019-07-05 11:07 + * @author https://github.com/kuangcp on 2019-07-05 11:07 */ public class FlatMapTest { diff --git a/java8/src/test/java/com/github/kuangcp/time/LocalDateTest.java b/java8/src/test/java/com/github/kuangcp/time/LocalDateTest.java index fc077d89..e48de4a9 100644 --- a/java8/src/test/java/com/github/kuangcp/time/LocalDateTest.java +++ b/java8/src/test/java/com/github/kuangcp/time/LocalDateTest.java @@ -5,8 +5,7 @@ import org.junit.Test; /** - * @author kuangchengping@qipeipu.com - * @since 2019-07-09 17:10 + * @author https://github.com/kuangcp on 2019-07-09 17:10 */ @Slf4j public class LocalDateTest { diff --git a/pattern/src/main/java/com/github/kuangcp/singleton/DoubleCheck.java b/pattern/src/main/java/com/github/kuangcp/singleton/DoubleCheck.java index 2b64a6f9..78509ce6 100644 --- a/pattern/src/main/java/com/github/kuangcp/singleton/DoubleCheck.java +++ b/pattern/src/main/java/com/github/kuangcp/singleton/DoubleCheck.java @@ -4,7 +4,7 @@ /** * 双重校验方式 线程不安全 - * + * TODO 验证这个问题 * https://blog.csdn.net/topdeveloperr/article/details/81194654 * * @author kuangcp on 2019-04-11 12:41 PM From f6a9b7483d3a78773c1174eab9a602f413903e1a Mon Sep 17 00:00:00 2001 From: kcp Date: Thu, 18 Jul 2019 16:51:47 +0800 Subject: [PATCH 078/476] =?UTF-8?q?+)=20=E5=AE=8C=E6=88=90=20=E6=89=A7?= =?UTF-8?q?=E8=A1=8C=E5=B9=B6=E6=A3=80=E6=9F=A5=E6=89=A7=E8=A1=8CJob?= =?UTF-8?q?=E7=8A=B6=E6=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/jvm/oom/DirectMemoryOOM.java | 6 ++-- .../java/syntax/integer/IntegerCacheTest.java | 6 ++-- flink/bootstrap.sh | 31 +++++++++++++++++-- .../com/github/kuangcp/hi/SimpleSource.java | 2 +- 4 files changed, 35 insertions(+), 10 deletions(-) diff --git a/class/src/main/java/jvm/oom/DirectMemoryOOM.java b/class/src/main/java/jvm/oom/DirectMemoryOOM.java index b7bc1b1a..6db0c16b 100644 --- a/class/src/main/java/jvm/oom/DirectMemoryOOM.java +++ b/class/src/main/java/jvm/oom/DirectMemoryOOM.java @@ -4,20 +4,20 @@ * -Xmx20M -XX:MaxDirectMemorySize=10M * * TODO 瞬间内存占满 似乎该参数在8中失效了 - * TODO Java11 中删除了 sun 包, 需要找到替代方式获取直接内存 + * TODO Java11 sun包成为内置包, 需要找到替代方式获取直接内存 * * @author kuangcp on 4/4/19-12:29 AM */ public class DirectMemoryOOM { - private static final int unit = 1024 * 1024; + private static final int memoryBlock = 1024 * 1024; public static void main(String[] args) throws IllegalAccessException { // Field field = Unsafe.class.getDeclaredFields()[0]; // field.setAccessible(true); // Unsafe unsafe = (Unsafe) field.get(null); // while (true) { -// unsafe.allocateMemory(unit); +// unsafe.allocateMemory(memoryBlock); // } } } diff --git a/class/src/test/java/syntax/integer/IntegerCacheTest.java b/class/src/test/java/syntax/integer/IntegerCacheTest.java index 4a27a93e..672fbeca 100644 --- a/class/src/test/java/syntax/integer/IntegerCacheTest.java +++ b/class/src/test/java/syntax/integer/IntegerCacheTest.java @@ -40,11 +40,9 @@ public void testBoxWithNPE() { @Test public void testAvoidBoxWithNPE() { - Integer num = ThreadLocalRandom.current().nextInt(100) > 50 ? null : 1; + Integer num = null; if (Objects.equals(num, 1)) { - System.out.println("less"); - } else { - System.out.println("more"); + System.out.println("true"); } } } diff --git a/flink/bootstrap.sh b/flink/bootstrap.sh index 347d1bbc..80c6d711 100755 --- a/flink/bootstrap.sh +++ b/flink/bootstrap.sh @@ -1,8 +1,35 @@ +. ./host.conf result=$(./upload.sh) temp=${result#*flink-web-upload/} jarId=${temp%%\"*} -echo "start run: " $jarId -./run.sh $jarId +echo "\n start run: " $jarId "\n" +# 当 job 执行完成后, get请求 才会返回 +run(){ + jobResult=$(./run.sh $1) + + echo $jobResult + temp=${jobResult#*:\"} + jobId=${temp%%\"\}*} + + echo $(date) "jobId: " $jobId +} + +# 当 job 在执行的时候, 并不会在 jobs/ +jobs(){ + host=$1 + for i in $(seq 10); do + sleep 1 + echo "\n start curl " $host/jobs/ "\n" + + curl $host/jobs + + echo "" + done; +} + +jobs $host & + +run $jarId \ No newline at end of file diff --git a/flink/src/main/java/com/github/kuangcp/hi/SimpleSource.java b/flink/src/main/java/com/github/kuangcp/hi/SimpleSource.java index e5957cbd..23afafd2 100644 --- a/flink/src/main/java/com/github/kuangcp/hi/SimpleSource.java +++ b/flink/src/main/java/com/github/kuangcp/hi/SimpleSource.java @@ -44,7 +44,7 @@ List generateResource() { */ private void delayTime() { try { - TimeUnit.MILLISECONDS.sleep(100); + TimeUnit.MILLISECONDS.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } From a9916211608e2c4bac33bb4870961ecb18bde314 Mon Sep 17 00:00:00 2001 From: kcp Date: Fri, 26 Jul 2019 10:04:34 +0800 Subject: [PATCH 079/476] -) remove java11 --- java11/build.gradle | 2 - .../com/github/kuangcp/string/StringTest.java | 41 ------------------- settings.gradle | 1 - 3 files changed, 44 deletions(-) delete mode 100644 java11/build.gradle delete mode 100644 java11/src/test/java/com/github/kuangcp/string/StringTest.java diff --git a/java11/build.gradle b/java11/build.gradle deleted file mode 100644 index 13f473e4..00000000 --- a/java11/build.gradle +++ /dev/null @@ -1,2 +0,0 @@ -sourceCompatibility = 1.11 -targetCompatibility = 1.11 diff --git a/java11/src/test/java/com/github/kuangcp/string/StringTest.java b/java11/src/test/java/com/github/kuangcp/string/StringTest.java deleted file mode 100644 index 2ea7fcd3..00000000 --- a/java11/src/test/java/com/github/kuangcp/string/StringTest.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.github.kuangcp.string; - -import static org.hamcrest.Matchers.equalTo; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; - -import java.util.Objects; -import java.util.stream.Collectors; -import lombok.extern.slf4j.Slf4j; -import org.junit.Test; - -/** - * @author https://github.com/Kuangcp on 2019-06-23 15:38 - */ -@Slf4j -public class StringTest { - - @Test - public void testSimple() { - assertTrue(" ".isBlank()); - - assertThat(" Java ".strip(), equalTo("Java")); - - assertThat(" Java ".stripTrailing(), equalTo(" Java")); - assertThat(" Java ".stripLeading(), equalTo("Java ")); - - assertThat("Java".repeat(3), equalTo("JavaJavaJava")); - - assertThat("A\nB\nC".lines().count(), equalTo(3L)); - } - - @Test - public void testStream() { - String result = "1\n222\n\t23\t".lines() - .filter(Objects::nonNull) - .filter(v -> v.length() == 1) - .collect(Collectors.joining(",")); - - log.info("result={}", result); - } -} diff --git a/settings.gradle b/settings.gradle index bcac6460..92b8ebdc 100644 --- a/settings.gradle +++ b/settings.gradle @@ -11,7 +11,6 @@ include( , 'question' , 'pattern' , 'java8' - , 'java11' , 'concurrency' , 'test' , 'guava' From d5360db085efd74b9c30edef002e7074f9221e1e Mon Sep 17 00:00:00 2001 From: kcp Date: Fri, 26 Jul 2019 10:04:49 +0800 Subject: [PATCH 080/476] x) use correct path --- spring/src/test/java/com/github/kuangcp/aop/ExceptionTest.java | 2 +- .../aop/exception}/applicationContext-exception.xml | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename spring/src/test/{java/com/github/kuangcp/aop => resources/aop/exception}/applicationContext-exception.xml (100%) diff --git a/spring/src/test/java/com/github/kuangcp/aop/ExceptionTest.java b/spring/src/test/java/com/github/kuangcp/aop/ExceptionTest.java index 62634b65..2a6cd1d6 100644 --- a/spring/src/test/java/com/github/kuangcp/aop/ExceptionTest.java +++ b/spring/src/test/java/com/github/kuangcp/aop/ExceptionTest.java @@ -7,7 +7,7 @@ public class ExceptionTest extends SpringHelper { static { - path = "applicationContext-exception.xml"; + path = "aop/exception/applicationContext-exception.xml"; } @Test diff --git a/spring/src/test/java/com/github/kuangcp/aop/applicationContext-exception.xml b/spring/src/test/resources/aop/exception/applicationContext-exception.xml similarity index 100% rename from spring/src/test/java/com/github/kuangcp/aop/applicationContext-exception.xml rename to spring/src/test/resources/aop/exception/applicationContext-exception.xml From d4f8d766116391c2c9bace9a7ddef84a9452ba4e Mon Sep 17 00:00:00 2001 From: kcp Date: Fri, 26 Jul 2019 21:33:01 +0800 Subject: [PATCH 081/476] +) add mybatis module --- dependency.gradle | 8 ++++++-- .../kuangcp/stream/HandleExceptionTest.java | 17 ++++++++++------- mybatis/build.gradle | 4 ++++ .../java/com/github/kuangcp/dao/UserDao.java | 11 +++++++++++ .../java/com/github/kuangcp/domain/User.java | 15 +++++++++++++++ settings.gradle | 1 + 6 files changed, 47 insertions(+), 9 deletions(-) create mode 100644 mybatis/build.gradle create mode 100644 mybatis/src/main/java/com/github/kuangcp/dao/UserDao.java create mode 100644 mybatis/src/main/java/com/github/kuangcp/domain/User.java diff --git a/dependency.gradle b/dependency.gradle index aada9ff5..390b4a14 100644 --- a/dependency.gradle +++ b/dependency.gradle @@ -75,13 +75,17 @@ ext { , "spring-aop" : "org.springframework:spring-aop:$ver.spring" , "spring-orm" : "org.springframework:spring-orm:$ver.spring" + // mybatis + , "mybatis" : "org.mybatis:mybatis:3.5.2" + , "mybatis-plus" : "com.baomidou:mybatis-plus:3.1.2" + // kafka , "kafka-clients" : "org.apache.kafka:kafka-clients:$ver.kafka" - + // flink , "flink-java" : "org.apache.flink:flink-java:$ver.flink" , "flink-clients" : "org.apache.flink:flink-clients_2.12:$ver.flink" - , "flink-streaming-java" : "org.apache.flink:flink-streaming-java_2.12:$ver.flink" + , "flink-streaming-java" : "org.apache.flink:flink-streaming-java_2.12:$ver.flink" // hadoop , "hadoop-common" : "org.apache.hadoop:hadoop-common:$ver.hadoop" diff --git a/java8/src/test/java/com/github/kuangcp/stream/HandleExceptionTest.java b/java8/src/test/java/com/github/kuangcp/stream/HandleExceptionTest.java index 5614fffc..b245042f 100644 --- a/java8/src/test/java/com/github/kuangcp/stream/HandleExceptionTest.java +++ b/java8/src/test/java/com/github/kuangcp/stream/HandleExceptionTest.java @@ -1,5 +1,6 @@ package com.github.kuangcp.stream; +import java.util.function.IntConsumer; import java.util.stream.IntStream; import org.junit.Test; @@ -11,12 +12,14 @@ public class HandleExceptionTest { // NPE in stream @Test(expected = NullPointerException.class) public void testException() { - IntStream.rangeClosed(1, 10) - .forEach(s -> { - System.out.println(s); - if (s > 7) { - throw new NullPointerException("Oops "); - } - }); + IntConsumer consumer = v -> { + if (v > 3) { + throw new NullPointerException("Oops " + v); + } else { + System.out.println(v); + } + }; + + IntStream.rangeClosed(1, 5).forEach(consumer); } } diff --git a/mybatis/build.gradle b/mybatis/build.gradle new file mode 100644 index 00000000..5f6620bb --- /dev/null +++ b/mybatis/build.gradle @@ -0,0 +1,4 @@ +dependencies { + implementation libs["mybatis"] + implementation libs["mybatis-plus"] +} diff --git a/mybatis/src/main/java/com/github/kuangcp/dao/UserDao.java b/mybatis/src/main/java/com/github/kuangcp/dao/UserDao.java new file mode 100644 index 00000000..708646f4 --- /dev/null +++ b/mybatis/src/main/java/com/github/kuangcp/dao/UserDao.java @@ -0,0 +1,11 @@ +package com.github.kuangcp.dao; + +import com.github.kuangcp.domain.User; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; + +public interface UserDao { + + @Select({"select * from user where name like '#{name}' "}) + User queryByName(@Param("name") String name); +} diff --git a/mybatis/src/main/java/com/github/kuangcp/domain/User.java b/mybatis/src/main/java/com/github/kuangcp/domain/User.java new file mode 100644 index 00000000..ad89d9c5 --- /dev/null +++ b/mybatis/src/main/java/com/github/kuangcp/domain/User.java @@ -0,0 +1,15 @@ +package com.github.kuangcp.domain; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class User { + + private String name; +} diff --git a/settings.gradle b/settings.gradle index 92b8ebdc..21b1b7fa 100644 --- a/settings.gradle +++ b/settings.gradle @@ -19,4 +19,5 @@ include( , 'kafka' , 'hadoop' , 'flink' + , 'mybatis' ) From 78c5bdb7d502ff52e0657bda5e991c5a70263256 Mon Sep 17 00:00:00 2001 From: kcp Date: Sat, 27 Jul 2019 16:29:20 +0800 Subject: [PATCH 082/476] +) simple test loop query --- mybatis/build.gradle | 27 +++++++ mybatis/create.sql | 17 ++++ .../java/com/github/kuangcp/Application.java | 12 +++ .../kuangcp/config/MybatisPlusConfig.java | 10 +++ .../java/com/github/kuangcp/dao/UserDao.java | 11 --- .../github/kuangcp/order/dao/OrderDao.java | 8 ++ .../github/kuangcp/order/domain/Order.java | 28 +++++++ .../github/kuangcp/order/dto/OrderDTO.java | 24 ++++++ .../kuangcp/order/service/OrderService.java | 11 +++ .../order/service/impl/OrderServiceImpl.java | 79 +++++++++++++++++++ .../com/github/kuangcp/user/dao/UserDao.java | 13 +++ .../kuangcp/{ => user}/domain/User.java | 6 +- mybatis/src/main/resources/application.yml | 6 ++ .../com/github/kuangcp/base/BaseDaoTest.java | 11 +++ .../kuangcp/order/dao/OrderDaoTest.java | 65 +++++++++++++++ .../order/service/OrderServiceTest.java | 27 +++++++ .../kuangcp/user/dao/UserDaoTestTest.java | 35 ++++++++ .../kuangcp/{ => user}/domain/Person.java | 2 +- 18 files changed, 379 insertions(+), 13 deletions(-) create mode 100644 mybatis/create.sql create mode 100644 mybatis/src/main/java/com/github/kuangcp/Application.java create mode 100644 mybatis/src/main/java/com/github/kuangcp/config/MybatisPlusConfig.java delete mode 100644 mybatis/src/main/java/com/github/kuangcp/dao/UserDao.java create mode 100644 mybatis/src/main/java/com/github/kuangcp/order/dao/OrderDao.java create mode 100644 mybatis/src/main/java/com/github/kuangcp/order/domain/Order.java create mode 100644 mybatis/src/main/java/com/github/kuangcp/order/dto/OrderDTO.java create mode 100644 mybatis/src/main/java/com/github/kuangcp/order/service/OrderService.java create mode 100644 mybatis/src/main/java/com/github/kuangcp/order/service/impl/OrderServiceImpl.java create mode 100644 mybatis/src/main/java/com/github/kuangcp/user/dao/UserDao.java rename mybatis/src/main/java/com/github/kuangcp/{ => user}/domain/User.java (57%) create mode 100644 mybatis/src/main/resources/application.yml create mode 100644 mybatis/src/test/java/com/github/kuangcp/base/BaseDaoTest.java create mode 100644 mybatis/src/test/java/com/github/kuangcp/order/dao/OrderDaoTest.java create mode 100644 mybatis/src/test/java/com/github/kuangcp/order/service/OrderServiceTest.java create mode 100644 mybatis/src/test/java/com/github/kuangcp/user/dao/UserDaoTestTest.java rename test/src/main/java/com/github/kuangcp/{ => user}/domain/Person.java (78%) diff --git a/mybatis/build.gradle b/mybatis/build.gradle index 5f6620bb..4d79d185 100644 --- a/mybatis/build.gradle +++ b/mybatis/build.gradle @@ -1,4 +1,31 @@ +buildscript { + repositories { + mavenCentral() + } + dependencies { + classpath("org.springframework.boot:spring-boot-gradle-plugin:2.1.6.RELEASE") + } +} + +apply plugin: 'java' +apply plugin: 'eclipse' +apply plugin: 'idea' +apply plugin: 'org.springframework.boot' +apply plugin: 'io.spring.dependency-management' + +repositories { + mavenCentral() +} + +sourceCompatibility = 1.8 +targetCompatibility = 1.8 + dependencies { + implementation("org.springframework.boot:spring-boot-starter-web") + testImplementation("org.springframework.boot:spring-boot-starter-test") + implementation("com.baomidou:mybatis-plus-boot-starter:3.1.2") + implementation("mysql:mysql-connector-java:8.0.17") + implementation libs["mybatis"] implementation libs["mybatis-plus"] } diff --git a/mybatis/create.sql b/mybatis/create.sql new file mode 100644 index 00000000..c8a2a4db --- /dev/null +++ b/mybatis/create.sql @@ -0,0 +1,17 @@ +CREATE TABLE `user` ( + `id` bigint(20) primary key , + `name` varchar(20) DEFAULT NULL, + `nation` int(11) DEFAULT NULL +); + +create table user_order ( + id bigint(20) primary key , + user_id bigint(20), + create_time datetime, + update_time datetime, + detail varchar(30), + price decimal(6,2), + discount decimal(6,2), + count int, + pay_time datetime +) \ No newline at end of file diff --git a/mybatis/src/main/java/com/github/kuangcp/Application.java b/mybatis/src/main/java/com/github/kuangcp/Application.java new file mode 100644 index 00000000..8636bf56 --- /dev/null +++ b/mybatis/src/main/java/com/github/kuangcp/Application.java @@ -0,0 +1,12 @@ +package com.github.kuangcp; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication() +public class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } +} diff --git a/mybatis/src/main/java/com/github/kuangcp/config/MybatisPlusConfig.java b/mybatis/src/main/java/com/github/kuangcp/config/MybatisPlusConfig.java new file mode 100644 index 00000000..053fb53f --- /dev/null +++ b/mybatis/src/main/java/com/github/kuangcp/config/MybatisPlusConfig.java @@ -0,0 +1,10 @@ +package com.github.kuangcp.config; + +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.context.annotation.Configuration; + +@Configuration +@MapperScan("com.github.kuangcp.*.dao") +public class MybatisPlusConfig { + +} diff --git a/mybatis/src/main/java/com/github/kuangcp/dao/UserDao.java b/mybatis/src/main/java/com/github/kuangcp/dao/UserDao.java deleted file mode 100644 index 708646f4..00000000 --- a/mybatis/src/main/java/com/github/kuangcp/dao/UserDao.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.github.kuangcp.dao; - -import com.github.kuangcp.domain.User; -import org.apache.ibatis.annotations.Param; -import org.apache.ibatis.annotations.Select; - -public interface UserDao { - - @Select({"select * from user where name like '#{name}' "}) - User queryByName(@Param("name") String name); -} diff --git a/mybatis/src/main/java/com/github/kuangcp/order/dao/OrderDao.java b/mybatis/src/main/java/com/github/kuangcp/order/dao/OrderDao.java new file mode 100644 index 00000000..a175386d --- /dev/null +++ b/mybatis/src/main/java/com/github/kuangcp/order/dao/OrderDao.java @@ -0,0 +1,8 @@ +package com.github.kuangcp.order.dao; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.github.kuangcp.order.domain.Order; + +public interface OrderDao extends BaseMapper { + +} diff --git a/mybatis/src/main/java/com/github/kuangcp/order/domain/Order.java b/mybatis/src/main/java/com/github/kuangcp/order/domain/Order.java new file mode 100644 index 00000000..8f0a7def --- /dev/null +++ b/mybatis/src/main/java/com/github/kuangcp/order/domain/Order.java @@ -0,0 +1,28 @@ +package com.github.kuangcp.order.domain; + +import com.baomidou.mybatisplus.annotation.TableName; +import java.math.BigDecimal; +import java.time.LocalDateTime; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@TableName("user_order") +public class Order { + + private Long id; + private Long userId; + private LocalDateTime createTime; + private LocalDateTime updateTime; + private String detail; + private BigDecimal price; + private BigDecimal discount; + private Integer count; + private LocalDateTime payTime; + +} diff --git a/mybatis/src/main/java/com/github/kuangcp/order/dto/OrderDTO.java b/mybatis/src/main/java/com/github/kuangcp/order/dto/OrderDTO.java new file mode 100644 index 00000000..d38b374e --- /dev/null +++ b/mybatis/src/main/java/com/github/kuangcp/order/dto/OrderDTO.java @@ -0,0 +1,24 @@ +package com.github.kuangcp.order.dto; +import java.math.BigDecimal; +import java.time.LocalDateTime; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class OrderDTO { + private Long id; + private Long userId; + private String username; + private LocalDateTime createTime; + private LocalDateTime updateTime; + private String detail; + private BigDecimal price; + private BigDecimal discount; + private Integer count; + private LocalDateTime payTime; +} diff --git a/mybatis/src/main/java/com/github/kuangcp/order/service/OrderService.java b/mybatis/src/main/java/com/github/kuangcp/order/service/OrderService.java new file mode 100644 index 00000000..f0f445ba --- /dev/null +++ b/mybatis/src/main/java/com/github/kuangcp/order/service/OrderService.java @@ -0,0 +1,11 @@ +package com.github.kuangcp.order.service; + +import com.github.kuangcp.order.dto.OrderDTO; +import java.util.List; + +public interface OrderService { + + List queryByUserIdWithLoop(Long userId); + + List queryByUserId(Long userId); +} diff --git a/mybatis/src/main/java/com/github/kuangcp/order/service/impl/OrderServiceImpl.java b/mybatis/src/main/java/com/github/kuangcp/order/service/impl/OrderServiceImpl.java new file mode 100644 index 00000000..20107e96 --- /dev/null +++ b/mybatis/src/main/java/com/github/kuangcp/order/service/impl/OrderServiceImpl.java @@ -0,0 +1,79 @@ +package com.github.kuangcp.order.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.github.kuangcp.order.dao.OrderDao; +import com.github.kuangcp.order.domain.Order; +import com.github.kuangcp.order.dto.OrderDTO; +import com.github.kuangcp.order.service.OrderService; +import com.github.kuangcp.user.dao.UserDao; +import com.github.kuangcp.user.domain.User; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class OrderServiceImpl implements OrderService { + + @Autowired + private OrderDao orderDao; + + @Autowired + private UserDao userDao; + + @Override + public List queryByUserIdWithLoop(Long userId) { + List orders = orderDao.selectList(new QueryWrapper().eq("user_id", 200)); + + return orders.stream().map(this::convert).collect(Collectors.toList()); + } + + @Override + public List queryByUserId(Long userId) { + List orders = orderDao.selectList(new QueryWrapper().eq("user_id", 200)); + + Set idSet = orders.stream().map(Order::getUserId).collect(Collectors.toSet()); + Map userMap = userDao.selectBatchIds(idSet).stream() + .collect(Collectors.toMap(User::getId, Function.identity(), (front, next) -> next)); + + return orders.stream().map(v -> convert(v, userMap)).collect(Collectors.toList()); + } + + private OrderDTO convert(Order order, Map userMap) { + User user = userMap.get(order.getUserId()); + return OrderDTO.builder() + .count(order.getCount()) + .userId(order.getUserId()) + .username(Optional.ofNullable(user).map(User::getName).orElse("")) + .createTime(order.getCreateTime()) + .updateTime(order.getUpdateTime()) + .detail(order.getDetail()) + .price(order.getPrice()) + .discount(order.getDiscount()) + .payTime(order.getPayTime()) + .id(order.getId()) + .build(); + + } + + private OrderDTO convert(Order order) { + User user = userDao.selectById(order.getUserId()); + + return OrderDTO.builder() + .count(order.getCount()) + .userId(order.getUserId()) + .username(Optional.ofNullable(user).map(User::getName).orElse("")) + .createTime(order.getCreateTime()) + .updateTime(order.getUpdateTime()) + .detail(order.getDetail()) + .price(order.getPrice()) + .discount(order.getDiscount()) + .payTime(order.getPayTime()) + .id(order.getId()) + .build(); + } +} diff --git a/mybatis/src/main/java/com/github/kuangcp/user/dao/UserDao.java b/mybatis/src/main/java/com/github/kuangcp/user/dao/UserDao.java new file mode 100644 index 00000000..26297b06 --- /dev/null +++ b/mybatis/src/main/java/com/github/kuangcp/user/dao/UserDao.java @@ -0,0 +1,13 @@ +package com.github.kuangcp.user.dao; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.github.kuangcp.user.domain.User; +import java.util.List; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; + +public interface UserDao extends BaseMapper { + + @Select({"select * from user where name like #{name} "}) + List queryByName(@Param("name") String name); +} diff --git a/mybatis/src/main/java/com/github/kuangcp/domain/User.java b/mybatis/src/main/java/com/github/kuangcp/user/domain/User.java similarity index 57% rename from mybatis/src/main/java/com/github/kuangcp/domain/User.java rename to mybatis/src/main/java/com/github/kuangcp/user/domain/User.java index ad89d9c5..831da374 100644 --- a/mybatis/src/main/java/com/github/kuangcp/domain/User.java +++ b/mybatis/src/main/java/com/github/kuangcp/user/domain/User.java @@ -1,5 +1,6 @@ -package com.github.kuangcp.domain; +package com.github.kuangcp.user.domain; +import com.baomidou.mybatisplus.annotation.TableName; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -9,7 +10,10 @@ @Builder @NoArgsConstructor @AllArgsConstructor +@TableName("user") public class User { + private Long id; private String name; + private Integer nation; } diff --git a/mybatis/src/main/resources/application.yml b/mybatis/src/main/resources/application.yml new file mode 100644 index 00000000..8bf8a693 --- /dev/null +++ b/mybatis/src/main/resources/application.yml @@ -0,0 +1,6 @@ +spring: + datasource: + url: jdbc:mysql://127.0.0.1:3360/mybatis?autoReconnect=true&useUnicode=true&characterEncoding=utf8&useSSL=false + username: root + password: jiushi + maximum-pool-size: 30 diff --git a/mybatis/src/test/java/com/github/kuangcp/base/BaseDaoTest.java b/mybatis/src/test/java/com/github/kuangcp/base/BaseDaoTest.java new file mode 100644 index 00000000..315748b9 --- /dev/null +++ b/mybatis/src/test/java/com/github/kuangcp/base/BaseDaoTest.java @@ -0,0 +1,11 @@ +package com.github.kuangcp.base; + +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@SpringBootTest +@RunWith(SpringRunner.class) +public abstract class BaseDaoTest { + +} diff --git a/mybatis/src/test/java/com/github/kuangcp/order/dao/OrderDaoTest.java b/mybatis/src/test/java/com/github/kuangcp/order/dao/OrderDaoTest.java new file mode 100644 index 00000000..250b3561 --- /dev/null +++ b/mybatis/src/test/java/com/github/kuangcp/order/dao/OrderDaoTest.java @@ -0,0 +1,65 @@ +package com.github.kuangcp.order.dao; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.github.kuangcp.base.BaseDaoTest; +import com.github.kuangcp.mock.map.MockValue; +import com.github.kuangcp.order.domain.Order; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; + +@Slf4j +public class OrderDaoTest extends BaseDaoTest { + + @Autowired + private OrderDao orderDao; + + @Test + public void testQuery() { + Order origin = Order.builder() + .count(1) + .detail("detail") + .createTime(LocalDateTime.now()) + .payTime(LocalDateTime.now()) + .build(); + orderDao.insert(origin); + List orders = orderDao.selectList(new QueryWrapper<>()); + orders.forEach(item -> log.info("{}", item)); + } + + @Test + public void testBulkInsert() throws InterruptedException { + + Consumer consumer = start -> { + Order temp = Order.builder().count(MockValue.mock(Integer.class)) + .createTime(LocalDateTime.now()).build(); + for (int i = 0; i < 10000; i++) { + long id = (start + i); + temp.setId(id); + temp.setUserId(id % 400); + orderDao.insert(temp); + } + }; + + List threads = new ArrayList<>(); + for (int i = 0; i < 8; i++) { + int finalI = i; + threads.add(new Thread(() -> consumer.accept(500200 + finalI * 20000))); + } + + threads.forEach(v -> { + try { + v.start(); + v.join(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + }); + + Thread.yield(); + } +} \ No newline at end of file diff --git a/mybatis/src/test/java/com/github/kuangcp/order/service/OrderServiceTest.java b/mybatis/src/test/java/com/github/kuangcp/order/service/OrderServiceTest.java new file mode 100644 index 00000000..2acc7a71 --- /dev/null +++ b/mybatis/src/test/java/com/github/kuangcp/order/service/OrderServiceTest.java @@ -0,0 +1,27 @@ +package com.github.kuangcp.order.service; + +import com.github.kuangcp.base.BaseDaoTest; +import com.github.kuangcp.order.dto.OrderDTO; +import com.github.kuangcp.time.GetRunTime; +import java.util.List; +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; + +@Slf4j +public class OrderServiceTest extends BaseDaoTest { + + @Autowired + private OrderService orderService; + + @Test + public void testCompareQueryId() { + GetRunTime run = new GetRunTime().startCount(); + List result = orderService.queryByUserIdWithLoop(200L); + run.endCountOneLine("loop"); + + run.startCount(); + result = orderService.queryByUserId(200L); + run.endCountOneLine("best"); + } +} \ No newline at end of file diff --git a/mybatis/src/test/java/com/github/kuangcp/user/dao/UserDaoTestTest.java b/mybatis/src/test/java/com/github/kuangcp/user/dao/UserDaoTestTest.java new file mode 100644 index 00000000..da0bcc28 --- /dev/null +++ b/mybatis/src/test/java/com/github/kuangcp/user/dao/UserDaoTestTest.java @@ -0,0 +1,35 @@ +package com.github.kuangcp.user.dao; + +import com.github.kuangcp.base.BaseDaoTest; +import com.github.kuangcp.mock.map.MockValue; +import com.github.kuangcp.user.domain.User; +import java.util.List; +import java.util.stream.LongStream; +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; + +@Slf4j +public class UserDaoTestTest extends BaseDaoTest { + + @Autowired + private UserDao userDao; + + @Test + public void testQuery() { + userDao.insert(User.builder().name("myth").build()); + List users = userDao.queryByName("myth"); + + users.forEach(item -> log.info("item={}", item)); + } + + @Test + public void testBulkInsert(){ + LongStream.rangeClosed(1, 1000) + .mapToObj(i -> User.builder().id(i) + .id(i) + .nation(MockValue.mock(Integer.class)) + .build()) + .forEach(userDao::insert); + } +} \ No newline at end of file diff --git a/test/src/main/java/com/github/kuangcp/domain/Person.java b/test/src/main/java/com/github/kuangcp/user/domain/Person.java similarity index 78% rename from test/src/main/java/com/github/kuangcp/domain/Person.java rename to test/src/main/java/com/github/kuangcp/user/domain/Person.java index 66900b58..4e96115a 100644 --- a/test/src/main/java/com/github/kuangcp/domain/Person.java +++ b/test/src/main/java/com/github/kuangcp/user/domain/Person.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.domain; +package com.github.kuangcp.user.domain; import lombok.Data; From c2250ff8a4ed4b0f9a2805fecba013684b09cc04 Mon Sep 17 00:00:00 2001 From: kcp Date: Sat, 27 Jul 2019 16:39:23 +0800 Subject: [PATCH 083/476] *) avoid confuse for field in mysqll --- mybatis/create.sql | 19 +++++----- .../kuangcp/customer/dao/CustomerDao.java | 13 +++++++ .../domain/Customer.java} | 4 +-- .../github/kuangcp/order/domain/Order.java | 4 +-- .../github/kuangcp/order/dto/OrderDTO.java | 2 +- .../order/service/impl/OrderServiceImpl.java | 24 ++++++------- .../com/github/kuangcp/user/dao/UserDao.java | 13 ------- .../{BaseDaoTest.java => TestStarter.java} | 2 +- .../dao/CustomerDaoTestTestStarter.java | 35 +++++++++++++++++++ ...rDaoTest.java => OrderDaoTestStarter.java} | 8 ++--- ...Test.java => OrderServiceTestStarter.java} | 4 +-- .../kuangcp/user/dao/UserDaoTestTest.java | 35 ------------------- .../{user => customer}/domain/Person.java | 2 +- 13 files changed, 82 insertions(+), 83 deletions(-) create mode 100644 mybatis/src/main/java/com/github/kuangcp/customer/dao/CustomerDao.java rename mybatis/src/main/java/com/github/kuangcp/{user/domain/User.java => customer/domain/Customer.java} (82%) delete mode 100644 mybatis/src/main/java/com/github/kuangcp/user/dao/UserDao.java rename mybatis/src/test/java/com/github/kuangcp/base/{BaseDaoTest.java => TestStarter.java} (86%) create mode 100644 mybatis/src/test/java/com/github/kuangcp/customer/dao/CustomerDaoTestTestStarter.java rename mybatis/src/test/java/com/github/kuangcp/order/dao/{OrderDaoTest.java => OrderDaoTestStarter.java} (89%) rename mybatis/src/test/java/com/github/kuangcp/order/service/{OrderServiceTest.java => OrderServiceTestStarter.java} (86%) delete mode 100644 mybatis/src/test/java/com/github/kuangcp/user/dao/UserDaoTestTest.java rename test/src/main/java/com/github/kuangcp/{user => customer}/domain/Person.java (76%) diff --git a/mybatis/create.sql b/mybatis/create.sql index c8a2a4db..e7008446 100644 --- a/mybatis/create.sql +++ b/mybatis/create.sql @@ -1,17 +1,16 @@ -CREATE TABLE `user` ( - `id` bigint(20) primary key , - `name` varchar(20) DEFAULT NULL, - `nation` int(11) DEFAULT NULL +CREATE TABLE customer ( + id bigint(20) auto_increment primary key , + name varchar(20) DEFAULT NULL, + nation int(11) DEFAULT NULL ); - -create table user_order ( - id bigint(20) primary key , +create table normal_order ( + id bigint(20) auto_increment primary key , user_id bigint(20), - create_time datetime, - update_time datetime, + create_time datetime default CURRENT_TIMESTAMP, + update_time datetime default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP, detail varchar(30), price decimal(6,2), discount decimal(6,2), - count int, + num int, pay_time datetime ) \ No newline at end of file diff --git a/mybatis/src/main/java/com/github/kuangcp/customer/dao/CustomerDao.java b/mybatis/src/main/java/com/github/kuangcp/customer/dao/CustomerDao.java new file mode 100644 index 00000000..e2a89a37 --- /dev/null +++ b/mybatis/src/main/java/com/github/kuangcp/customer/dao/CustomerDao.java @@ -0,0 +1,13 @@ +package com.github.kuangcp.customer.dao; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.github.kuangcp.customer.domain.Customer; +import java.util.List; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; + +public interface CustomerDao extends BaseMapper { + + @Select({"select * from customer where name like #{name} "}) + List queryByName(@Param("name") String name); +} diff --git a/mybatis/src/main/java/com/github/kuangcp/user/domain/User.java b/mybatis/src/main/java/com/github/kuangcp/customer/domain/Customer.java similarity index 82% rename from mybatis/src/main/java/com/github/kuangcp/user/domain/User.java rename to mybatis/src/main/java/com/github/kuangcp/customer/domain/Customer.java index 831da374..4d34731b 100644 --- a/mybatis/src/main/java/com/github/kuangcp/user/domain/User.java +++ b/mybatis/src/main/java/com/github/kuangcp/customer/domain/Customer.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.user.domain; +package com.github.kuangcp.customer.domain; import com.baomidou.mybatisplus.annotation.TableName; import lombok.AllArgsConstructor; @@ -11,7 +11,7 @@ @NoArgsConstructor @AllArgsConstructor @TableName("user") -public class User { +public class Customer { private Long id; private String name; diff --git a/mybatis/src/main/java/com/github/kuangcp/order/domain/Order.java b/mybatis/src/main/java/com/github/kuangcp/order/domain/Order.java index 8f0a7def..0f1259d8 100644 --- a/mybatis/src/main/java/com/github/kuangcp/order/domain/Order.java +++ b/mybatis/src/main/java/com/github/kuangcp/order/domain/Order.java @@ -12,7 +12,7 @@ @Builder @NoArgsConstructor @AllArgsConstructor -@TableName("user_order") +@TableName("normal_order") public class Order { private Long id; @@ -22,7 +22,7 @@ public class Order { private String detail; private BigDecimal price; private BigDecimal discount; - private Integer count; + private Integer num; private LocalDateTime payTime; } diff --git a/mybatis/src/main/java/com/github/kuangcp/order/dto/OrderDTO.java b/mybatis/src/main/java/com/github/kuangcp/order/dto/OrderDTO.java index d38b374e..e0c7d8b4 100644 --- a/mybatis/src/main/java/com/github/kuangcp/order/dto/OrderDTO.java +++ b/mybatis/src/main/java/com/github/kuangcp/order/dto/OrderDTO.java @@ -19,6 +19,6 @@ public class OrderDTO { private String detail; private BigDecimal price; private BigDecimal discount; - private Integer count; + private Integer num; private LocalDateTime payTime; } diff --git a/mybatis/src/main/java/com/github/kuangcp/order/service/impl/OrderServiceImpl.java b/mybatis/src/main/java/com/github/kuangcp/order/service/impl/OrderServiceImpl.java index 20107e96..3cb74c07 100644 --- a/mybatis/src/main/java/com/github/kuangcp/order/service/impl/OrderServiceImpl.java +++ b/mybatis/src/main/java/com/github/kuangcp/order/service/impl/OrderServiceImpl.java @@ -5,8 +5,8 @@ import com.github.kuangcp.order.domain.Order; import com.github.kuangcp.order.dto.OrderDTO; import com.github.kuangcp.order.service.OrderService; -import com.github.kuangcp.user.dao.UserDao; -import com.github.kuangcp.user.domain.User; +import com.github.kuangcp.customer.dao.CustomerDao; +import com.github.kuangcp.customer.domain.Customer; import java.util.List; import java.util.Map; import java.util.Optional; @@ -23,7 +23,7 @@ public class OrderServiceImpl implements OrderService { private OrderDao orderDao; @Autowired - private UserDao userDao; + private CustomerDao customerDao; @Override public List queryByUserIdWithLoop(Long userId) { @@ -37,18 +37,18 @@ public List queryByUserId(Long userId) { List orders = orderDao.selectList(new QueryWrapper().eq("user_id", 200)); Set idSet = orders.stream().map(Order::getUserId).collect(Collectors.toSet()); - Map userMap = userDao.selectBatchIds(idSet).stream() - .collect(Collectors.toMap(User::getId, Function.identity(), (front, next) -> next)); + Map userMap = customerDao.selectBatchIds(idSet).stream() + .collect(Collectors.toMap(Customer::getId, Function.identity(), (front, next) -> next)); return orders.stream().map(v -> convert(v, userMap)).collect(Collectors.toList()); } - private OrderDTO convert(Order order, Map userMap) { - User user = userMap.get(order.getUserId()); + private OrderDTO convert(Order order, Map userMap) { + Customer customer = userMap.get(order.getUserId()); return OrderDTO.builder() - .count(order.getCount()) + .num(order.getNum()) .userId(order.getUserId()) - .username(Optional.ofNullable(user).map(User::getName).orElse("")) + .username(Optional.ofNullable(customer).map(Customer::getName).orElse("")) .createTime(order.getCreateTime()) .updateTime(order.getUpdateTime()) .detail(order.getDetail()) @@ -61,12 +61,12 @@ private OrderDTO convert(Order order, Map userMap) { } private OrderDTO convert(Order order) { - User user = userDao.selectById(order.getUserId()); + Customer customer = customerDao.selectById(order.getUserId()); return OrderDTO.builder() - .count(order.getCount()) + .num(order.getNum()) .userId(order.getUserId()) - .username(Optional.ofNullable(user).map(User::getName).orElse("")) + .username(Optional.ofNullable(customer).map(Customer::getName).orElse("")) .createTime(order.getCreateTime()) .updateTime(order.getUpdateTime()) .detail(order.getDetail()) diff --git a/mybatis/src/main/java/com/github/kuangcp/user/dao/UserDao.java b/mybatis/src/main/java/com/github/kuangcp/user/dao/UserDao.java deleted file mode 100644 index 26297b06..00000000 --- a/mybatis/src/main/java/com/github/kuangcp/user/dao/UserDao.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.github.kuangcp.user.dao; - -import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import com.github.kuangcp.user.domain.User; -import java.util.List; -import org.apache.ibatis.annotations.Param; -import org.apache.ibatis.annotations.Select; - -public interface UserDao extends BaseMapper { - - @Select({"select * from user where name like #{name} "}) - List queryByName(@Param("name") String name); -} diff --git a/mybatis/src/test/java/com/github/kuangcp/base/BaseDaoTest.java b/mybatis/src/test/java/com/github/kuangcp/base/TestStarter.java similarity index 86% rename from mybatis/src/test/java/com/github/kuangcp/base/BaseDaoTest.java rename to mybatis/src/test/java/com/github/kuangcp/base/TestStarter.java index 315748b9..6631022e 100644 --- a/mybatis/src/test/java/com/github/kuangcp/base/BaseDaoTest.java +++ b/mybatis/src/test/java/com/github/kuangcp/base/TestStarter.java @@ -6,6 +6,6 @@ @SpringBootTest @RunWith(SpringRunner.class) -public abstract class BaseDaoTest { +public abstract class TestStarter { } diff --git a/mybatis/src/test/java/com/github/kuangcp/customer/dao/CustomerDaoTestTestStarter.java b/mybatis/src/test/java/com/github/kuangcp/customer/dao/CustomerDaoTestTestStarter.java new file mode 100644 index 00000000..a0f9195a --- /dev/null +++ b/mybatis/src/test/java/com/github/kuangcp/customer/dao/CustomerDaoTestTestStarter.java @@ -0,0 +1,35 @@ +package com.github.kuangcp.customer.dao; + +import com.github.kuangcp.base.TestStarter; +import com.github.kuangcp.mock.map.MockValue; +import com.github.kuangcp.customer.domain.Customer; +import java.util.List; +import java.util.stream.LongStream; +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; + +@Slf4j +public class CustomerDaoTestTestStarter extends TestStarter { + + @Autowired + private CustomerDao customerDao; + + @Test + public void testQuery() { + customerDao.insert(Customer.builder().name("myth").build()); + List customers = customerDao.queryByName("myth"); + + customers.forEach(item -> log.info("item={}", item)); + } + + @Test + public void testBulkInsert(){ + LongStream.rangeClosed(1, 1000) + .mapToObj(i -> Customer.builder().id(i) + .id(i) + .nation(MockValue.mock(Integer.class)) + .build()) + .forEach(customerDao::insert); + } +} \ No newline at end of file diff --git a/mybatis/src/test/java/com/github/kuangcp/order/dao/OrderDaoTest.java b/mybatis/src/test/java/com/github/kuangcp/order/dao/OrderDaoTestStarter.java similarity index 89% rename from mybatis/src/test/java/com/github/kuangcp/order/dao/OrderDaoTest.java rename to mybatis/src/test/java/com/github/kuangcp/order/dao/OrderDaoTestStarter.java index 250b3561..ce8b5ba5 100644 --- a/mybatis/src/test/java/com/github/kuangcp/order/dao/OrderDaoTest.java +++ b/mybatis/src/test/java/com/github/kuangcp/order/dao/OrderDaoTestStarter.java @@ -1,7 +1,7 @@ package com.github.kuangcp.order.dao; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; -import com.github.kuangcp.base.BaseDaoTest; +import com.github.kuangcp.base.TestStarter; import com.github.kuangcp.mock.map.MockValue; import com.github.kuangcp.order.domain.Order; import java.time.LocalDateTime; @@ -13,7 +13,7 @@ import org.springframework.beans.factory.annotation.Autowired; @Slf4j -public class OrderDaoTest extends BaseDaoTest { +public class OrderDaoTestStarter extends TestStarter { @Autowired private OrderDao orderDao; @@ -21,7 +21,7 @@ public class OrderDaoTest extends BaseDaoTest { @Test public void testQuery() { Order origin = Order.builder() - .count(1) + .num(1) .detail("detail") .createTime(LocalDateTime.now()) .payTime(LocalDateTime.now()) @@ -35,7 +35,7 @@ public void testQuery() { public void testBulkInsert() throws InterruptedException { Consumer consumer = start -> { - Order temp = Order.builder().count(MockValue.mock(Integer.class)) + Order temp = Order.builder().num(MockValue.mock(Integer.class)) .createTime(LocalDateTime.now()).build(); for (int i = 0; i < 10000; i++) { long id = (start + i); diff --git a/mybatis/src/test/java/com/github/kuangcp/order/service/OrderServiceTest.java b/mybatis/src/test/java/com/github/kuangcp/order/service/OrderServiceTestStarter.java similarity index 86% rename from mybatis/src/test/java/com/github/kuangcp/order/service/OrderServiceTest.java rename to mybatis/src/test/java/com/github/kuangcp/order/service/OrderServiceTestStarter.java index 2acc7a71..8568227b 100644 --- a/mybatis/src/test/java/com/github/kuangcp/order/service/OrderServiceTest.java +++ b/mybatis/src/test/java/com/github/kuangcp/order/service/OrderServiceTestStarter.java @@ -1,6 +1,6 @@ package com.github.kuangcp.order.service; -import com.github.kuangcp.base.BaseDaoTest; +import com.github.kuangcp.base.TestStarter; import com.github.kuangcp.order.dto.OrderDTO; import com.github.kuangcp.time.GetRunTime; import java.util.List; @@ -9,7 +9,7 @@ import org.springframework.beans.factory.annotation.Autowired; @Slf4j -public class OrderServiceTest extends BaseDaoTest { +public class OrderServiceTestStarter extends TestStarter { @Autowired private OrderService orderService; diff --git a/mybatis/src/test/java/com/github/kuangcp/user/dao/UserDaoTestTest.java b/mybatis/src/test/java/com/github/kuangcp/user/dao/UserDaoTestTest.java deleted file mode 100644 index da0bcc28..00000000 --- a/mybatis/src/test/java/com/github/kuangcp/user/dao/UserDaoTestTest.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.github.kuangcp.user.dao; - -import com.github.kuangcp.base.BaseDaoTest; -import com.github.kuangcp.mock.map.MockValue; -import com.github.kuangcp.user.domain.User; -import java.util.List; -import java.util.stream.LongStream; -import lombok.extern.slf4j.Slf4j; -import org.junit.Test; -import org.springframework.beans.factory.annotation.Autowired; - -@Slf4j -public class UserDaoTestTest extends BaseDaoTest { - - @Autowired - private UserDao userDao; - - @Test - public void testQuery() { - userDao.insert(User.builder().name("myth").build()); - List users = userDao.queryByName("myth"); - - users.forEach(item -> log.info("item={}", item)); - } - - @Test - public void testBulkInsert(){ - LongStream.rangeClosed(1, 1000) - .mapToObj(i -> User.builder().id(i) - .id(i) - .nation(MockValue.mock(Integer.class)) - .build()) - .forEach(userDao::insert); - } -} \ No newline at end of file diff --git a/test/src/main/java/com/github/kuangcp/user/domain/Person.java b/test/src/main/java/com/github/kuangcp/customer/domain/Person.java similarity index 76% rename from test/src/main/java/com/github/kuangcp/user/domain/Person.java rename to test/src/main/java/com/github/kuangcp/customer/domain/Person.java index 4e96115a..d9cdc851 100644 --- a/test/src/main/java/com/github/kuangcp/user/domain/Person.java +++ b/test/src/main/java/com/github/kuangcp/customer/domain/Person.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.user.domain; +package com.github.kuangcp.customer.domain; import lombok.Data; From 067b101c49791aba5e91f8071a89c90d8e325b98 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Sun, 28 Jul 2019 14:49:33 +0800 Subject: [PATCH 084/476] *) optimized design --- LICENSE | 2 +- gui/build.gradle | 12 ++ .../main/java/com/github/kuangcp/README.md | 7 +- .../java/com/github/kuangcp/notepad/Note.java | 161 +++++------------- .../ActionCommand.java} | 5 +- .../kuangcp/notepad/base/HandlerType.java | 9 + .../kuangcp/notepad/handler/BaseHandler.java | 11 ++ .../kuangcp/notepad/handler/FileHandler.java | 124 ++++++++++++++ .../handler/NotepadActionListener.java | 34 ++++ .../github/kuangcp/order/domain/Order.java | 1 - 10 files changed, 247 insertions(+), 119 deletions(-) mode change 100755 => 100644 LICENSE rename gui/src/main/java/com/github/kuangcp/notepad/{ICommand.java => base/ActionCommand.java} (51%) create mode 100644 gui/src/main/java/com/github/kuangcp/notepad/base/HandlerType.java create mode 100644 gui/src/main/java/com/github/kuangcp/notepad/handler/BaseHandler.java create mode 100644 gui/src/main/java/com/github/kuangcp/notepad/handler/FileHandler.java create mode 100644 gui/src/main/java/com/github/kuangcp/notepad/handler/NotepadActionListener.java diff --git a/LICENSE b/LICENSE old mode 100755 new mode 100644 index 9578352c..5e851920 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2019 kuangcp +Copyright (c) 2017 kuangcp Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/gui/build.gradle b/gui/build.gradle index e69de29b..0974dde3 100644 --- a/gui/build.gradle +++ b/gui/build.gradle @@ -0,0 +1,12 @@ +task uberJar(type: Jar) { + archiveClassifier = 'all-dependency' + + from sourceSets.main.output + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith('jar') }.collect { zipTree(it) } + } + manifest { + attributes 'Main-Class': 'com.github.kuangcp.notepad.Note' + } +} \ No newline at end of file diff --git a/gui/src/main/java/com/github/kuangcp/README.md b/gui/src/main/java/com/github/kuangcp/README.md index 752d668d..f0b1d29b 100644 --- a/gui/src/main/java/com/github/kuangcp/README.md +++ b/gui/src/main/java/com/github/kuangcp/README.md @@ -1,2 +1,7 @@ # GUI -> 图形化界面 \ No newline at end of file +> 图形化界面 + +1. notepad +1. calculator + + \ No newline at end of file diff --git a/gui/src/main/java/com/github/kuangcp/notepad/Note.java b/gui/src/main/java/com/github/kuangcp/notepad/Note.java index bd197058..3e5415e3 100644 --- a/gui/src/main/java/com/github/kuangcp/notepad/Note.java +++ b/gui/src/main/java/com/github/kuangcp/notepad/Note.java @@ -1,23 +1,20 @@ package com.github.kuangcp.notepad; -import com.github.kuangcp.io.ResourceTool; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.io.BufferedReader; -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileReader; -import java.io.FileWriter; -import java.io.IOException; -import java.util.Objects; -import javax.swing.ImageIcon; -import javax.swing.JFileChooser; +import static java.awt.event.InputEvent.CTRL_MASK; +import static java.awt.event.KeyEvent.VK_S; + +import com.github.kuangcp.notepad.base.ActionCommand; +import com.github.kuangcp.notepad.base.HandlerType; +import com.github.kuangcp.notepad.handler.NotepadActionListener; +import java.awt.Color; +import java.awt.Font; import javax.swing.JFrame; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.JScrollPane; import javax.swing.JTextArea; +import javax.swing.KeyStroke; import javax.swing.WindowConstants; import lombok.extern.slf4j.Slf4j; @@ -25,119 +22,55 @@ * Created by https://github.com/kuangcp on 17-8-22 下午2:39 */ @Slf4j -class Note extends JFrame implements ActionListener { +public +class Note extends JFrame { - private JTextArea textArea; + public static JTextArea textArea = new JTextArea(); public static void main(String[] args) { - new Note().init(); + float[] hsbArray = Color.RGBtoHSB(60, 63, 65, null); + textArea.setBackground(Color.getHSBColor(hsbArray[0], hsbArray[1], hsbArray[2])); + textArea.setForeground(Color.LIGHT_GRAY); + textArea.setFont(Font.decode("'IBM Plex Mono' 18")); + new Note().run(); } - private void init() { - textArea = new JTextArea(); - JMenuBar menuBar = new JMenuBar(); - this.setJMenuBar(menuBar); - - JMenu fileMenu = new JMenu("文件"); - fileMenu.setMnemonic('F'); - - JMenu saveMenu = new JMenu("保存"); - - JMenuItem jmi1 = new JMenuItem("打开", new ImageIcon()); - JMenuItem jmi2 = new JMenuItem("保存"); + private void run() { + createMenuBar(); - //注册监听 - jmi1.addActionListener(this); - jmi1.setActionCommand(ICommand.OPEN_FILE); - jmi2.addActionListener(this); - jmi2.setActionCommand(ICommand.SAVE_FILE); - - //把jm1放入jmb - menuBar.add(fileMenu); - menuBar.add(saveMenu); - - //把item放入到Menu - fileMenu.add(jmi1); - saveMenu.add(jmi2); - - JScrollPane jsp = new JScrollPane(textArea); - - this.add(jsp); + JScrollPane scrollPane = new JScrollPane(textArea); + this.add(scrollPane); this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); - this.setSize(400, 300); + this.setSize(500, 400); this.setVisible(true); } - public void actionPerformed(ActionEvent event) { - switch (event.getActionCommand()) { - case ICommand.OPEN_FILE: - openFile(); - break; - case ICommand.SAVE_FILE: - saveFile(); - break; - default: - log.info("not supported command"); - break; - } - } - - private void openFile() { - //推荐JFileChooser 组件 - JFileChooser jc = new JFileChooser(); - //设置名字 - jc.setDialogTitle("请选择文件..."); - //默认方式 - jc.showOpenDialog(null); - jc.setVisible(true); - - //得知道用户选择的文件 绝对路径 - String filePath = jc.getSelectedFile().getAbsolutePath(); - BufferedReader br = null; - try { - br = new BufferedReader(new FileReader(filePath)); - - //从文件读取信息显示到jta - String s; - StringBuilder result = new StringBuilder(); - while ((s = br.readLine()) != null) { - result.append(s).append("\n"); - } - - //输出到 textArea - textArea.setText(result.toString()); - } catch (Exception e) { - log.error(e.getMessage(), e); - } finally { - try { - ResourceTool.close(br); - } catch (IOException e) { - log.error(e.getMessage(), e); - } - } - } - - private void saveFile() { - JFileChooser chooser = new JFileChooser(); - chooser.setDialogTitle("另存为..."); - - //按默认方式显示 - chooser.showSaveDialog(null); - chooser.setVisible(true); + private void createMenuBar() { + JMenuBar menuBar = new JMenuBar(); + this.setJMenuBar(menuBar); - //得到用户希望把文件保存到何处 - File file = chooser.getSelectedFile(); - if (Objects.isNull(file)) { - log.warn("please select file: chooser={}", chooser); - return; - } + JMenu fileMenu = new JMenu("File"); + fileMenu.setMnemonic('F'); + menuBar.add(fileMenu); - //准备写入到指定目录下 - String path = file.getAbsolutePath(); - try (BufferedWriter bw = new BufferedWriter(new FileWriter(path))) { - bw.write(this.textArea.getText()); - } catch (Exception e) { - log.error(e.getMessage(), e); - } + JMenuItem openItem = new JMenuItem("Open"); + openItem.setName(HandlerType.FILE); + openItem.addActionListener(NotepadActionListener.LISTENER); + openItem.setActionCommand(ActionCommand.OPEN_FILE); + + JMenuItem saveAsItem = new JMenuItem("Save"); + saveAsItem.setName(HandlerType.FILE); + saveAsItem.addActionListener(NotepadActionListener.LISTENER); + saveAsItem.setActionCommand(ActionCommand.SAVE_FILE); + + JMenuItem saveItem = new JMenuItem("Save"); + saveItem.setName(HandlerType.FILE); + saveItem.addActionListener(NotepadActionListener.LISTENER); + saveItem.setActionCommand(ActionCommand.SAVE_FILE); + saveItem.setAccelerator(KeyStroke.getKeyStroke(VK_S, CTRL_MASK)); + + fileMenu.add(openItem); + fileMenu.add(saveAsItem); + menuBar.add(saveItem); } } diff --git a/gui/src/main/java/com/github/kuangcp/notepad/ICommand.java b/gui/src/main/java/com/github/kuangcp/notepad/base/ActionCommand.java similarity index 51% rename from gui/src/main/java/com/github/kuangcp/notepad/ICommand.java rename to gui/src/main/java/com/github/kuangcp/notepad/base/ActionCommand.java index b76b404f..45017544 100644 --- a/gui/src/main/java/com/github/kuangcp/notepad/ICommand.java +++ b/gui/src/main/java/com/github/kuangcp/notepad/base/ActionCommand.java @@ -1,10 +1,11 @@ -package com.github.kuangcp.notepad; +package com.github.kuangcp.notepad.base; /** * @author kuangcp on 2019-04-25 1:55 PM */ -public interface ICommand { +public interface ActionCommand { String OPEN_FILE = "OPEN_FILE"; String SAVE_FILE = "SAVE_FILE"; + String SAVE_AS_FILE = "SAVE_AS_FILE"; } diff --git a/gui/src/main/java/com/github/kuangcp/notepad/base/HandlerType.java b/gui/src/main/java/com/github/kuangcp/notepad/base/HandlerType.java new file mode 100644 index 00000000..1dca998c --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/notepad/base/HandlerType.java @@ -0,0 +1,9 @@ +package com.github.kuangcp.notepad.base; + +/** + * @author https://github.com/kuangcp on 2019-07-28 13:11 + */ +public interface HandlerType { + + String FILE = "FILE"; +} diff --git a/gui/src/main/java/com/github/kuangcp/notepad/handler/BaseHandler.java b/gui/src/main/java/com/github/kuangcp/notepad/handler/BaseHandler.java new file mode 100644 index 00000000..71be22c9 --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/notepad/handler/BaseHandler.java @@ -0,0 +1,11 @@ +package com.github.kuangcp.notepad.handler; + +import java.awt.event.ActionEvent; + +/** + * @author https://github.com/kuangcp on 2019-07-28 13:16 + */ +abstract class BaseHandler { + + abstract void handle(ActionEvent event); +} diff --git a/gui/src/main/java/com/github/kuangcp/notepad/handler/FileHandler.java b/gui/src/main/java/com/github/kuangcp/notepad/handler/FileHandler.java new file mode 100644 index 00000000..8f20704e --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/notepad/handler/FileHandler.java @@ -0,0 +1,124 @@ +package com.github.kuangcp.notepad.handler; + +import com.github.kuangcp.io.ResourceTool; +import com.github.kuangcp.notepad.Note; +import com.github.kuangcp.notepad.base.ActionCommand; +import java.awt.event.ActionEvent; +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.util.Objects; +import java.util.Optional; +import javax.swing.JFileChooser; +import lombok.extern.slf4j.Slf4j; + +/** + * @author https://github.com/kuangcp on 2019-07-28 13:10 + */ +@Slf4j +class FileHandler extends BaseHandler { + + private String currentPath; + + @Override + void handle(ActionEvent event) { + switch (event.getActionCommand()) { + case ActionCommand.OPEN_FILE: + openFile(); + break; + case ActionCommand.SAVE_FILE: + saveFile(); + break; + case ActionCommand.SAVE_AS_FILE: + saveAsFile(); + break; + default: + log.info("not supported command"); + break; + } + } + + private void openFile() { + //推荐JFileChooser 组件 + JFileChooser jc = new JFileChooser(); + //设置名字 + jc.setDialogTitle("Please select file..."); + //默认方式 + jc.showOpenDialog(null); + jc.setVisible(true); + + //得知道用户选择的文件 绝对路径 + File selectedFile = jc.getSelectedFile(); + if (Objects.isNull(selectedFile)) { + log.warn("not select any file"); + return; + } + + String filePath = selectedFile.getAbsolutePath(); + currentPath = filePath; + BufferedReader br = null; + try { + br = new BufferedReader(new FileReader(filePath)); + + //从文件读取信息显示到jta + String s; + StringBuilder result = new StringBuilder(); + while ((s = br.readLine()) != null) { + result.append(s).append("\n"); + } + + //输出到 textArea + Note.textArea.setText(result.toString()); + } catch (Exception e) { + log.error(e.getMessage(), e); + } finally { + try { + ResourceTool.close(br); + } catch (IOException e) { + log.error(e.getMessage(), e); + } + } + } + + private void saveFile() { + if (Objects.isNull(currentPath)) { + log.warn("not open any file"); + Optional pathOpt = saveAsFile(); + pathOpt.ifPresent(v -> currentPath = v); + return; + } + try (BufferedWriter bw = new BufferedWriter(new FileWriter(currentPath))) { + bw.write(Note.textArea.getText()); + } catch (Exception e) { + log.error(e.getMessage(), e); + } + } + + private Optional saveAsFile() { + JFileChooser chooser = new JFileChooser(); + chooser.setDialogTitle("Save as "); + + //按默认方式显示 + chooser.showSaveDialog(null); + chooser.setVisible(true); + + //得到用户希望把文件保存到何处 + File file = chooser.getSelectedFile(); + if (Objects.isNull(file)) { + log.warn("please select file: chooser={}", chooser); + return Optional.empty(); + } + + //准备写入到指定目录下 + String path = file.getAbsolutePath(); + try (BufferedWriter bw = new BufferedWriter(new FileWriter(path))) { + bw.write(Note.textArea.getText()); + } catch (Exception e) { + log.error(e.getMessage(), e); + } + return Optional.of(path); + } +} diff --git a/gui/src/main/java/com/github/kuangcp/notepad/handler/NotepadActionListener.java b/gui/src/main/java/com/github/kuangcp/notepad/handler/NotepadActionListener.java new file mode 100644 index 00000000..e75e0271 --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/notepad/handler/NotepadActionListener.java @@ -0,0 +1,34 @@ +package com.github.kuangcp.notepad.handler; + +import com.github.kuangcp.notepad.base.HandlerType; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import javax.swing.JMenuItem; +import lombok.extern.slf4j.Slf4j; + +/** + * @author https://github.com/kuangcp on 2019-07-28 13:16 + */ +@Slf4j +public class NotepadActionListener implements ActionListener { + + public static final NotepadActionListener LISTENER = new NotepadActionListener(); + private static final Map handlerMap = new HashMap<>(); + + static { + handlerMap.put(HandlerType.FILE, new FileHandler()); + } + + @Override + public void actionPerformed(ActionEvent event) { + log.info(": event={}", event); + Object source = event.getSource(); + if (source instanceof JMenuItem) { + String name = ((JMenuItem) source).getName(); + Optional.ofNullable(handlerMap.get(name)).ifPresent(v -> v.handle(event)); + } + } +} diff --git a/mybatis/src/main/java/com/github/kuangcp/order/domain/Order.java b/mybatis/src/main/java/com/github/kuangcp/order/domain/Order.java index 0f1259d8..e79add5c 100644 --- a/mybatis/src/main/java/com/github/kuangcp/order/domain/Order.java +++ b/mybatis/src/main/java/com/github/kuangcp/order/domain/Order.java @@ -24,5 +24,4 @@ public class Order { private BigDecimal discount; private Integer num; private LocalDateTime payTime; - } From 48b498732aa8633648755821350cd7fbb239b340 Mon Sep 17 00:00:00 2001 From: kcp Date: Mon, 29 Jul 2019 13:31:30 +0800 Subject: [PATCH 085/476] *) rename test class --- gui/src/main/java/com/github/kuangcp/notepad/Note.java | 4 ++-- .../github/kuangcp/notepad/handler/NotepadActionListener.java | 2 +- .../{CustomerDaoTestTestStarter.java => CustomerDaoTest.java} | 2 +- .../order/dao/{OrderDaoTestStarter.java => OrderDaoTest.java} | 2 +- .../{OrderServiceTestStarter.java => OrderServiceTest.java} | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) rename mybatis/src/test/java/com/github/kuangcp/customer/dao/{CustomerDaoTestTestStarter.java => CustomerDaoTest.java} (93%) rename mybatis/src/test/java/com/github/kuangcp/order/dao/{OrderDaoTestStarter.java => OrderDaoTest.java} (96%) rename mybatis/src/test/java/com/github/kuangcp/order/service/{OrderServiceTestStarter.java => OrderServiceTest.java} (92%) diff --git a/gui/src/main/java/com/github/kuangcp/notepad/Note.java b/gui/src/main/java/com/github/kuangcp/notepad/Note.java index 3e5415e3..017e41a0 100644 --- a/gui/src/main/java/com/github/kuangcp/notepad/Note.java +++ b/gui/src/main/java/com/github/kuangcp/notepad/Note.java @@ -58,10 +58,10 @@ private void createMenuBar() { openItem.addActionListener(NotepadActionListener.LISTENER); openItem.setActionCommand(ActionCommand.OPEN_FILE); - JMenuItem saveAsItem = new JMenuItem("Save"); + JMenuItem saveAsItem = new JMenuItem("Save as"); saveAsItem.setName(HandlerType.FILE); saveAsItem.addActionListener(NotepadActionListener.LISTENER); - saveAsItem.setActionCommand(ActionCommand.SAVE_FILE); + saveAsItem.setActionCommand(ActionCommand.SAVE_AS_FILE); JMenuItem saveItem = new JMenuItem("Save"); saveItem.setName(HandlerType.FILE); diff --git a/gui/src/main/java/com/github/kuangcp/notepad/handler/NotepadActionListener.java b/gui/src/main/java/com/github/kuangcp/notepad/handler/NotepadActionListener.java index e75e0271..a3c223af 100644 --- a/gui/src/main/java/com/github/kuangcp/notepad/handler/NotepadActionListener.java +++ b/gui/src/main/java/com/github/kuangcp/notepad/handler/NotepadActionListener.java @@ -24,7 +24,7 @@ public class NotepadActionListener implements ActionListener { @Override public void actionPerformed(ActionEvent event) { - log.info(": event={}", event); + log.debug(": event={}", event); Object source = event.getSource(); if (source instanceof JMenuItem) { String name = ((JMenuItem) source).getName(); diff --git a/mybatis/src/test/java/com/github/kuangcp/customer/dao/CustomerDaoTestTestStarter.java b/mybatis/src/test/java/com/github/kuangcp/customer/dao/CustomerDaoTest.java similarity index 93% rename from mybatis/src/test/java/com/github/kuangcp/customer/dao/CustomerDaoTestTestStarter.java rename to mybatis/src/test/java/com/github/kuangcp/customer/dao/CustomerDaoTest.java index a0f9195a..5de4b676 100644 --- a/mybatis/src/test/java/com/github/kuangcp/customer/dao/CustomerDaoTestTestStarter.java +++ b/mybatis/src/test/java/com/github/kuangcp/customer/dao/CustomerDaoTest.java @@ -10,7 +10,7 @@ import org.springframework.beans.factory.annotation.Autowired; @Slf4j -public class CustomerDaoTestTestStarter extends TestStarter { +public class CustomerDaoTest extends TestStarter { @Autowired private CustomerDao customerDao; diff --git a/mybatis/src/test/java/com/github/kuangcp/order/dao/OrderDaoTestStarter.java b/mybatis/src/test/java/com/github/kuangcp/order/dao/OrderDaoTest.java similarity index 96% rename from mybatis/src/test/java/com/github/kuangcp/order/dao/OrderDaoTestStarter.java rename to mybatis/src/test/java/com/github/kuangcp/order/dao/OrderDaoTest.java index ce8b5ba5..7a8f5cdc 100644 --- a/mybatis/src/test/java/com/github/kuangcp/order/dao/OrderDaoTestStarter.java +++ b/mybatis/src/test/java/com/github/kuangcp/order/dao/OrderDaoTest.java @@ -13,7 +13,7 @@ import org.springframework.beans.factory.annotation.Autowired; @Slf4j -public class OrderDaoTestStarter extends TestStarter { +public class OrderDaoTest extends TestStarter { @Autowired private OrderDao orderDao; diff --git a/mybatis/src/test/java/com/github/kuangcp/order/service/OrderServiceTestStarter.java b/mybatis/src/test/java/com/github/kuangcp/order/service/OrderServiceTest.java similarity index 92% rename from mybatis/src/test/java/com/github/kuangcp/order/service/OrderServiceTestStarter.java rename to mybatis/src/test/java/com/github/kuangcp/order/service/OrderServiceTest.java index 8568227b..f90a80ba 100644 --- a/mybatis/src/test/java/com/github/kuangcp/order/service/OrderServiceTestStarter.java +++ b/mybatis/src/test/java/com/github/kuangcp/order/service/OrderServiceTest.java @@ -9,7 +9,7 @@ import org.springframework.beans.factory.annotation.Autowired; @Slf4j -public class OrderServiceTestStarter extends TestStarter { +public class OrderServiceTest extends TestStarter { @Autowired private OrderService orderService; From 8ec118524bf3161c4a3d7f9f41d9897c8eb6343a Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Tue, 30 Jul 2019 00:48:57 +0800 Subject: [PATCH 086/476] x) remove useless config, fixed error table name --- .../src/main/java/com/github/kuangcp/Application.java | 2 ++ .../com/github/kuangcp/config/MybatisPlusConfig.java | 10 ---------- .../com/github/kuangcp/customer/domain/Customer.java | 2 +- 3 files changed, 3 insertions(+), 11 deletions(-) delete mode 100644 mybatis/src/main/java/com/github/kuangcp/config/MybatisPlusConfig.java diff --git a/mybatis/src/main/java/com/github/kuangcp/Application.java b/mybatis/src/main/java/com/github/kuangcp/Application.java index 8636bf56..c5675c5c 100644 --- a/mybatis/src/main/java/com/github/kuangcp/Application.java +++ b/mybatis/src/main/java/com/github/kuangcp/Application.java @@ -1,8 +1,10 @@ package com.github.kuangcp; +import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +@MapperScan("com.github.kuangcp.*.dao") @SpringBootApplication() public class Application { diff --git a/mybatis/src/main/java/com/github/kuangcp/config/MybatisPlusConfig.java b/mybatis/src/main/java/com/github/kuangcp/config/MybatisPlusConfig.java deleted file mode 100644 index 053fb53f..00000000 --- a/mybatis/src/main/java/com/github/kuangcp/config/MybatisPlusConfig.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.github.kuangcp.config; - -import org.mybatis.spring.annotation.MapperScan; -import org.springframework.context.annotation.Configuration; - -@Configuration -@MapperScan("com.github.kuangcp.*.dao") -public class MybatisPlusConfig { - -} diff --git a/mybatis/src/main/java/com/github/kuangcp/customer/domain/Customer.java b/mybatis/src/main/java/com/github/kuangcp/customer/domain/Customer.java index 4d34731b..a2f4530f 100644 --- a/mybatis/src/main/java/com/github/kuangcp/customer/domain/Customer.java +++ b/mybatis/src/main/java/com/github/kuangcp/customer/domain/Customer.java @@ -10,7 +10,7 @@ @Builder @NoArgsConstructor @AllArgsConstructor -@TableName("user") +@TableName("customer") public class Customer { private Long id; From a069b691bb56533f864f62abb7460a0b9eb325f0 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Tue, 30 Jul 2019 01:21:11 +0800 Subject: [PATCH 087/476] *) optimized --- .../com/github/kuangcp/read/ClassScanner.java | 72 ++++++++++--------- .../github/kuangcp/caculator/Calculator.java | 37 +++++----- 2 files changed, 53 insertions(+), 56 deletions(-) diff --git a/class/src/main/java/com/github/kuangcp/read/ClassScanner.java b/class/src/main/java/com/github/kuangcp/read/ClassScanner.java index 683e3c35..dee2f63c 100644 --- a/class/src/main/java/com/github/kuangcp/read/ClassScanner.java +++ b/class/src/main/java/com/github/kuangcp/read/ClassScanner.java @@ -6,9 +6,9 @@ import java.net.URL; import java.net.URLDecoder; import java.util.Enumeration; -import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; +import java.util.Objects; import java.util.Set; import java.util.jar.JarEntry; import java.util.jar.JarFile; @@ -16,8 +16,7 @@ import lombok.NoArgsConstructor; /** - * Created by https://github.com/kuangcp - * 读取类 是一个工具类 + * Created by https://github.com/kuangcp 读取类 是一个工具类 * * @author kuangcp */ @@ -88,24 +87,25 @@ private void doScanPackageClassesByFile(Set> classes, String packageNam && ClassScanner.this.filterClassName(filename); } }); - if (files != null) { - for (File file : files) { - if (file.isDirectory()) { - this.doScanPackageClassesByFile(classes, packageName + "." + file.getName(), - file.getAbsolutePath(), recursive); - } else { - String className = file.getName().substring(0, file.getName().length() - 6); - - try { - classes.add(Thread.currentThread().getContextClassLoader() - .loadClass(packageName + '.' + className)); - } catch (ClassNotFoundException var14) { - var14.printStackTrace(); - throw new InternalError("过滤失败"); - } + if (Objects.isNull(files)) { + return; + } + + for (File file : files) { + if (file.isDirectory()) { + this.doScanPackageClassesByFile(classes, packageName + "." + file.getName(), + file.getAbsolutePath(), recursive); + } else { + String className = file.getName().substring(0, file.getName().length() - 6); + + try { + classes.add(Thread.currentThread().getContextClassLoader() + .loadClass(packageName + '.' + className)); + } catch (ClassNotFoundException var14) { + var14.printStackTrace(); + throw new InternalError("过滤失败"); } } - } } } @@ -127,7 +127,9 @@ private void doScanPackageClassesByJar(String basePackage, URL url, boolean recu do { JarEntry entry; do { + // 目标包下具体类 非目录 do { + // 找到目标包 do { if (!entries.hasMoreElements()) { return; @@ -162,23 +164,23 @@ private void doScanPackageClassesByJar(String basePackage, URL url, boolean recu private boolean filterClassName(String className) { if (!className.endsWith(".class")) { return false; - } else if (null != this.classFilters && !this.classFilters.isEmpty()) { - String tmpName = className.substring(0, className.length() - 6); - boolean flag = false; - Iterator var4 = this.classFilters.iterator(); - - while (var4.hasNext()) { - String str = (String) var4.next(); - String tmpreg = "^" + str.replace("*", ".*") + "$"; - Pattern p = Pattern.compile(tmpreg); - if (p.matcher(tmpName).find()) { - flag = true; - break; - } - } - return this.checkInOrEx && flag || !this.checkInOrEx && !flag; - } else { + } + + if (Objects.isNull(classFilters) || this.classFilters.isEmpty()) { return true; } + + String tmpName = className.substring(0, className.length() - 6); + boolean flag = false; + + for (String str : this.classFilters) { + String tmpreg = "^" + str.replace("*", ".*") + "$"; + Pattern p = Pattern.compile(tmpreg); + if (p.matcher(tmpName).find()) { + flag = true; + break; + } + } + return this.checkInOrEx && flag || !this.checkInOrEx && !flag; } } \ No newline at end of file diff --git a/gui/src/main/java/com/github/kuangcp/caculator/Calculator.java b/gui/src/main/java/com/github/kuangcp/caculator/Calculator.java index da7f316c..f1bd7ccd 100644 --- a/gui/src/main/java/com/github/kuangcp/caculator/Calculator.java +++ b/gui/src/main/java/com/github/kuangcp/caculator/Calculator.java @@ -7,6 +7,7 @@ import java.awt.event.ActionEvent; import java.util.Objects; import javax.swing.JButton; +import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JTextField; @@ -14,8 +15,7 @@ import javax.swing.WindowConstants; /** - * 搜到的一个计算器实现代码 - * TODO 思考如何重构 + * 搜到的一个计算器实现代码 TODO 思考如何重构 */ public class Calculator extends JFrame { @@ -49,6 +49,8 @@ public class Calculator extends JFrame { private JButton btnEight = new JButton("8"); private JButton btnNine = new JButton("9"); + private Font dialogFont = new Font("Dialog", Font.PLAIN, 16); + private Calculator() { try { setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); @@ -65,7 +67,6 @@ private void initButton() { setSize(new Dimension(400, 300)); setTitle("计算器"); txtResult.setEnabled(false); - txtResult.setFont(new Font("Dialog", Font.PLAIN, 20)); txtResult.setEditable(false); txtResult.setHorizontalAlignment(SwingConstants.RIGHT); @@ -75,43 +76,36 @@ private void initButton() { //btnNull.addActionListener(new FrameCalculate_btnNull_actionAdapter(this)); btnFour.setBounds(new Rectangle(33, 120, 46, 37)); - btnFour.setFont(new Font("Dialog", Font.PLAIN, 20)); btnFive.setBounds(new Rectangle(101, 120, 46, 37)); - btnFive.setFont(new Font("Dialog", Font.PLAIN, 20)); btnSix.setBounds(new Rectangle(167, 119, 46, 37)); - btnSix.setFont(new Font("Dialog", Font.PLAIN, 20)); btnDecrease.setBounds(new Rectangle(234, 120, 46, 37)); - btnDecrease.setFont(new Font("Dialog", Font.PLAIN, 20)); btnBegin.setBounds(new Rectangle(298, 121, 46, 37)); - btnBegin.setFont(new Font("Dialog", Font.PLAIN, 15)); btnBegin.addActionListener(new Calculate_btnBegin_actionAdapter(this)); btnOne.setBounds(new Rectangle(33, 172, 46, 37)); - btnOne.setFont(new Font("Dialog", Font.PLAIN, 20)); btnTwo.setBounds(new Rectangle(101, 172, 46, 37)); - btnTwo.setFont(new Font("Dialog", Font.PLAIN, 20)); btnThree.setBounds(new Rectangle(167, 172, 46, 37)); - btnThree.setFont(new Font("Dialog", Font.PLAIN, 20)); btnMultiply.setBounds(new Rectangle(234, 172, 46, 37)); - btnMultiply.setFont(new Font("Dialog", Font.PLAIN, 20)); btnCancel.setBounds(new Rectangle(298, 172, 46, 37)); btnCancel.setFont(new Font("Dialog", Font.PLAIN, 12)); btnCancel.addActionListener(new Calculate_btnCancel_actionAdapter(this)); btnZero.setBounds(new Rectangle(33, 222, 46, 37)); - btnZero.setFont(new Font("Dialog", Font.PLAIN, 20)); //加载数字0-9的监听事件 bindListener(btnZero, btnOne, btnTwo, btnThree, btnFour, btnFive, btnSix, btnSeven, btnEight, btnNine); + setFontForCompenent(btnZero, btnOne, btnTwo, btnThree, btnFour, btnFive, btnSix, btnSeven, + btnEight, btnNine, txtResult, btnDecrease, btnBegin, btnMultiply, btnDivide, btnIncrease, + btnEqual); btnMinus.setBounds(new Rectangle(101, 222, 46, 37)); btnMinus.setFont(new Font("Dialog", Font.PLAIN, 10)); @@ -123,14 +117,11 @@ private void initButton() { btnPoint.addActionListener(new Calculate_btnPoint_actionAdapter(this)); btnDivide.setBounds(new Rectangle(234, 222, 46, 37)); - btnDivide.setFont(new Font("Dialog", Font.PLAIN, 20)); btnEqual.setBounds(new Rectangle(298, 222, 46, 37)); - btnEqual.setFont(new Font("Dialog", Font.PLAIN, 20)); btnEqual.addActionListener(new Calculate_btnEqual_actionAdapter(this)); btnIncrease.setBounds(new Rectangle(234, 70, 46, 37)); - btnIncrease.setFont(new Font("Dialog", Font.PLAIN, 20)); //加载加减乘除运算符的监听事件 btnIncrease.addActionListener(new Calculate_btnIncrease_actionAdapter(this)); @@ -139,13 +130,8 @@ private void initButton() { btnDivide.addActionListener(new Calculate_btnIncrease_actionAdapter(this)); btnSeven.setBounds(new Rectangle(33, 70, 46, 37)); - btnSeven.setFont(new Font("Dialog", Font.PLAIN, 20)); - btnEight.setBounds(new Rectangle(101, 70, 46, 37)); - btnEight.setFont(new Font("Dialog", Font.PLAIN, 20)); - btnNine.setBounds(new Rectangle(167, 70, 46, 37)); - btnNine.setFont(new Font("Dialog", Font.PLAIN, 20)); bindButton(contentPane, btnZero, btnOne, btnTwo, btnThree, btnFour, btnFive, btnSix, btnSeven, btnEight, btnNine, btnDecrease, btnBegin, btnMultiply, btnCancel, btnMinus, btnPoint, @@ -154,6 +140,15 @@ private void initButton() { contentPane.add(txtResult); } + private void setFontForCompenent(JComponent... components) { + if (Objects.isNull(components)) { + return; + } + for (JComponent component : components) { + component.setFont(dialogFont); + } + } + private void bindListener(JButton... buttons) { if (Objects.isNull(buttons)) { return; From 451be76c82148b448dc64081f40365183f6f6803 Mon Sep 17 00:00:00 2001 From: kcp Date: Sun, 4 Aug 2019 12:54:30 +0800 Subject: [PATCH 088/476] +) add async --- .../concurrent/CompletableFutureTest.java | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 java8/src/test/java/com/github/kuangcp/concurrent/CompletableFutureTest.java diff --git a/java8/src/test/java/com/github/kuangcp/concurrent/CompletableFutureTest.java b/java8/src/test/java/com/github/kuangcp/concurrent/CompletableFutureTest.java new file mode 100644 index 00000000..d5db5d1c --- /dev/null +++ b/java8/src/test/java/com/github/kuangcp/concurrent/CompletableFutureTest.java @@ -0,0 +1,24 @@ +package com.github.kuangcp.concurrent; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; + +@Slf4j +public class CompletableFutureTest { + + @Test + public void testAsync() throws InterruptedException, ExecutionException, TimeoutException { + CompletableFuture future = CompletableFuture.runAsync(() -> { + System.out.println("temp"); + }); + + future.get(5, TimeUnit.SECONDS); + if (future.isDone()) { + System.out.println("complete"); + } + } +} From 39aed5eb0a1ac5fe47fd7f34e9538c66fc75a6a2 Mon Sep 17 00:00:00 2001 From: kcp Date: Sun, 4 Aug 2019 17:19:07 +0800 Subject: [PATCH 089/476] +) bloom filter --- .../kuangcp/bloom/filter/BloomFilter.java | 62 +++++++++++++++++++ .../kuangcp/bloom/filter/HashFunctions.java | 26 ++++++++ .../structure}/Node.java | 2 +- .../structure}/NodeAction.java | 2 +- .../structure}/NodeMgr.java | 2 +- .../structure}/package-info.java | 2 +- .../kuangcp/bloom/filter/BloomFilterTest.java | 35 +++++++++++ .../structure}/NodeMgrTest.java | 2 +- .../src/test/resources/logback-test.xml | 19 ++++++ 9 files changed, 147 insertions(+), 5 deletions(-) create mode 100644 algorithms/src/main/java/com/github/kuangcp/bloom/filter/BloomFilter.java create mode 100644 algorithms/src/main/java/com/github/kuangcp/bloom/filter/HashFunctions.java rename algorithms/src/main/java/com/github/kuangcp/{organizationstructure => organization/structure}/Node.java (90%) rename algorithms/src/main/java/com/github/kuangcp/{organizationstructure => organization/structure}/NodeAction.java (81%) rename algorithms/src/main/java/com/github/kuangcp/{organizationstructure => organization/structure}/NodeMgr.java (98%) rename algorithms/src/main/java/com/github/kuangcp/{organizationstructure => organization/structure}/package-info.java (75%) create mode 100644 algorithms/src/test/java/com/github/kuangcp/bloom/filter/BloomFilterTest.java rename algorithms/src/test/java/com/github/kuangcp/{organizationstructure => organization/structure}/NodeMgrTest.java (61%) create mode 100644 algorithms/src/test/resources/logback-test.xml diff --git a/algorithms/src/main/java/com/github/kuangcp/bloom/filter/BloomFilter.java b/algorithms/src/main/java/com/github/kuangcp/bloom/filter/BloomFilter.java new file mode 100644 index 00000000..4b1bfe4d --- /dev/null +++ b/algorithms/src/main/java/com/github/kuangcp/bloom/filter/BloomFilter.java @@ -0,0 +1,62 @@ +package com.github.kuangcp.bloom.filter; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Function; +import java.util.function.Predicate; +import lombok.extern.slf4j.Slf4j; + +/** + * https://blog.csdn.net/hguisu/article/details/7866173 + * + * @author https://github.com/kuangcp on 2019-08-04 15:57 + */ +@Slf4j +public class BloomFilter { + + private static List> functions = new ArrayList<>(); + private static final short LEN_OF_BYTE = 7; + + private byte[] cache = new byte[2 << 28]; + + static { + functions.add(HashFunctions.hashByObjects); + } + + public void add(String str) { + for (Function function : functions) { + Integer hash = function.apply(str); + int index = hash / LEN_OF_BYTE; + if (log.isDebugEnabled()) { + log.debug("hash: index={}", index); + } + byte unitByte = cache[index]; + byte unitIndex = (byte) (hash % LEN_OF_BYTE); + if (log.isDebugEnabled()) { + log.debug("read value: byte={} unitIndex={}", toStr(unitByte), unitIndex); + } + unitByte = (byte) (unitByte | 1 << (unitIndex)); + cache[index] = unitByte; + if (log.isDebugEnabled()) { + log.debug("set value: byte={}", toStr(unitByte)); + log.debug(""); + } + } + } + + public boolean exist(String str) { + Predicate> has = func -> { + Integer hash = func.apply(str); + int index = hash / LEN_OF_BYTE; + byte unitByte = cache[index]; + byte unitIndex = (byte) (hash % LEN_OF_BYTE); + return (unitByte & 1 << (unitIndex)) > 0; + }; + + return functions.stream().allMatch(has); + } + + private String toStr(byte value) { + return Integer.toBinaryString(value); + } +} diff --git a/algorithms/src/main/java/com/github/kuangcp/bloom/filter/HashFunctions.java b/algorithms/src/main/java/com/github/kuangcp/bloom/filter/HashFunctions.java new file mode 100644 index 00000000..324de344 --- /dev/null +++ b/algorithms/src/main/java/com/github/kuangcp/bloom/filter/HashFunctions.java @@ -0,0 +1,26 @@ +package com.github.kuangcp.bloom.filter; + +import java.util.Objects; +import java.util.function.Function; + +/** + * https://stackoverflow.com/questions/34595/what-is-a-good-hash-function + * + * @author https://github.com/kuangcp on 2019-08-04 16:02 + */ +public class HashFunctions { + + static Function hashByObjects = url -> { + int hash = Objects.hash(url); + + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 4; + hash += hash >> 17; + hash ^= hash << 25; + hash += hash >> 6; + + return hash > 0 ? hash : hash * -1; + }; + +} diff --git a/algorithms/src/main/java/com/github/kuangcp/organizationstructure/Node.java b/algorithms/src/main/java/com/github/kuangcp/organization/structure/Node.java similarity index 90% rename from algorithms/src/main/java/com/github/kuangcp/organizationstructure/Node.java rename to algorithms/src/main/java/com/github/kuangcp/organization/structure/Node.java index 7db8c919..0ddf3243 100644 --- a/algorithms/src/main/java/com/github/kuangcp/organizationstructure/Node.java +++ b/algorithms/src/main/java/com/github/kuangcp/organization/structure/Node.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.organizationstructure; +package com.github.kuangcp.organization.structure; import lombok.Data; diff --git a/algorithms/src/main/java/com/github/kuangcp/organizationstructure/NodeAction.java b/algorithms/src/main/java/com/github/kuangcp/organization/structure/NodeAction.java similarity index 81% rename from algorithms/src/main/java/com/github/kuangcp/organizationstructure/NodeAction.java rename to algorithms/src/main/java/com/github/kuangcp/organization/structure/NodeAction.java index 8d225856..e02b08b3 100644 --- a/algorithms/src/main/java/com/github/kuangcp/organizationstructure/NodeAction.java +++ b/algorithms/src/main/java/com/github/kuangcp/organization/structure/NodeAction.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.organizationstructure; +package com.github.kuangcp.organization.structure; /** * created by https://gitee.com/gin9 diff --git a/algorithms/src/main/java/com/github/kuangcp/organizationstructure/NodeMgr.java b/algorithms/src/main/java/com/github/kuangcp/organization/structure/NodeMgr.java similarity index 98% rename from algorithms/src/main/java/com/github/kuangcp/organizationstructure/NodeMgr.java rename to algorithms/src/main/java/com/github/kuangcp/organization/structure/NodeMgr.java index cc2f5464..73196d72 100644 --- a/algorithms/src/main/java/com/github/kuangcp/organizationstructure/NodeMgr.java +++ b/algorithms/src/main/java/com/github/kuangcp/organization/structure/NodeMgr.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.organizationstructure; +package com.github.kuangcp.organization.structure; import java.util.HashMap; import java.util.Map; diff --git a/algorithms/src/main/java/com/github/kuangcp/organizationstructure/package-info.java b/algorithms/src/main/java/com/github/kuangcp/organization/structure/package-info.java similarity index 75% rename from algorithms/src/main/java/com/github/kuangcp/organizationstructure/package-info.java rename to algorithms/src/main/java/com/github/kuangcp/organization/structure/package-info.java index 870f72aa..c09669c4 100644 --- a/algorithms/src/main/java/com/github/kuangcp/organizationstructure/package-info.java +++ b/algorithms/src/main/java/com/github/kuangcp/organization/structure/package-info.java @@ -2,4 +2,4 @@ * 组织结构 设计, 达到, 任意 添加, 移动, 删除 的需求, 但是存储是无序无关联的 * @author kuangcp on 18-9-17-下午1:46 */ -package com.github.kuangcp.organizationstructure; \ No newline at end of file +package com.github.kuangcp.organization.structure; \ No newline at end of file diff --git a/algorithms/src/test/java/com/github/kuangcp/bloom/filter/BloomFilterTest.java b/algorithms/src/test/java/com/github/kuangcp/bloom/filter/BloomFilterTest.java new file mode 100644 index 00000000..3abe1a05 --- /dev/null +++ b/algorithms/src/test/java/com/github/kuangcp/bloom/filter/BloomFilterTest.java @@ -0,0 +1,35 @@ +package com.github.kuangcp.bloom.filter; + +import java.util.UUID; +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; + +/** + * @author https://github.com/kuangcp on 2019-08-04 16:34 + */ +@Slf4j +public class BloomFilterTest { + + @Test + public void testMain() { + BloomFilter bloomFilter = new BloomFilter(); + int count = 0; + for (int i = 0; i < 10000; i++) { + String str = UUID.randomUUID().toString(); + boolean exist = bloomFilter.exist(str); + if (exist) { + System.out.println("already exist"); + count++; + } + + bloomFilter.add(str); + + exist = bloomFilter.exist(str); + if (!exist) { + System.out.println("not exist"); + count++; + } + } + log.info("invalid: count={}", count); + } +} diff --git a/algorithms/src/test/java/com/github/kuangcp/organizationstructure/NodeMgrTest.java b/algorithms/src/test/java/com/github/kuangcp/organization/structure/NodeMgrTest.java similarity index 61% rename from algorithms/src/test/java/com/github/kuangcp/organizationstructure/NodeMgrTest.java rename to algorithms/src/test/java/com/github/kuangcp/organization/structure/NodeMgrTest.java index 7e6bfcde..55cc3ca6 100644 --- a/algorithms/src/test/java/com/github/kuangcp/organizationstructure/NodeMgrTest.java +++ b/algorithms/src/test/java/com/github/kuangcp/organization/structure/NodeMgrTest.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.organizationstructure; +package com.github.kuangcp.organization.structure; /** * @author kuangcp on 18-9-17-下午1:46 diff --git a/algorithms/src/test/resources/logback-test.xml b/algorithms/src/test/resources/logback-test.xml new file mode 100644 index 00000000..9b387ff2 --- /dev/null +++ b/algorithms/src/test/resources/logback-test.xml @@ -0,0 +1,19 @@ + + + + + + + + %d{HH:mm:ss.SSS} %highlight(%-5level) [%thread] %logger{30}:%yellow(%-3L) %msg%n + + + DEBUG + + + + + + + + \ No newline at end of file From 330e23fcdb3999763de0116131c72bc0c3129a75 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Tue, 6 Aug 2019 00:44:40 +0800 Subject: [PATCH 090/476] -) delete simplex method implementation --- ...va => CalculateBtnBeginActionAdapter.java} | 4 +- ...a => CalculateBtnCancelActionAdapter.java} | 4 +- ...va => CalculateBtnEqualActionAdapter.java} | 4 +- ...=> CalculateBtnIncreaseActionAdapter.java} | 4 +- ...va => CalculateBtnMinusActionAdapter.java} | 4 +- ...va => CalculateBtnPointActionAdapter.java} | 4 +- ...ava => CalculateBtnZeroActionAdapter.java} | 4 +- .../github/kuangcp/caculator/Calculator.java | 92 +++--- ...a => EqualityAndNumericalEnumeration.java} | 5 +- .../com/github/kuangcp/key/CreateKey.java | 6 +- .../com/github/kuangcp/simpleMethod/README.md | 4 - .../simpleMethod/SimplexMethod/Equality.java | 62 ---- .../SimplexMethod/SimplexMethod.java | 294 ------------------ .../simpleMethod/SimplexMethod/Table.java | 85 ----- .../method}/Equality.java | 2 +- .../github/kuangcp/simplex/method/README.md | 3 + .../method}/ReadProperties.java | 17 +- .../method/SimplexMethodWithFraction.java} | 11 +- .../method}/Table.java | 2 +- .../asm/cglib/SimpleTransformer.java | 3 +- .../method}/MethodTest.java | 5 +- 21 files changed, 92 insertions(+), 527 deletions(-) rename gui/src/main/java/com/github/kuangcp/caculator/{Calculate_btnBegin_actionAdapter.java => CalculateBtnBeginActionAdapter.java} (74%) rename gui/src/main/java/com/github/kuangcp/caculator/{Calculate_btnCancel_actionAdapter.java => CalculateBtnCancelActionAdapter.java} (73%) rename gui/src/main/java/com/github/kuangcp/caculator/{Calculate_btnEqual_actionAdapter.java => CalculateBtnEqualActionAdapter.java} (74%) rename gui/src/main/java/com/github/kuangcp/caculator/{Calculate_btnIncrease_actionAdapter.java => CalculateBtnIncreaseActionAdapter.java} (73%) rename gui/src/main/java/com/github/kuangcp/caculator/{Calculate_btnMinus_actionAdapter.java => CalculateBtnMinusActionAdapter.java} (74%) rename gui/src/main/java/com/github/kuangcp/caculator/{Calculate_btnPoint_actionAdapter.java => CalculateBtnPointActionAdapter.java} (74%) rename gui/src/main/java/com/github/kuangcp/caculator/{Calculate_btnZero_actionAdapter.java => CalculateBtnZeroActionAdapter.java} (74%) rename question/src/main/java/com/github/kuangcp/caculator/{ListDengShi2.java => EqualityAndNumericalEnumeration.java} (63%) delete mode 100644 question/src/main/java/com/github/kuangcp/simpleMethod/README.md delete mode 100644 question/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethod/Equality.java delete mode 100644 question/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethod/SimplexMethod.java delete mode 100644 question/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethod/Table.java rename question/src/main/java/com/github/kuangcp/{simpleMethod/SimplexMethodQuarter => simplex/method}/Equality.java (93%) create mode 100644 question/src/main/java/com/github/kuangcp/simplex/method/README.md rename question/src/main/java/com/github/kuangcp/{simpleMethod/number => simplex/method}/ReadProperties.java (67%) rename question/src/main/java/com/github/kuangcp/{simpleMethod/SimplexMethodQuarter/SimplexMethod.java => simplex/method/SimplexMethodWithFraction.java} (97%) rename question/src/main/java/com/github/kuangcp/{simpleMethod/SimplexMethodQuarter => simplex/method}/Table.java (93%) rename question/src/test/java/com/github/kuangcp/{simpleMethod/SimplexMethodQuarter => simplex/method}/MethodTest.java (85%) diff --git a/gui/src/main/java/com/github/kuangcp/caculator/Calculate_btnBegin_actionAdapter.java b/gui/src/main/java/com/github/kuangcp/caculator/CalculateBtnBeginActionAdapter.java similarity index 74% rename from gui/src/main/java/com/github/kuangcp/caculator/Calculate_btnBegin_actionAdapter.java rename to gui/src/main/java/com/github/kuangcp/caculator/CalculateBtnBeginActionAdapter.java index 2e792468..34637f0d 100644 --- a/gui/src/main/java/com/github/kuangcp/caculator/Calculate_btnBegin_actionAdapter.java +++ b/gui/src/main/java/com/github/kuangcp/caculator/CalculateBtnBeginActionAdapter.java @@ -8,11 +8,11 @@ * * @author kuangcp on 3/19/19-1:02 AM */ -class Calculate_btnBegin_actionAdapter implements ActionListener { +class CalculateBtnBeginActionAdapter implements ActionListener { private Calculator adapter; - Calculate_btnBegin_actionAdapter(Calculator adapter) { + CalculateBtnBeginActionAdapter(Calculator adapter) { this.adapter = adapter; } diff --git a/gui/src/main/java/com/github/kuangcp/caculator/Calculate_btnCancel_actionAdapter.java b/gui/src/main/java/com/github/kuangcp/caculator/CalculateBtnCancelActionAdapter.java similarity index 73% rename from gui/src/main/java/com/github/kuangcp/caculator/Calculate_btnCancel_actionAdapter.java rename to gui/src/main/java/com/github/kuangcp/caculator/CalculateBtnCancelActionAdapter.java index b081f8c4..062abf41 100644 --- a/gui/src/main/java/com/github/kuangcp/caculator/Calculate_btnCancel_actionAdapter.java +++ b/gui/src/main/java/com/github/kuangcp/caculator/CalculateBtnCancelActionAdapter.java @@ -8,11 +8,11 @@ * * @author kuangcp on 3/19/19-1:02 AM */ -class Calculate_btnCancel_actionAdapter implements ActionListener { +class CalculateBtnCancelActionAdapter implements ActionListener { private Calculator adapter; - Calculate_btnCancel_actionAdapter(Calculator adapter) { + CalculateBtnCancelActionAdapter(Calculator adapter) { this.adapter = adapter; } diff --git a/gui/src/main/java/com/github/kuangcp/caculator/Calculate_btnEqual_actionAdapter.java b/gui/src/main/java/com/github/kuangcp/caculator/CalculateBtnEqualActionAdapter.java similarity index 74% rename from gui/src/main/java/com/github/kuangcp/caculator/Calculate_btnEqual_actionAdapter.java rename to gui/src/main/java/com/github/kuangcp/caculator/CalculateBtnEqualActionAdapter.java index deb865f5..0461836a 100644 --- a/gui/src/main/java/com/github/kuangcp/caculator/Calculate_btnEqual_actionAdapter.java +++ b/gui/src/main/java/com/github/kuangcp/caculator/CalculateBtnEqualActionAdapter.java @@ -8,11 +8,11 @@ * * @author kuangcp on 3/19/19-1:02 AM */ -class Calculate_btnEqual_actionAdapter implements ActionListener { +class CalculateBtnEqualActionAdapter implements ActionListener { private Calculator adapter; - Calculate_btnEqual_actionAdapter(Calculator adapter) { + CalculateBtnEqualActionAdapter(Calculator adapter) { this.adapter = adapter; } diff --git a/gui/src/main/java/com/github/kuangcp/caculator/Calculate_btnIncrease_actionAdapter.java b/gui/src/main/java/com/github/kuangcp/caculator/CalculateBtnIncreaseActionAdapter.java similarity index 73% rename from gui/src/main/java/com/github/kuangcp/caculator/Calculate_btnIncrease_actionAdapter.java rename to gui/src/main/java/com/github/kuangcp/caculator/CalculateBtnIncreaseActionAdapter.java index 2fd99fb5..c33c3081 100644 --- a/gui/src/main/java/com/github/kuangcp/caculator/Calculate_btnIncrease_actionAdapter.java +++ b/gui/src/main/java/com/github/kuangcp/caculator/CalculateBtnIncreaseActionAdapter.java @@ -8,11 +8,11 @@ * * @author kuangcp on 3/19/19-1:02 AM */ -class Calculate_btnIncrease_actionAdapter implements ActionListener { +class CalculateBtnIncreaseActionAdapter implements ActionListener { private Calculator adapter; - Calculate_btnIncrease_actionAdapter(Calculator adapter) { + CalculateBtnIncreaseActionAdapter(Calculator adapter) { this.adapter = adapter; } diff --git a/gui/src/main/java/com/github/kuangcp/caculator/Calculate_btnMinus_actionAdapter.java b/gui/src/main/java/com/github/kuangcp/caculator/CalculateBtnMinusActionAdapter.java similarity index 74% rename from gui/src/main/java/com/github/kuangcp/caculator/Calculate_btnMinus_actionAdapter.java rename to gui/src/main/java/com/github/kuangcp/caculator/CalculateBtnMinusActionAdapter.java index f35648a7..cf860aae 100644 --- a/gui/src/main/java/com/github/kuangcp/caculator/Calculate_btnMinus_actionAdapter.java +++ b/gui/src/main/java/com/github/kuangcp/caculator/CalculateBtnMinusActionAdapter.java @@ -8,11 +8,11 @@ * * @author kuangcp on 3/19/19-1:02 AM */ -class Calculate_btnMinus_actionAdapter implements ActionListener { +class CalculateBtnMinusActionAdapter implements ActionListener { private Calculator adapter; - Calculate_btnMinus_actionAdapter(Calculator adapter) { + CalculateBtnMinusActionAdapter(Calculator adapter) { this.adapter = adapter; } diff --git a/gui/src/main/java/com/github/kuangcp/caculator/Calculate_btnPoint_actionAdapter.java b/gui/src/main/java/com/github/kuangcp/caculator/CalculateBtnPointActionAdapter.java similarity index 74% rename from gui/src/main/java/com/github/kuangcp/caculator/Calculate_btnPoint_actionAdapter.java rename to gui/src/main/java/com/github/kuangcp/caculator/CalculateBtnPointActionAdapter.java index dd16adc3..0e6c1fda 100644 --- a/gui/src/main/java/com/github/kuangcp/caculator/Calculate_btnPoint_actionAdapter.java +++ b/gui/src/main/java/com/github/kuangcp/caculator/CalculateBtnPointActionAdapter.java @@ -8,11 +8,11 @@ * * @author kuangcp on 3/19/19-1:02 AM */ -class Calculate_btnPoint_actionAdapter implements ActionListener { +class CalculateBtnPointActionAdapter implements ActionListener { private Calculator adapter; - Calculate_btnPoint_actionAdapter(Calculator adapter) { + CalculateBtnPointActionAdapter(Calculator adapter) { this.adapter = adapter; } diff --git a/gui/src/main/java/com/github/kuangcp/caculator/Calculate_btnZero_actionAdapter.java b/gui/src/main/java/com/github/kuangcp/caculator/CalculateBtnZeroActionAdapter.java similarity index 74% rename from gui/src/main/java/com/github/kuangcp/caculator/Calculate_btnZero_actionAdapter.java rename to gui/src/main/java/com/github/kuangcp/caculator/CalculateBtnZeroActionAdapter.java index 24aee553..22c54e90 100644 --- a/gui/src/main/java/com/github/kuangcp/caculator/Calculate_btnZero_actionAdapter.java +++ b/gui/src/main/java/com/github/kuangcp/caculator/CalculateBtnZeroActionAdapter.java @@ -8,11 +8,11 @@ * * @author kuangcp on 3/19/19-1:02 AM */ -class Calculate_btnZero_actionAdapter implements ActionListener { +class CalculateBtnZeroActionAdapter implements ActionListener { private Calculator adapter; - Calculate_btnZero_actionAdapter(Calculator adapter) { + CalculateBtnZeroActionAdapter(Calculator adapter) { this.adapter = adapter; } diff --git a/gui/src/main/java/com/github/kuangcp/caculator/Calculator.java b/gui/src/main/java/com/github/kuangcp/caculator/Calculator.java index f1bd7ccd..26530f5c 100644 --- a/gui/src/main/java/com/github/kuangcp/caculator/Calculator.java +++ b/gui/src/main/java/com/github/kuangcp/caculator/Calculator.java @@ -19,7 +19,11 @@ */ public class Calculator extends JFrame { - private String front = "", behind = ""; //分别用于记录加减乘除运算符之前,之后输入的内容 + private short width = 46; + private short height = 37; + + private String front = ""; + private String behind = ""; //分别用于记录加减乘除运算符之前,之后输入的内容 private String op; //用于记录运算符 private String re;//用于存储运算结果的字符串格式 private boolean flag = false; //用于记录是否按下了运算符 @@ -71,76 +75,76 @@ private void initButton() { txtResult.setHorizontalAlignment(SwingConstants.RIGHT); txtResult.setBounds(new Rectangle(33, 19, 310, 34)); - btnNull.setBounds(new Rectangle(298, 70, 46, 37)); + btnNull.setBounds(new Rectangle(298, 70, width, height)); btnNull.setFont(new Font("Dialog", Font.PLAIN, 12)); //btnNull.addActionListener(new FrameCalculate_btnNull_actionAdapter(this)); - btnFour.setBounds(new Rectangle(33, 120, 46, 37)); - - btnFive.setBounds(new Rectangle(101, 120, 46, 37)); - - btnSix.setBounds(new Rectangle(167, 119, 46, 37)); - - btnDecrease.setBounds(new Rectangle(234, 120, 46, 37)); + btnFour.setBounds(new Rectangle(33, 120, width, height)); + btnFive.setBounds(new Rectangle(101, 120, width, height)); + btnSix.setBounds(new Rectangle(167, 119, width, height)); - btnBegin.setBounds(new Rectangle(298, 121, 46, 37)); + btnDecrease.setBounds(new Rectangle(234, 120, width, height)); - btnBegin.addActionListener(new Calculate_btnBegin_actionAdapter(this)); - btnOne.setBounds(new Rectangle(33, 172, 46, 37)); + btnBegin.setBounds(new Rectangle(298, 121, width, height)); - btnTwo.setBounds(new Rectangle(101, 172, 46, 37)); + btnBegin.addActionListener(new CalculateBtnBeginActionAdapter(this)); + btnOne.setBounds(new Rectangle(33, 172, width, height)); + btnTwo.setBounds(new Rectangle(101, 172, width, height)); + btnThree.setBounds(new Rectangle(167, 172, width, height)); - btnThree.setBounds(new Rectangle(167, 172, 46, 37)); + btnMultiply.setBounds(new Rectangle(234, 172, width, height)); - btnMultiply.setBounds(new Rectangle(234, 172, 46, 37)); - - btnCancel.setBounds(new Rectangle(298, 172, 46, 37)); + btnCancel.setBounds(new Rectangle(298, 172, width, height)); btnCancel.setFont(new Font("Dialog", Font.PLAIN, 12)); - btnCancel.addActionListener(new Calculate_btnCancel_actionAdapter(this)); - btnZero.setBounds(new Rectangle(33, 222, 46, 37)); + btnCancel.addActionListener(new CalculateBtnCancelActionAdapter(this)); + btnZero.setBounds(new Rectangle(33, 222, width, height)); //加载数字0-9的监听事件 - bindListener(btnZero, btnOne, btnTwo, btnThree, btnFour, btnFive, btnSix, btnSeven, btnEight, - btnNine); - setFontForCompenent(btnZero, btnOne, btnTwo, btnThree, btnFour, btnFive, btnSix, btnSeven, - btnEight, btnNine, txtResult, btnDecrease, btnBegin, btnMultiply, btnDivide, btnIncrease, - btnEqual); + bindListener(btnZero, btnOne, btnTwo, btnThree, btnFour, + btnFive, btnSix, btnSeven, btnEight, btnNine); + setFontForComponent(btnZero, btnOne, btnTwo, btnThree, btnFour, btnFive, + btnSix, btnSeven, btnEight, btnNine, txtResult, btnDecrease, + btnBegin, btnMultiply, btnDivide, btnIncrease, btnEqual); - btnMinus.setBounds(new Rectangle(101, 222, 46, 37)); + btnMinus.setBounds(new Rectangle(101, 222, width, height)); btnMinus.setFont(new Font("Dialog", Font.PLAIN, 10)); - btnMinus.addActionListener(new Calculate_btnMinus_actionAdapter(this)); - btnPoint.setBounds(new Rectangle(167, 222, 46, 37)); + btnMinus.addActionListener(new CalculateBtnMinusActionAdapter(this)); + btnPoint.setBounds(new Rectangle(167, 222, width, height)); btnPoint.setFont(new Font("Dialog", Font.PLAIN, 30)); btnPoint.setHorizontalTextPosition(SwingConstants.CENTER); - btnPoint.addActionListener(new Calculate_btnPoint_actionAdapter(this)); - btnDivide.setBounds(new Rectangle(234, 222, 46, 37)); + btnPoint.addActionListener(new CalculateBtnPointActionAdapter(this)); + btnDivide.setBounds(new Rectangle(234, 222, width, height)); - btnEqual.setBounds(new Rectangle(298, 222, 46, 37)); + btnEqual.setBounds(new Rectangle(298, 222, width, height)); - btnEqual.addActionListener(new Calculate_btnEqual_actionAdapter(this)); - btnIncrease.setBounds(new Rectangle(234, 70, 46, 37)); + btnEqual.addActionListener(new CalculateBtnEqualActionAdapter(this)); + btnIncrease.setBounds(new Rectangle(234, 70, width, height)); //加载加减乘除运算符的监听事件 - btnIncrease.addActionListener(new Calculate_btnIncrease_actionAdapter(this)); - btnDecrease.addActionListener(new Calculate_btnIncrease_actionAdapter(this)); - btnMultiply.addActionListener(new Calculate_btnIncrease_actionAdapter(this)); - btnDivide.addActionListener(new Calculate_btnIncrease_actionAdapter(this)); + bindCalculatorListener(); - btnSeven.setBounds(new Rectangle(33, 70, 46, 37)); - btnEight.setBounds(new Rectangle(101, 70, 46, 37)); - btnNine.setBounds(new Rectangle(167, 70, 46, 37)); + btnSeven.setBounds(new Rectangle(33, 70, width, height)); + btnEight.setBounds(new Rectangle(101, 70, width, height)); + btnNine.setBounds(new Rectangle(167, 70, width, height)); - bindButton(contentPane, btnZero, btnOne, btnTwo, btnThree, btnFour, btnFive, btnSix, btnSeven, - btnEight, btnNine, btnDecrease, btnBegin, btnMultiply, btnCancel, btnMinus, btnPoint, - btnDivide, btnEqual, btnIncrease, btnNull); + bindButton(contentPane, btnZero, btnOne, btnTwo, btnThree, btnFour, btnFive, + btnSix, btnSeven, btnEight, btnNine, btnDecrease, btnBegin, btnMultiply, btnCancel, + btnMinus, btnPoint, btnDivide, btnEqual, btnIncrease, btnNull); contentPane.add(txtResult); } - private void setFontForCompenent(JComponent... components) { + private void bindCalculatorListener() { + btnIncrease.addActionListener(new CalculateBtnIncreaseActionAdapter(this)); + btnDecrease.addActionListener(new CalculateBtnIncreaseActionAdapter(this)); + btnMultiply.addActionListener(new CalculateBtnIncreaseActionAdapter(this)); + btnDivide.addActionListener(new CalculateBtnIncreaseActionAdapter(this)); + } + + private void setFontForComponent(JComponent... components) { if (Objects.isNull(components)) { return; } @@ -157,7 +161,7 @@ private void bindListener(JButton... buttons) { if (Objects.isNull(button)) { continue; } - button.addActionListener(new Calculate_btnZero_actionAdapter(this)); + button.addActionListener(new CalculateBtnZeroActionAdapter(this)); } } diff --git a/question/src/main/java/com/github/kuangcp/caculator/ListDengShi2.java b/question/src/main/java/com/github/kuangcp/caculator/EqualityAndNumericalEnumeration.java similarity index 63% rename from question/src/main/java/com/github/kuangcp/caculator/ListDengShi2.java rename to question/src/main/java/com/github/kuangcp/caculator/EqualityAndNumericalEnumeration.java index bc8c7d4f..29413e29 100644 --- a/question/src/main/java/com/github/kuangcp/caculator/ListDengShi2.java +++ b/question/src/main/java/com/github/kuangcp/caculator/EqualityAndNumericalEnumeration.java @@ -3,7 +3,8 @@ /** * Created by https://github.com/kuangcp on 17-8-14 上午8:25 * abc + def = ghi 9个数字都不同: - * 思路 先要全排列,然后判断 + * 思路 先要全排列,然后判断 */ -public class ListDengShi2 { +public class EqualityAndNumericalEnumeration { + } diff --git a/question/src/main/java/com/github/kuangcp/key/CreateKey.java b/question/src/main/java/com/github/kuangcp/key/CreateKey.java index 30ded8a1..87e2cbef 100644 --- a/question/src/main/java/com/github/kuangcp/key/CreateKey.java +++ b/question/src/main/java/com/github/kuangcp/key/CreateKey.java @@ -77,10 +77,8 @@ private byte[] generateKeyBytes(int licenseType, int productId, return keyBytes; } - public String generateKey(BigInteger privKey, BigInteger pubKey, - int licenseType, int productId, - int minorVersion, int majorVersion, - String userName) { + public String generateKey(BigInteger privKey, BigInteger pubKey, int licenseType, int productId, + int minorVersion, int majorVersion, String userName) { int customerId = random.nextInt(9000) + 1000; byte[] keyBytes = generateKeyBytes(licenseType, productId, minorVersion, majorVersion, userName, diff --git a/question/src/main/java/com/github/kuangcp/simpleMethod/README.md b/question/src/main/java/com/github/kuangcp/simpleMethod/README.md deleted file mode 100644 index e5f19935..00000000 --- a/question/src/main/java/com/github/kuangcp/simpleMethod/README.md +++ /dev/null @@ -1,4 +0,0 @@ -# 有关数学 - -- SimpleMethod 单纯形法的计算 -- SimpleMethodQuarter 引入分式的单纯形法 \ No newline at end of file diff --git a/question/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethod/Equality.java b/question/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethod/Equality.java deleted file mode 100644 index 78e55318..00000000 --- a/question/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethod/Equality.java +++ /dev/null @@ -1,62 +0,0 @@ -package com.github.kuangcp.simpleMethod.SimplexMethod; - -import java.util.ArrayList; -import java.util.List; - -/** - * Created by Myth on 2017/3/20 0020 - * 约束式的对象形式 - */ -public class Equality { - - //式子的代表性参数X的下标 - Integer index; - List params; - Double result; - - public Equality() { - params = new ArrayList(); - } - - public Equality(List params, Double result) { - this.params = params; - this.result = result; - } - - public List getParams() { - return params; - } - - public void setParams(List params) { - this.params = params; - } - - public Double getResult() { - return result; - } - - public void setResult(Double result) { - this.result = result; - } - - public Integer getIndex() { - return index; - } - - public void setIndex(Integer index) { - this.index = index; - } - - @Override - public String toString() { - StringBuilder sub = new StringBuilder("["); - for (Double b : params) { - sub.append(b).append(","); - } - sub.append("]"); - return "Equality{" + - "params=" + sub + - ", result=" + result + - '}'; - } -} diff --git a/question/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethod/SimplexMethod.java b/question/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethod/SimplexMethod.java deleted file mode 100644 index 1f60e670..00000000 --- a/question/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethod/SimplexMethod.java +++ /dev/null @@ -1,294 +0,0 @@ -package com.github.kuangcp.simpleMethod.SimplexMethod; - - -import com.github.kuangcp.simpleMethod.number.ReadProperties; -import java.util.ArrayList; -import java.util.List; - -/** - * Created by Myth on 2017/3/20 - * 使用单纯形法来求解最优解 - * 但是还有一个数据类型的问题是无法避免的,一般是用分数是最好的,但是浮点数方便计算。。。 - */ -public class SimplexMethod { - - //读取配置文件 - private static ReadProperties config; - //最大参数个数 - private static int MAX_PARAMS; - //最大行列式数 - private static int EQUALITY; - //约束式子系数集合 - private List Rows = new ArrayList(); - //原始式子系数集合 - private List Max = new ArrayList(); - //单纯形表的总体数据结构 - private List
Tables = new ArrayList
(); - //单纯形表中间计算结果右侧 - private List Os = new ArrayList(); - //单纯形表计算结果最下一行 - private List Zs = new ArrayList(); - //出入基锁定的坐标 - private Integer resultCol; - private Integer resultRow; - - static { - config = new ReadProperties("src/main/resources/math/SimplexMethod.properties"); - MAX_PARAMS = config.getInt("MaxParams"); - EQUALITY = config.getInt("Equality"); - } - - public static void main(String[] s) { - SimplexMethod sm = new SimplexMethod(); - sm.run(); - } - - /** - * 初始化整体数据 - */ - public void init() { - //添加求解式的数据 - String max = config.getString("Max"); - String[] temp = max.split(","); - for (String data : temp) { - //System.out.println(data); - Max.add(Double.parseDouble(data)); - } - //添加约束式的数据 - for (int i = 1; i <= EQUALITY; i++) { - String buffer = config.getString("E" + i); - String[] tempList = buffer.split(","); - Equality e = new Equality(); - for (String s : tempList) { - e.getParams().add(Double.parseDouble(s)); - } - e.setResult(config.getDouble("B" + i)); - e.setIndex(config.getInt("I" + i)); - Rows.add(e); - } - //填入单纯形表总体数据中去 - for (int i = 0; i < EQUALITY; i++) { - Equality stRow = Rows.get(i); - Integer index = stRow.getIndex(); - //装填第一行的数据 - Table table = new Table(Max.get(index - 1), index, stRow.getResult(), stRow.getParams(), - null); - Tables.add(table); - } - //查看数据是否正确装填 - display("单纯形表的中心数据 : ", Tables); -// display("求解的方程式",Max); -// display("约束条件的方程式",Rows); - //展示方程式 - System.out.println("原题样式 : "); - showRows(); - } - - //进行表格的运算 - private void run() { - init(); - boolean flag; -//运算出初始的表格 - //计算最底下一行 - CalculateLastRow(); - //计算右边列 - CalculateRightCol(); - //判断是否达到退出条件 - flag = exitTime(Zs); - display("运行状态", Tables); - -//迭代的计算直到满足条件 - while (!flag) { - try { - Thread.sleep(10); - } catch (Exception e) { - e.printStackTrace(); - } - //算完一轮 - Double fatherNum = Tables.get(resultRow).getRows().get(resultCol); - List
oldTables = Tables; - Tables = new ArrayList
(); - - //转换出基入基的参数 - List rowsTemps = oldTables.get(resultRow).getRows(); - for (int k = 0; k < rowsTemps.size(); k++) { - rowsTemps.set(k, rowsTemps.get(k) / fatherNum); - } - Table temp = new Table(Max.get(resultCol), resultCol + 1, - oldTables.get(resultRow).getB() / fatherNum, rowsTemps, null); - Tables.add(temp); - //转换剩余的 - for (int j = 0; j < EQUALITY; j++) { - if (j != resultRow) { - rowsTemps = oldTables.get(j).getRows(); - Double motherNum = rowsTemps.get(resultCol); - Table fatherRow = Tables.get(0); - for (int k = 0; k < rowsTemps.size(); k++) { - rowsTemps.set(k, rowsTemps.get(k) - (motherNum * fatherRow.getRows().get(k))); - } - Integer tempXb = oldTables.get(j).getXb(); - Table otherTemp = new Table(Max.get(tempXb - 1), tempXb, - oldTables.get(j).getB() - motherNum * fatherRow.getB(), rowsTemps, null); - Tables.add(otherTemp); - } - - } -// System.out.println("Tables"+Tables.size()); - Zs.clear(); - Os.clear(); - //计算最后一行 - CalculateLastRow(); - //判断是否达到退出条件 - flag = exitTime(Zs); - if (!flag) { - CalculateRightCol(); - } - display("运行状态", Tables, true); - display("底栏", Zs, false); - - } - //运算完成 - double result = 0.0; - Double[] results = new Double[MAX_PARAMS]; - for (int i = 0; i < EQUALITY; i++) { - Table t = Tables.get(i); - result += t.getCb() * t.getB(); - results[t.getXb() - 1] = t.getB(); - } - System.out.println("最优目标值是 : " + result); - StringBuilder resultStr = new StringBuilder("X=("); - for (Double d : results) { - if (d != null) { - resultStr.append(d).append(","); - } else { - resultStr.append("0,"); - } - - } - resultStr = new StringBuilder(resultStr.substring(0, resultStr.length() - 1)); - resultStr.append(")"); - System.out.println(resultStr); - - } - - /** - * 计算最后一行和最右边的一列 - */ - public void CalculateLastRow() { - for (int i = 0; i < MAX_PARAMS; i++) {//循环变量个数 - Double temp = Max.get(i); - for (int j = 0; j < EQUALITY; j++) {//循环层数 - temp -= Tables.get(j).getRows().get(i) * Tables.get(j).getCb(); - } - Zs.add(temp); - } - display("最后一行", Zs, false); - resultCol = MaxList(Zs, true); - - } - - public void CalculateRightCol() { - //计算右边栏 - for (int i = 0; i < EQUALITY; i++) { - Double temp = Tables.get(i).getB() / Tables.get(i).getRows().get(resultCol); - Tables.get(i).setO(temp); - Os.add(temp); - } - //display("右栏",Os,false); - resultRow = MaxList(Os, false); - } - - /** - * 判断最后一行是否到了最后的计算结果 - * - * @return true就是算到了最后一步 - */ - public boolean exitTime(List list) { - boolean flag = true; - for (Double b : list) { - if (b > 0) { - flag = false; - } - } - return flag; - } - - /** - * 求得集合中的极值元素下标 - * - * @param max true就是最大值 - * @return 极值下标 - */ - public Integer MaxList(List list, boolean max) { - int index = 0; - Double temp = list.get(index); - for (int i = 1; i < list.size(); i++) { - if (max && temp < list.get(i)) { - index = i; - temp = list.get(i); - } - if (!max && temp > list.get(i)) { - index = i; - temp = list.get(i); - } - } - return index; - } - - /** - * 方便展示原始数据 - */ - public void display(String title, List list) { - display(title, list, true); - } - - public void display(String title, List list, boolean flag) { - System.out.println(title); - for (int i = 0; i < list.size(); i++) { - if (flag) { - System.out.println(list.get(i).toString()); - } else { - System.out.print(list.get(i).toString() + " "); - } - } - if (!flag) { - System.out.println(); - } - } - - /** - * 展示整体方程式,原题的样子 - */ - public void showRows() { - StringBuilder MaxRows = new StringBuilder("Max(z)="); - for (int i = 0; i < MAX_PARAMS; i++) { - if (Max.get(i) != 0) { - MaxRows.append(Max.get(i)).append(" X").append(i + 1).append(" + "); - } - } - MaxRows = new StringBuilder(MaxRows.substring(0, MaxRows.length() - 2)); - System.out.println("Aim : " + MaxRows); - for (int i = 0; i < EQUALITY; i++) { - StringBuilder Row = new StringBuilder(); - List temp = Rows.get(i).getParams(); - for (int j = 0; j < temp.size(); j++) { - Double a = temp.get(j); - if (a != 0) { - if (a != 1) { - Row.append(a).append("X").append(j + 1).append(" + "); - } else { - Row.append("X").append(j + 1).append(" + "); - } - } else { - Row.append(" "); - } - } - Row = new StringBuilder(Row.substring(0, Row.length() - 2)); - Row.append(" = ").append(Rows.get(i).getResult()).append(" |X").append(Rows.get(i).getIndex()) - .append("|"); - System.out.println("Equality : " + Row); - } - } - -} - diff --git a/question/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethod/Table.java b/question/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethod/Table.java deleted file mode 100644 index d4567748..00000000 --- a/question/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethod/Table.java +++ /dev/null @@ -1,85 +0,0 @@ -package com.github.kuangcp.simpleMethod.SimplexMethod; - -import java.util.ArrayList; -import java.util.List; - -/** - * Created by Myth on 2017/3/21 0021 - * 单纯形表的总体结构中的行对象 - */ -public class Table { - - private Double Cb; - private Integer Xb; - private Double b; - private List rows; - private Double O; - - public Table() { - } - - public Table(Double cb, Integer xb, Double b, List row, Double o) { - Cb = cb; - Xb = xb; - this.b = b; - O = o; - rows = new ArrayList<>(); - rows.addAll(row); - } - - public Double getCb() { - return Cb; - } - - public void setCb(Double cb) { - Cb = cb; - } - - public Integer getXb() { - return Xb; - } - - public void setXb(Integer xb) { - Xb = xb; - } - - public Double getB() { - return b; - } - - public void setB(Double b) { - this.b = b; - } - - public List getRows() { - return rows; - } - - public void setRows(List rows) { - this.rows = rows; - } - - public Double getO() { - return O; - } - - public void setO(Double o) { - O = o; - } - - @Override - public String toString() { - StringBuilder sub = new StringBuilder("["); - for (Double b : rows) { - sub.append(b).append(","); - } - sub.append("]"); - return "Table{" + - "Cb=" + Cb + - ", Xb=" + Xb + - ", b=" + b + - ", rows=" + sub + - ", O=" + O + - '}'; - } -} diff --git a/question/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethodQuarter/Equality.java b/question/src/main/java/com/github/kuangcp/simplex/method/Equality.java similarity index 93% rename from question/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethodQuarter/Equality.java rename to question/src/main/java/com/github/kuangcp/simplex/method/Equality.java index 6a9112fd..f5ae3e05 100644 --- a/question/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethodQuarter/Equality.java +++ b/question/src/main/java/com/github/kuangcp/simplex/method/Equality.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.simpleMethod.SimplexMethodQuarter; +package com.github.kuangcp.simplex.method; import com.github.kuangcp.math.number.Fraction; diff --git a/question/src/main/java/com/github/kuangcp/simplex/method/README.md b/question/src/main/java/com/github/kuangcp/simplex/method/README.md new file mode 100644 index 00000000..f2cc931e --- /dev/null +++ b/question/src/main/java/com/github/kuangcp/simplex/method/README.md @@ -0,0 +1,3 @@ +# 有关数学 + +- SimpleMethodWithFraction 引入分式的单纯形法 diff --git a/question/src/main/java/com/github/kuangcp/simpleMethod/number/ReadProperties.java b/question/src/main/java/com/github/kuangcp/simplex/method/ReadProperties.java similarity index 67% rename from question/src/main/java/com/github/kuangcp/simpleMethod/number/ReadProperties.java rename to question/src/main/java/com/github/kuangcp/simplex/method/ReadProperties.java index 10c06916..c9a884f4 100644 --- a/question/src/main/java/com/github/kuangcp/simpleMethod/number/ReadProperties.java +++ b/question/src/main/java/com/github/kuangcp/simplex/method/ReadProperties.java @@ -1,24 +1,29 @@ -package com.github.kuangcp.simpleMethod.number; +package com.github.kuangcp.simplex.method; import java.io.File; import java.io.FileInputStream; +import java.net.URL; import java.nio.charset.StandardCharsets; +import java.util.Objects; import java.util.Properties; /** * Created by Myth on 2017/1/13 0013 - 20:51 - * FIXME 读取不到文件 */ public class ReadProperties { private Properties cfg = new Properties(); /** - * 输入的是从src开始的路径: src/main/resources/a.properties + * @param file 例如 resources 目录下 a.properties */ public ReadProperties(String file) { try { - File f = new File(file); + URL resource = this.getClass().getClassLoader().getResource(file); + if (Objects.isNull(resource)) { + throw new RuntimeException("file not exist"); + } + File f = new File(resource.getPath()); cfg.load(new FileInputStream(f)); } catch (Exception e) { e.printStackTrace(); @@ -39,8 +44,8 @@ public double getDouble(String key) { } public static void main(String[] a) { - ReadProperties read = new ReadProperties("src/main/resources/math/SimplexMethod.properties"); - String result = read.getString("78"); + ReadProperties read = new ReadProperties("math/SimplexMethod.properties"); + String result = read.getString("Max"); try { //配置文件含中文需要转码 System.out.println(new String(result.getBytes(StandardCharsets.ISO_8859_1), diff --git a/question/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethodQuarter/SimplexMethod.java b/question/src/main/java/com/github/kuangcp/simplex/method/SimplexMethodWithFraction.java similarity index 97% rename from question/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethodQuarter/SimplexMethod.java rename to question/src/main/java/com/github/kuangcp/simplex/method/SimplexMethodWithFraction.java index 47ea2b61..fab4d7f3 100644 --- a/question/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethodQuarter/SimplexMethod.java +++ b/question/src/main/java/com/github/kuangcp/simplex/method/SimplexMethodWithFraction.java @@ -1,7 +1,6 @@ -package com.github.kuangcp.simpleMethod.SimplexMethodQuarter; +package com.github.kuangcp.simplex.method; import com.github.kuangcp.math.number.Fraction; -import com.github.kuangcp.simpleMethod.number.ReadProperties; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -10,7 +9,7 @@ /** * Created by Myth on 2017/3/22 0022 */ -public class SimplexMethod { +public class SimplexMethodWithFraction { private static ReadProperties config;//读取配置文件 private static int MAX_PARAMS;//最大参数个数 @@ -29,13 +28,13 @@ public class SimplexMethod { private Integer Round = 1; static { - config = new ReadProperties("src/main/resources/math/SimplexMethod.properties"); + config = new ReadProperties("math/SimplexMethod.properties"); MAX_PARAMS = config.getInt("MaxParams"); EQUALITY = config.getInt("Equality"); } public static void main(String[] s) { - SimplexMethod sm = new SimplexMethod(); + SimplexMethodWithFraction sm = new SimplexMethodWithFraction(); try { sm.run(); } catch (Exception e) { @@ -280,7 +279,7 @@ private boolean isNeedContinue(List list) { * @param permitMinus 是否允许负数进行笔记比较 * @return 最大 */ - Integer maxList(List list, boolean isMax, boolean haveInfinity, boolean permitMinus) { + public Integer maxList(List list, boolean isMax, boolean haveInfinity, boolean permitMinus) { Integer index = null; //有非数的集合 if (haveInfinity) { diff --git a/question/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethodQuarter/Table.java b/question/src/main/java/com/github/kuangcp/simplex/method/Table.java similarity index 93% rename from question/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethodQuarter/Table.java rename to question/src/main/java/com/github/kuangcp/simplex/method/Table.java index 521491ef..6b8aa30a 100644 --- a/question/src/main/java/com/github/kuangcp/simpleMethod/SimplexMethodQuarter/Table.java +++ b/question/src/main/java/com/github/kuangcp/simplex/method/Table.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.simpleMethod.SimplexMethodQuarter; +package com.github.kuangcp.simplex.method; import com.github.kuangcp.math.number.Fraction; diff --git a/question/src/main/java/com/github/kuangcp/situation/asm/cglib/SimpleTransformer.java b/question/src/main/java/com/github/kuangcp/situation/asm/cglib/SimpleTransformer.java index c48af19b..e33426f1 100644 --- a/question/src/main/java/com/github/kuangcp/situation/asm/cglib/SimpleTransformer.java +++ b/question/src/main/java/com/github/kuangcp/situation/asm/cglib/SimpleTransformer.java @@ -3,8 +3,7 @@ import net.sf.cglib.beans.BeanCopier; /** - * @author https://github.com/kuangcp - * @date 2019-06-13 09:12 + * @author https://github.com/kuangcp on 2019-06-13 09:12 */ class SimpleTransformer { diff --git a/question/src/test/java/com/github/kuangcp/simpleMethod/SimplexMethodQuarter/MethodTest.java b/question/src/test/java/com/github/kuangcp/simplex/method/MethodTest.java similarity index 85% rename from question/src/test/java/com/github/kuangcp/simpleMethod/SimplexMethodQuarter/MethodTest.java rename to question/src/test/java/com/github/kuangcp/simplex/method/MethodTest.java index ebce7927..32af9811 100644 --- a/question/src/test/java/com/github/kuangcp/simpleMethod/SimplexMethodQuarter/MethodTest.java +++ b/question/src/test/java/com/github/kuangcp/simplex/method/MethodTest.java @@ -1,6 +1,7 @@ -package com.github.kuangcp.simpleMethod.SimplexMethodQuarter; +package com.github.kuangcp.simplex.method; import com.github.kuangcp.math.number.Fraction; +import com.github.kuangcp.simplex.method.SimplexMethodWithFraction; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -19,7 +20,7 @@ public void testMax() { list.add(new Fraction(-1, 3)); list.add(new Fraction(-15, 1)); list.add(new Fraction(5, 2)); - SimplexMethod sm = new SimplexMethod(); + SimplexMethodWithFraction sm = new SimplexMethodWithFraction(); Integer index = sm.maxList(list, false, true, false); if (index != -1) { System.out.println(index + " => " + list.get(index)); From 76082a6d75e8fe52443d18e00c445340fcd92b82 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Tue, 6 Aug 2019 01:14:06 +0800 Subject: [PATCH 091/476] +) class path with compile file --- .../kuangcp/simplex/method/ReadProperties.java | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/question/src/main/java/com/github/kuangcp/simplex/method/ReadProperties.java b/question/src/main/java/com/github/kuangcp/simplex/method/ReadProperties.java index c9a884f4..bea9074c 100644 --- a/question/src/main/java/com/github/kuangcp/simplex/method/ReadProperties.java +++ b/question/src/main/java/com/github/kuangcp/simplex/method/ReadProperties.java @@ -9,6 +9,13 @@ /** * Created by Myth on 2017/1/13 0013 - 20:51 + * 将 java 目录和 resources 目录引入 classpath 即可正常运行 + * + * 例如 IDEA编译目录: ./out/production + * java -classpath ./classes/:./resources/ com.github.kuangcp.simplex.method.ReadProperties + * + * 例如 Gradle 编译目录: ./out/build + * java -classpath ./classes/java/main/:./resources/main/ com.github.kuangcp.simplex.method.ReadProperties */ public class ReadProperties { @@ -17,7 +24,7 @@ public class ReadProperties { /** * @param file 例如 resources 目录下 a.properties */ - public ReadProperties(String file) { + ReadProperties(String file) { try { URL resource = this.getClass().getClassLoader().getResource(file); if (Objects.isNull(resource)) { @@ -31,11 +38,11 @@ public ReadProperties(String file) { } } - public String getString(String key) { + String getString(String key) { return cfg.getProperty(key); } - public int getInt(String key) { + int getInt(String key) { return Integer.parseInt(cfg.getProperty(key)); } From 38c0fa6dafdc246c36a65253d575d164bf690995 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Sun, 11 Aug 2019 01:29:12 +0800 Subject: [PATCH 092/476] *) make field to local variable --- .../github/kuangcp/caculator/Calculator.java | 196 ++++++++++-------- 1 file changed, 106 insertions(+), 90 deletions(-) diff --git a/gui/src/main/java/com/github/kuangcp/caculator/Calculator.java b/gui/src/main/java/com/github/kuangcp/caculator/Calculator.java index 26530f5c..4db9ab5c 100644 --- a/gui/src/main/java/com/github/kuangcp/caculator/Calculator.java +++ b/gui/src/main/java/com/github/kuangcp/caculator/Calculator.java @@ -9,88 +9,118 @@ import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JFrame; -import javax.swing.JPanel; +import javax.swing.JRootPane; import javax.swing.JTextField; import javax.swing.SwingConstants; import javax.swing.WindowConstants; +import lombok.extern.slf4j.Slf4j; /** * 搜到的一个计算器实现代码 TODO 思考如何重构 */ +@Slf4j public class Calculator extends JFrame { - private short width = 46; - private short height = 37; + private static final short width = 47; + private static final short height = 38; + private static final Font dialogFont = new Font("Dialog", Font.PLAIN, 16); private String front = ""; private String behind = ""; //分别用于记录加减乘除运算符之前,之后输入的内容 - private String op; //用于记录运算符 - private String re;//用于存储运算结果的字符串格式 + private String operator; //用于记录运算符 + private String result;//用于存储运算结果的字符串格式 private boolean flag = false; //用于记录是否按下了运算符 - private boolean flag1 = false;//用于判断是否输入了点运算符 - private boolean flag2 = false;//用于判断是否输入了数字 - private boolean flag3 = false;//用于判断是否按下了等号运算符 + private boolean dotFlag = false;//用于判断是否输入了点运算符 + private boolean numFlag = false;//用于判断是否输入了数字 + private boolean calculateFlag = false;//用于判断是否按下了等号运算符 private JTextField txtResult = new JTextField("0"); - private JButton btnNull = new JButton("sqrt"); - private JButton btnFour = new JButton("4"); - private JButton btnFive = new JButton("5"); - private JButton btnSix = new JButton("6"); + private JTextField inputCache = new JTextField(""); + private JButton btnNull = new JButton(" "); + private JButton btnDecrease = new JButton("-"); private JButton btnBegin = new JButton("C"); - private JButton btnOne = new JButton("1"); - private JButton btnTwo = new JButton("2"); - private JButton btnThree = new JButton("3"); + private JButton btnMultiply = new JButton("*"); private JButton btnCancel = new JButton("←"); - private JButton btnZero = new JButton("0"); + private JButton btnMinus = new JButton("+/-"); private JButton btnPoint = new JButton("."); private JButton btnDivide = new JButton("/"); private JButton btnEqual = new JButton("="); private JButton btnIncrease = new JButton("+"); - private JButton btnSeven = new JButton("7"); - private JButton btnEight = new JButton("8"); - private JButton btnNine = new JButton("9"); - - private Font dialogFont = new Font("Dialog", Font.PLAIN, 16); private Calculator() { try { + rootPane.setLayout(null); + this.setResizable(false); + setSize(new Dimension(400, 300)); + setTitle("计算器"); + setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); - initButton(); - } catch (Exception exception) { - exception.printStackTrace(); + initLogicButton(); + initNumberButton(); + } catch (Exception e) { + log.error(e.getMessage(), e); } } - private void initButton() { - JPanel contentPane = (JPanel) getContentPane(); - contentPane.setLayout(null); - this.setResizable(false); - setSize(new Dimension(400, 300)); - setTitle("计算器"); + /** + * 初始化数字按钮 + */ + private void initNumberButton() { + JButton btnZero = new JButton("0"); + JButton btnOne = new JButton("1"); + JButton btnTwo = new JButton("2"); + JButton btnThree = new JButton("3"); + JButton btnFour = new JButton("4"); + JButton btnFive = new JButton("5"); + JButton btnSix = new JButton("6"); + + JButton btnSeven = new JButton("7"); + JButton btnEight = new JButton("8"); + JButton btnNine = new JButton("9"); + + btnZero.setBounds(new Rectangle(33, 222, width, height)); + btnOne.setBounds(new Rectangle(33, 172, width, height)); + btnTwo.setBounds(new Rectangle(101, 172, width, height)); + btnThree.setBounds(new Rectangle(167, 172, width, height)); + btnFour.setBounds(new Rectangle(33, 120, width, height)); + btnFive.setBounds(new Rectangle(101, 120, width, height)); + btnSix.setBounds(new Rectangle(167, 119, width, height)); + + btnSeven.setBounds(new Rectangle(33, 70, width, height)); + btnEight.setBounds(new Rectangle(101, 70, width, height)); + btnNine.setBounds(new Rectangle(167, 70, width, height)); + + JButton[] numberButtons = {btnZero, btnOne, btnTwo, btnThree, btnFour, btnFive, btnSix, + btnSeven, btnEight, btnNine}; + + //加载数字0-9的监听事件 + bindListener(numberButtons); + configFont(numberButtons); + showButton(numberButtons); + } + + private void initLogicButton() { txtResult.setEnabled(false); txtResult.setEditable(false); - txtResult.setHorizontalAlignment(SwingConstants.RIGHT); txtResult.setBounds(new Rectangle(33, 19, 310, 34)); + + inputCache.setEnabled(false); + inputCache.setEditable(false); + inputCache.setHorizontalAlignment(SwingConstants.RIGHT); + inputCache.setBounds(new Rectangle(27, 19, 310, 8)); + btnNull.setBounds(new Rectangle(298, 70, width, height)); btnNull.setFont(new Font("Dialog", Font.PLAIN, 12)); - //btnNull.addActionListener(new FrameCalculate_btnNull_actionAdapter(this)); - btnFour.setBounds(new Rectangle(33, 120, width, height)); - btnFive.setBounds(new Rectangle(101, 120, width, height)); - btnSix.setBounds(new Rectangle(167, 119, width, height)); - btnDecrease.setBounds(new Rectangle(234, 120, width, height)); btnBegin.setBounds(new Rectangle(298, 121, width, height)); btnBegin.addActionListener(new CalculateBtnBeginActionAdapter(this)); - btnOne.setBounds(new Rectangle(33, 172, width, height)); - btnTwo.setBounds(new Rectangle(101, 172, width, height)); - btnThree.setBounds(new Rectangle(167, 172, width, height)); btnMultiply.setBounds(new Rectangle(234, 172, width, height)); @@ -98,14 +128,8 @@ private void initButton() { btnCancel.setFont(new Font("Dialog", Font.PLAIN, 12)); btnCancel.addActionListener(new CalculateBtnCancelActionAdapter(this)); - btnZero.setBounds(new Rectangle(33, 222, width, height)); - //加载数字0-9的监听事件 - bindListener(btnZero, btnOne, btnTwo, btnThree, btnFour, - btnFive, btnSix, btnSeven, btnEight, btnNine); - setFontForComponent(btnZero, btnOne, btnTwo, btnThree, btnFour, btnFive, - btnSix, btnSeven, btnEight, btnNine, txtResult, btnDecrease, - btnBegin, btnMultiply, btnDivide, btnIncrease, btnEqual); + configFont(txtResult, btnDecrease, btnBegin, btnMultiply, btnDivide, btnIncrease, btnEqual); btnMinus.setBounds(new Rectangle(101, 222, width, height)); btnMinus.setFont(new Font("Dialog", Font.PLAIN, 10)); @@ -124,27 +148,18 @@ private void initButton() { btnIncrease.setBounds(new Rectangle(234, 70, width, height)); //加载加减乘除运算符的监听事件 - bindCalculatorListener(); - - btnSeven.setBounds(new Rectangle(33, 70, width, height)); - btnEight.setBounds(new Rectangle(101, 70, width, height)); - btnNine.setBounds(new Rectangle(167, 70, width, height)); - - bindButton(contentPane, btnZero, btnOne, btnTwo, btnThree, btnFour, btnFive, - btnSix, btnSeven, btnEight, btnNine, btnDecrease, btnBegin, btnMultiply, btnCancel, - btnMinus, btnPoint, btnDivide, btnEqual, btnIncrease, btnNull); - - contentPane.add(txtResult); - } - - private void bindCalculatorListener() { btnIncrease.addActionListener(new CalculateBtnIncreaseActionAdapter(this)); btnDecrease.addActionListener(new CalculateBtnIncreaseActionAdapter(this)); btnMultiply.addActionListener(new CalculateBtnIncreaseActionAdapter(this)); btnDivide.addActionListener(new CalculateBtnIncreaseActionAdapter(this)); + + showButton(btnDecrease, btnBegin, btnMultiply, btnCancel, + btnMinus, btnPoint, btnDivide, btnEqual, btnIncrease, btnNull); + + rootPane.add(txtResult); } - private void setFontForComponent(JComponent... components) { + private void configFont(JComponent... components) { if (Objects.isNull(components)) { return; } @@ -165,31 +180,32 @@ private void bindListener(JButton... buttons) { } } - private void bindButton(JPanel panel, JButton... buttons) { + private void showButton(JButton... buttons) { if (Objects.isNull(buttons)) { return; } - if (Objects.isNull(panel)) { + if (Objects.isNull(rootPane)) { return; } for (JButton button : buttons) { if (Objects.isNull(button)) { continue; } - panel.add(button); + + rootPane.add(button); } } void btnZeroActionPerformed(ActionEvent e) { if (flag) { //如果刚刚按下了运算符 txtResult.setText(""); - if (flag1) {//判断之前是否输入了点运算符 + if (dotFlag) {//判断之前是否输入了点运算符 txtResult.setText("0." + e.getActionCommand()); - flag1 = false; + dotFlag = false; } else { txtResult.setText(e.getActionCommand()); } - flag2 = true; + numFlag = true; } else { int num = txtResult.getText().indexOf("."); if (num < 0 && !txtResult.getText().equals("0")) { @@ -203,33 +219,33 @@ void btnZeroActionPerformed(ActionEvent e) { } } flag = false; - flag3 = false; + calculateFlag = false; } void btnIncreaseActionPerformed(ActionEvent e) { - if (flag3) { + if (calculateFlag) { txtResult.setText(txtResult.getText()); - op = e.getActionCommand(); //得到刚刚按下的运算符 + operator = e.getActionCommand(); //得到刚刚按下的运算符 front = txtResult.getText(); //记录加减乘除运算符之前输入的内容 - } else if (flag2) { + } else if (numFlag) { // ActionEvent ee = new ActionEvent("qq", 1, "pp"); btnEqualActionPerformed(); - op = e.getActionCommand(); //得到刚刚按下的运算符 - front = re; - flag2 = false; + operator = e.getActionCommand(); //得到刚刚按下的运算符 + front = result; + numFlag = false; } else { front = txtResult.getText(); //记录加减乘除运算符之前输入的内容 - op = e.getActionCommand(); //得到刚刚按下的运算符 + operator = e.getActionCommand(); //得到刚刚按下的运算符 } - flag3 = false; + calculateFlag = false; flag = true; //记录已经按下了加减乘除运算符的其中一个 } void btnEqualActionPerformed() { - if (!flag3) { //未曾按下等于运算符 + if (!calculateFlag) { //未曾按下等于运算符 behind = txtResult.getText(); } else { - front = re; + front = result; } try { if (Objects.isNull(front) || front.isEmpty() || Objects.isNull(behind) || behind.isEmpty()) { @@ -238,22 +254,22 @@ void btnEqualActionPerformed() { double a1 = Double.parseDouble(front); double b1 = Double.parseDouble(behind); double result; - if (Objects.equals(op, "+")) { + if (Objects.equals(operator, "+")) { result = a1 + b1; - } else if (Objects.equals(op, "-")) { + } else if (Objects.equals(operator, "-")) { result = a1 - b1; - } else if (Objects.equals(op, "*")) { + } else if (Objects.equals(operator, "*")) { result = a1 * b1; } else { result = a1 / b1; } - re = Double.toString(result); - txtResult.setText(re); + this.result = Double.toString(result); + txtResult.setText(this.result); } catch (ArithmeticException ce) { txtResult.setText("除数不能为零"); } - if (!flag3) { - flag3 = true; + if (!calculateFlag) { + calculateFlag = true; } } @@ -263,18 +279,18 @@ void btnPointActionPerformed(ActionEvent e) { txtResult.setText(txtResult.getText() + e.getActionCommand()); } if (flag) { - flag1 = true; + dotFlag = true; } } void btnBeginActionPerformed() {//清零运算符事件处理 flag = false; - flag1 = false; - flag2 = false; - flag3 = false; + dotFlag = false; + numFlag = false; + calculateFlag = false; front = ""; behind = ""; - re = ""; + result = ""; txtResult.setText("0"); } From 4ec76bac234176ace92cb4f2f6b3b749b192af3a Mon Sep 17 00:00:00 2001 From: kcp Date: Sun, 11 Aug 2019 12:12:06 +0800 Subject: [PATCH 093/476] *) calculate position --- .../github/kuangcp/caculator/Calculator.java | 41 +++++++------------ 1 file changed, 15 insertions(+), 26 deletions(-) diff --git a/gui/src/main/java/com/github/kuangcp/caculator/Calculator.java b/gui/src/main/java/com/github/kuangcp/caculator/Calculator.java index 4db9ab5c..f75ee22b 100644 --- a/gui/src/main/java/com/github/kuangcp/caculator/Calculator.java +++ b/gui/src/main/java/com/github/kuangcp/caculator/Calculator.java @@ -9,7 +9,6 @@ import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JFrame; -import javax.swing.JRootPane; import javax.swing.JTextField; import javax.swing.SwingConstants; import javax.swing.WindowConstants; @@ -69,32 +68,22 @@ private Calculator() { * 初始化数字按钮 */ private void initNumberButton() { + int startX = 30; + int startY = 170; + int deltaW = 70; + int deltaH = 50; + JButton[] numberButtons = new JButton[10]; + for (int i = 1; i < 10; i++) { + JButton button = new JButton(String.valueOf(i)); + int x = startX + ((i - 1) % 3) * deltaW; + int y = startY - ((i - 1) / 3) * deltaH; + button.setBounds(new Rectangle(x, y, width, height)); + numberButtons[i] = button; + } + JButton btnZero = new JButton("0"); - JButton btnOne = new JButton("1"); - JButton btnTwo = new JButton("2"); - JButton btnThree = new JButton("3"); - JButton btnFour = new JButton("4"); - JButton btnFive = new JButton("5"); - JButton btnSix = new JButton("6"); - - JButton btnSeven = new JButton("7"); - JButton btnEight = new JButton("8"); - JButton btnNine = new JButton("9"); - - btnZero.setBounds(new Rectangle(33, 222, width, height)); - btnOne.setBounds(new Rectangle(33, 172, width, height)); - btnTwo.setBounds(new Rectangle(101, 172, width, height)); - btnThree.setBounds(new Rectangle(167, 172, width, height)); - btnFour.setBounds(new Rectangle(33, 120, width, height)); - btnFive.setBounds(new Rectangle(101, 120, width, height)); - btnSix.setBounds(new Rectangle(167, 119, width, height)); - - btnSeven.setBounds(new Rectangle(33, 70, width, height)); - btnEight.setBounds(new Rectangle(101, 70, width, height)); - btnNine.setBounds(new Rectangle(167, 70, width, height)); - - JButton[] numberButtons = {btnZero, btnOne, btnTwo, btnThree, btnFour, btnFive, btnSix, - btnSeven, btnEight, btnNine}; + btnZero.setBounds(new Rectangle(startX, startY + deltaH, width, height)); + numberButtons[0] = btnZero; //加载数字0-9的监听事件 bindListener(numberButtons); From 712d792c4547177918af74ad7ce2ee5fb6a5ca61 Mon Sep 17 00:00:00 2001 From: kcp Date: Sat, 17 Aug 2019 17:42:28 +0800 Subject: [PATCH 094/476] +) hash function --- .../github/kuangcp/bloom/filter/BloomFilter.java | 1 + .../github/kuangcp/bloom/filter/HashFunctions.java | 14 ++++++++++++++ .../kuangcp/bloom/filter/BloomFilterTest.java | 2 +- 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/algorithms/src/main/java/com/github/kuangcp/bloom/filter/BloomFilter.java b/algorithms/src/main/java/com/github/kuangcp/bloom/filter/BloomFilter.java index 4b1bfe4d..74306e9f 100644 --- a/algorithms/src/main/java/com/github/kuangcp/bloom/filter/BloomFilter.java +++ b/algorithms/src/main/java/com/github/kuangcp/bloom/filter/BloomFilter.java @@ -21,6 +21,7 @@ public class BloomFilter { static { functions.add(HashFunctions.hashByObjects); + functions.add(HashFunctions.hashWithString); } public void add(String str) { diff --git a/algorithms/src/main/java/com/github/kuangcp/bloom/filter/HashFunctions.java b/algorithms/src/main/java/com/github/kuangcp/bloom/filter/HashFunctions.java index 324de344..9ae258e7 100644 --- a/algorithms/src/main/java/com/github/kuangcp/bloom/filter/HashFunctions.java +++ b/algorithms/src/main/java/com/github/kuangcp/bloom/filter/HashFunctions.java @@ -23,4 +23,18 @@ public class HashFunctions { return hash > 0 ? hash : hash * -1; }; + static Function hashWithString = url -> { + if (Objects.isNull(url)) { + return 0; + } + int hash = 7; + for (int i = 0; i < url.length(); i++) { + hash = hash * 31 + url.charAt(i); + } + hash = hash % 0x5fffff; + + System.out.println(hash); + return hash; + }; + } diff --git a/algorithms/src/test/java/com/github/kuangcp/bloom/filter/BloomFilterTest.java b/algorithms/src/test/java/com/github/kuangcp/bloom/filter/BloomFilterTest.java index 3abe1a05..0c548e57 100644 --- a/algorithms/src/test/java/com/github/kuangcp/bloom/filter/BloomFilterTest.java +++ b/algorithms/src/test/java/com/github/kuangcp/bloom/filter/BloomFilterTest.java @@ -14,7 +14,7 @@ public class BloomFilterTest { public void testMain() { BloomFilter bloomFilter = new BloomFilter(); int count = 0; - for (int i = 0; i < 10000; i++) { + for (int i = 0; i < 10; i++) { String str = UUID.randomUUID().toString(); boolean exist = bloomFilter.exist(str); if (exist) { From 31c21bed3d71246927ed30c1443c159d5872ae4b Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Sun, 25 Aug 2019 01:00:27 +0800 Subject: [PATCH 095/476] -) remove issue --- .../kuangcp/bloom/filter/HashFunctions.java | 6 +----- .../kuangcp/bloom/filter/BloomFilterTest.java | 14 +++++++++----- .../bloom/filter/HashFunctionsTest.java | 18 ++++++++++++++++++ .../order/service/impl/OrderServiceImpl.java | 10 ++++++++-- 4 files changed, 36 insertions(+), 12 deletions(-) create mode 100644 algorithms/src/test/java/com/github/kuangcp/bloom/filter/HashFunctionsTest.java diff --git a/algorithms/src/main/java/com/github/kuangcp/bloom/filter/HashFunctions.java b/algorithms/src/main/java/com/github/kuangcp/bloom/filter/HashFunctions.java index 9ae258e7..a104bf4c 100644 --- a/algorithms/src/main/java/com/github/kuangcp/bloom/filter/HashFunctions.java +++ b/algorithms/src/main/java/com/github/kuangcp/bloom/filter/HashFunctions.java @@ -31,10 +31,6 @@ public class HashFunctions { for (int i = 0; i < url.length(); i++) { hash = hash * 31 + url.charAt(i); } - hash = hash % 0x5fffff; - - System.out.println(hash); - return hash; + return hash > 0 ? hash : -hash; }; - } diff --git a/algorithms/src/test/java/com/github/kuangcp/bloom/filter/BloomFilterTest.java b/algorithms/src/test/java/com/github/kuangcp/bloom/filter/BloomFilterTest.java index 0c548e57..5133f46f 100644 --- a/algorithms/src/test/java/com/github/kuangcp/bloom/filter/BloomFilterTest.java +++ b/algorithms/src/test/java/com/github/kuangcp/bloom/filter/BloomFilterTest.java @@ -1,6 +1,6 @@ package com.github.kuangcp.bloom.filter; -import java.util.UUID; +import com.github.kuangcp.time.GetRunTime; import lombok.extern.slf4j.Slf4j; import org.junit.Test; @@ -12,13 +12,16 @@ public class BloomFilterTest { @Test public void testMain() { + GetRunTime run = new GetRunTime(); BloomFilter bloomFilter = new BloomFilter(); int count = 0; - for (int i = 0; i < 10; i++) { - String str = UUID.randomUUID().toString(); + int scale = 10000000; + run.startCount(); + for (int i = 0; i < scale; i++) { + String str = String.valueOf(i); boolean exist = bloomFilter.exist(str); if (exist) { - System.out.println("already exist"); + System.out.println("already exist: " + str); count++; } @@ -26,10 +29,11 @@ public void testMain() { exist = bloomFilter.exist(str); if (!exist) { - System.out.println("not exist"); + System.out.println("should exist: "+str); count++; } } + run.endCountOneLine("end"); log.info("invalid: count={}", count); } } diff --git a/algorithms/src/test/java/com/github/kuangcp/bloom/filter/HashFunctionsTest.java b/algorithms/src/test/java/com/github/kuangcp/bloom/filter/HashFunctionsTest.java new file mode 100644 index 00000000..0f304a57 --- /dev/null +++ b/algorithms/src/test/java/com/github/kuangcp/bloom/filter/HashFunctionsTest.java @@ -0,0 +1,18 @@ +package com.github.kuangcp.bloom.filter; + +import org.junit.Test; + +/** + * @author kuangcp on 2019-08-18 下午12:06 + */ +public class HashFunctionsTest { + + @Test + public void test() { + String id = "d4b02db2-6fb2-48e5-925f-44223f7f43e4"; + + Integer result = HashFunctions.hashWithString.apply(id); + System.out.println(result); + + } +} \ No newline at end of file diff --git a/mybatis/src/main/java/com/github/kuangcp/order/service/impl/OrderServiceImpl.java b/mybatis/src/main/java/com/github/kuangcp/order/service/impl/OrderServiceImpl.java index 3cb74c07..ff72ae37 100644 --- a/mybatis/src/main/java/com/github/kuangcp/order/service/impl/OrderServiceImpl.java +++ b/mybatis/src/main/java/com/github/kuangcp/order/service/impl/OrderServiceImpl.java @@ -45,10 +45,11 @@ public List queryByUserId(Long userId) { private OrderDTO convert(Order order, Map userMap) { Customer customer = userMap.get(order.getUserId()); + String username = getName(customer); return OrderDTO.builder() .num(order.getNum()) .userId(order.getUserId()) - .username(Optional.ofNullable(customer).map(Customer::getName).orElse("")) + .username(username) .createTime(order.getCreateTime()) .updateTime(order.getUpdateTime()) .detail(order.getDetail()) @@ -63,10 +64,11 @@ private OrderDTO convert(Order order, Map userMap) { private OrderDTO convert(Order order) { Customer customer = customerDao.selectById(order.getUserId()); + String username = getName(customer); return OrderDTO.builder() .num(order.getNum()) .userId(order.getUserId()) - .username(Optional.ofNullable(customer).map(Customer::getName).orElse("")) + .username(username) .createTime(order.getCreateTime()) .updateTime(order.getUpdateTime()) .detail(order.getDetail()) @@ -76,4 +78,8 @@ private OrderDTO convert(Order order) { .id(order.getId()) .build(); } + + private String getName(Customer customer) { + return Optional.ofNullable(customer).map(Customer::getName).orElse(""); + } } From d5460497ab6f58cf128c5c0d1ff986b8a8e1a585 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Sun, 1 Sep 2019 00:33:35 +0800 Subject: [PATCH 096/476] *) use log rather than sout --- .../com/github/kuangcp/read/ClassScanner.java | 107 ++++++++++-------- 1 file changed, 60 insertions(+), 47 deletions(-) diff --git a/class/src/main/java/com/github/kuangcp/read/ClassScanner.java b/class/src/main/java/com/github/kuangcp/read/ClassScanner.java index dee2f63c..22470465 100644 --- a/class/src/main/java/com/github/kuangcp/read/ClassScanner.java +++ b/class/src/main/java/com/github/kuangcp/read/ClassScanner.java @@ -5,6 +5,7 @@ import java.net.JarURLConnection; import java.net.URL; import java.net.URLDecoder; +import java.nio.charset.StandardCharsets; import java.util.Enumeration; import java.util.LinkedHashSet; import java.util.List; @@ -14,15 +15,19 @@ import java.util.jar.JarFile; import java.util.regex.Pattern; import lombok.NoArgsConstructor; +import lombok.extern.slf4j.Slf4j; /** * Created by https://github.com/kuangcp 读取类 是一个工具类 * * @author kuangcp */ +@Slf4j @NoArgsConstructor public class ClassScanner { + private static final String suffix = ".class"; + private boolean checkInOrEx; private List classFilters; private boolean excludeInner; @@ -50,6 +55,7 @@ public Set> getPackageAllClasses(String basePackage, boolean recursive) if (basePackage.endsWith(".")) { packageName = basePackage.substring(0, basePackage.length() - 1); } + String package2Path = packageName.replace('.', '/'); try { Enumeration dirs = Thread.currentThread().getContextClassLoader() @@ -58,15 +64,15 @@ public Set> getPackageAllClasses(String basePackage, boolean recursive) URL url = dirs.nextElement(); String protocol = url.getProtocol(); if ("file".equals(protocol)) { - String filePath = URLDecoder.decode(url.getFile(), "UTF-8"); + String filePath = URLDecoder.decode(url.getFile(), StandardCharsets.UTF_8.name()); this.doScanPackageClassesByFile(classes, packageName, filePath, recursive); } else if ("jar".equals(protocol)) { this.doScanPackageClassesByJar(packageName, url, recursive, classes); } } return classes; - } catch (IOException var10) { - var10.printStackTrace(); + } catch (IOException e) { + log.error(e.getMessage(), e); throw new InternalError("扫描包失败"); } } @@ -74,37 +80,45 @@ public Set> getPackageAllClasses(String basePackage, boolean recursive) /** * 从文件中读取类 */ - private void doScanPackageClassesByFile(Set> classes, String packageName, - String packagePath, final boolean recursive) { + private void doScanPackageClassesByFile( + Set> classes, + String packageName, + String packagePath, + final boolean recursive) { + File dir = new File(packagePath); - if (dir.exists() && dir.isDirectory()) { - File[] files = dir.listFiles(file -> { - if (file.isDirectory()) { - return recursive; - } else { - String filename = file.getName(); - return (!ClassScanner.this.excludeInner || filename.indexOf(36) == -1) - && ClassScanner.this.filterClassName(filename); - } - }); - if (Objects.isNull(files)) { - return; + if (!dir.exists() || !dir.isDirectory()) { + return; + } + + File[] files = dir.listFiles(file -> { + if (file.isDirectory()) { + return recursive; + } else { + String filename = file.getName(); + return (!ClassScanner.this.excludeInner || filename.indexOf(36) == -1) + && ClassScanner.this.filterClassName(filename); } + }); - for (File file : files) { - if (file.isDirectory()) { - this.doScanPackageClassesByFile(classes, packageName + "." + file.getName(), - file.getAbsolutePath(), recursive); - } else { - String className = file.getName().substring(0, file.getName().length() - 6); + if (Objects.isNull(files)) { + return; + } - try { - classes.add(Thread.currentThread().getContextClassLoader() - .loadClass(packageName + '.' + className)); - } catch (ClassNotFoundException var14) { - var14.printStackTrace(); - throw new InternalError("过滤失败"); - } + for (File file : files) { + if (file.isDirectory()) { + this.doScanPackageClassesByFile(classes, packageName + "." + file.getName(), + file.getAbsolutePath(), recursive); + } else { + String className = file.getName().substring(0, file.getName().length() - suffix.length()); + + try { + String classPath = packageName + '.' + className; + Class target = Thread.currentThread().getContextClassLoader().loadClass(classPath); + classes.add(target); + } catch (ClassNotFoundException e) { + log.error(e.getMessage(), e); + throw new InternalError("过滤失败"); } } } @@ -145,24 +159,26 @@ private void doScanPackageClassesByJar(String basePackage, URL url, boolean recu String classSimpleName = name.substring(name.lastIndexOf(47) + 1); if (this.filterClassName(classSimpleName)) { String className = name.replace('/', '.'); - className = className.substring(0, className.length() - 6); + className = className.substring(0, className.length() - suffix.length()); try { classes.add(Thread.currentThread().getContextClassLoader().loadClass(className)); - } catch (ClassNotFoundException var14) { - var14.printStackTrace(); - throw new InternalError("过滤失败"); + } catch (ClassNotFoundException e) { + log.error(e.getMessage(), e); + throw new RuntimeException("过滤失败"); } } } + } catch (IOException e) { - e.printStackTrace(); + log.error(e.getMessage(), e); throw new InternalError("扫描jar失败"); } } private boolean filterClassName(String className) { - if (!className.endsWith(".class")) { + + if (!className.endsWith(suffix)) { return false; } @@ -171,16 +187,13 @@ private boolean filterClassName(String className) { } String tmpName = className.substring(0, className.length() - 6); - boolean flag = false; - - for (String str : this.classFilters) { - String tmpreg = "^" + str.replace("*", ".*") + "$"; - Pattern p = Pattern.compile(tmpreg); - if (p.matcher(tmpName).find()) { - flag = true; - break; - } - } - return this.checkInOrEx && flag || !this.checkInOrEx && !flag; + boolean matchFlag = this.classFilters.stream() + .anyMatch(v -> { + String reg = "^" + v.replace("*", ".*") + "$"; + Pattern p = Pattern.compile(reg); + return p.matcher(tmpName).find(); + }); + + return this.checkInOrEx && matchFlag || !this.checkInOrEx && !matchFlag; } } \ No newline at end of file From c737f5ad9c8962738e2a86db1e40e3158a6d3d81 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Sat, 14 Sep 2019 09:57:59 +0800 Subject: [PATCH 097/476] +) test jar --- .../github/kuangcp/read/ClassScannerTest.java | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/class/src/test/java/com/github/kuangcp/read/ClassScannerTest.java b/class/src/test/java/com/github/kuangcp/read/ClassScannerTest.java index f3d153c5..a782533a 100644 --- a/class/src/test/java/com/github/kuangcp/read/ClassScannerTest.java +++ b/class/src/test/java/com/github/kuangcp/read/ClassScannerTest.java @@ -1,6 +1,8 @@ package com.github.kuangcp.read; +import com.github.kuangcp.time.GetRunTime; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Set; import lombok.extern.slf4j.Slf4j; @@ -16,15 +18,18 @@ public class ClassScannerTest { @Test - public void testGetPackageAllClasses() throws Exception { + public void testGetPackageAllClasses() { // 递归找到当前类所在包的所有 Base 后缀且继承了抽象逻辑类, 然后生成代码 List list = new ArrayList<>(); list.add("*Base"); ClassScanner scanner = new ClassScanner(true, true, list); + System.out.println(ClassScannerTest.class.getPackage().getName()); Set> clazzSet = scanner .getPackageAllClasses(ClassScannerTest.class.getPackage().getName(), true); + log.info("size: {}", clazzSet.size()); + for (Class clazz : clazzSet) { if (clazz != BaseFather.class && BaseFather.class.isAssignableFrom(clazz)) { // 继承自 BaseFather String rawName = clazz.getSimpleName(); @@ -35,6 +40,15 @@ public void testGetPackageAllClasses() throws Exception { } } + @Test + public void testReadJar(){ + ClassScanner scanner = new ClassScanner(true, true, Collections.emptyList()); + Set> result = scanner.getPackageAllClasses(GetRunTime.class.getPackage().getName(), true); + log.info("result {}", result.size()); + + result.forEach(System.out::println); + } + private static String lowerCaseFirstLetter(String name) { return name.length() == 1 ? name.toLowerCase() : name.substring(0, 1).toLowerCase() + name.substring(1); From 39ab7866ac3bd58dbc11a0bdfd02c26b9ade26c2 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Sat, 14 Sep 2019 10:34:27 +0800 Subject: [PATCH 098/476] *) optimized scan jar --- .../com/github/kuangcp/read/ClassScanner.java | 73 +++++++++---------- .../github/kuangcp/read/ClassScannerTest.java | 8 +- 2 files changed, 39 insertions(+), 42 deletions(-) diff --git a/class/src/main/java/com/github/kuangcp/read/ClassScanner.java b/class/src/main/java/com/github/kuangcp/read/ClassScanner.java index 22470465..abc4f966 100644 --- a/class/src/main/java/com/github/kuangcp/read/ClassScanner.java +++ b/class/src/main/java/com/github/kuangcp/read/ClassScanner.java @@ -31,6 +31,7 @@ public class ClassScanner { private boolean checkInOrEx; private List classFilters; private boolean excludeInner; + private boolean hasEnterEntry; public ClassScanner(Boolean excludeInner, Boolean checkInOrEx, List classFilters) { this.excludeInner = excludeInner; @@ -67,7 +68,7 @@ public Set> getPackageAllClasses(String basePackage, boolean recursive) String filePath = URLDecoder.decode(url.getFile(), StandardCharsets.UTF_8.name()); this.doScanPackageClassesByFile(classes, packageName, filePath, recursive); } else if ("jar".equals(protocol)) { - this.doScanPackageClassesByJar(packageName, url, recursive, classes); + this.scanPackageClassesByJar(packageName, url, classes); } } return classes; @@ -127,57 +128,49 @@ private void doScanPackageClassesByFile( /** * 从Jar中读取类 */ - private void doScanPackageClassesByJar(String basePackage, URL url, boolean recursive, - Set> classes) { + private void scanPackageClassesByJar(String basePackage, URL url, Set> classes) { String package2Path = basePackage.replace('.', '/'); try { JarFile jar = ((JarURLConnection) url.openConnection()).getJarFile(); Enumeration entries = jar.entries(); - while (true) { - String name; - // TODO optimized !!! - do { - JarEntry entry; - do { - // 目标包下具体类 非目录 - do { - // 找到目标包 - do { - if (!entries.hasMoreElements()) { - return; - } - - entry = (JarEntry) entries.nextElement(); - name = entry.getName(); - } while (!name.startsWith(package2Path)); - } while (entry.isDirectory()); - } while (!recursive && name.lastIndexOf(47) != package2Path.length()); - } while (this.excludeInner && name.indexOf(36) != -1); - - String classSimpleName = name.substring(name.lastIndexOf(47) + 1); - if (this.filterClassName(classSimpleName)) { - String className = name.replace('/', '.'); - className = className.substring(0, className.length() - suffix.length()); - - try { - classes.add(Thread.currentThread().getContextClassLoader().loadClass(className)); - } catch (ClassNotFoundException e) { - log.error(e.getMessage(), e); - throw new RuntimeException("过滤失败"); - } - } - } - - } catch (IOException e) { + scanEntry(package2Path, classes, entries); + } catch (Exception e) { log.error(e.getMessage(), e); throw new InternalError("扫描jar失败"); } } - private boolean filterClassName(String className) { + private void scanEntry(String packagePath, Set> classes, Enumeration entries) { + if (!entries.hasMoreElements()) { + return; + } + JarEntry entry = (JarEntry) entries.nextElement(); + String name = entry.getName(); + + String classSimpleName = name.substring(name.lastIndexOf('/') + 1); + if (this.filterClassName(classSimpleName) && name.startsWith(packagePath)) { + String className = name.replace('/', '.'); + hasEnterEntry = true; + className = className.substring(0, className.length() - suffix.length()); + try { + classes.add(Thread.currentThread().getContextClassLoader().loadClass(className)); + } catch (ClassNotFoundException e) { + log.error(e.getMessage(), e); + throw new RuntimeException("过滤失败"); + } + } + + // 已经遍历完目标目录,无需继续递归 + if (hasEnterEntry && !name.startsWith(packagePath)) { + return; + } + scanEntry(packagePath, classes, entries); + } + + private boolean filterClassName(String className) { if (!className.endsWith(suffix)) { return false; } diff --git a/class/src/test/java/com/github/kuangcp/read/ClassScannerTest.java b/class/src/test/java/com/github/kuangcp/read/ClassScannerTest.java index a782533a..3d7f7e1a 100644 --- a/class/src/test/java/com/github/kuangcp/read/ClassScannerTest.java +++ b/class/src/test/java/com/github/kuangcp/read/ClassScannerTest.java @@ -1,5 +1,6 @@ package com.github.kuangcp.read; +import com.github.kuangcp.io.ResourceTool; import com.github.kuangcp.time.GetRunTime; import java.util.ArrayList; import java.util.Collections; @@ -42,11 +43,14 @@ public void testGetPackageAllClasses() { @Test public void testReadJar(){ + GetRunTime getRunTime = new GetRunTime().startCount(); ClassScanner scanner = new ClassScanner(true, true, Collections.emptyList()); - Set> result = scanner.getPackageAllClasses(GetRunTime.class.getPackage().getName(), true); + String path = ResourceTool.class.getPackage().getName(); + log.info("path={}", path); + Set> result = scanner.getPackageAllClasses(path, true); log.info("result {}", result.size()); - result.forEach(System.out::println); + getRunTime.endCountOneLine(""); } private static String lowerCaseFirstLetter(String name) { From 05218f9bbf3ddefad30d812b8342e50dc47f05ec Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Sun, 15 Sep 2019 11:04:15 +0800 Subject: [PATCH 099/476] +) update to 1.0.5 --- class/src/main/java/com/github/kuangcp/annotation/First.java | 2 +- class/src/main/java/com/github/kuangcp/read/ClassScanner.java | 4 ---- .../test/java/com/github/kuangcp/read/ClassScannerTest.java | 2 +- common-config/Readme.md | 2 +- common-config/build.gradle | 1 + dependency.gradle | 2 +- 6 files changed, 5 insertions(+), 8 deletions(-) diff --git a/class/src/main/java/com/github/kuangcp/annotation/First.java b/class/src/main/java/com/github/kuangcp/annotation/First.java index 938515a1..26a7688e 100644 --- a/class/src/main/java/com/github/kuangcp/annotation/First.java +++ b/class/src/main/java/com/github/kuangcp/annotation/First.java @@ -15,5 +15,5 @@ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface First { - public String column() default ""; + String column() default ""; } diff --git a/class/src/main/java/com/github/kuangcp/read/ClassScanner.java b/class/src/main/java/com/github/kuangcp/read/ClassScanner.java index abc4f966..0e4758fb 100644 --- a/class/src/main/java/com/github/kuangcp/read/ClassScanner.java +++ b/class/src/main/java/com/github/kuangcp/read/ClassScanner.java @@ -39,10 +39,6 @@ public ClassScanner(Boolean excludeInner, Boolean checkInOrEx, List clas this.classFilters = classFilters; } - public List getClassFilters() { - return this.classFilters; - } - /** * 按正则匹配, 递归获取包下所有类 * diff --git a/class/src/test/java/com/github/kuangcp/read/ClassScannerTest.java b/class/src/test/java/com/github/kuangcp/read/ClassScannerTest.java index 3d7f7e1a..309e2cfb 100644 --- a/class/src/test/java/com/github/kuangcp/read/ClassScannerTest.java +++ b/class/src/test/java/com/github/kuangcp/read/ClassScannerTest.java @@ -42,7 +42,7 @@ public void testGetPackageAllClasses() { } @Test - public void testReadJar(){ + public void testReadJar() { GetRunTime getRunTime = new GetRunTime().startCount(); ClassScanner scanner = new ClassScanner(true, true, Collections.emptyList()); String path = ResourceTool.class.getPackage().getName(); diff --git a/common-config/Readme.md b/common-config/Readme.md index 37c4b4d2..ba1b3d81 100644 --- a/common-config/Readme.md +++ b/common-config/Readme.md @@ -1 +1 @@ -# 公共的配置文件 +# 项目统一配置文件 diff --git a/common-config/build.gradle b/common-config/build.gradle index 9548d28e..96df3cbd 100644 --- a/common-config/build.gradle +++ b/common-config/build.gradle @@ -1,3 +1,4 @@ configurations { + // void dependency loop compile.exclude module: 'common-config' } \ No newline at end of file diff --git a/dependency.gradle b/dependency.gradle index 390b4a14..30deb491 100644 --- a/dependency.gradle +++ b/dependency.gradle @@ -10,7 +10,7 @@ ext { , mail : '1.4.7' , jackson : '2.9.5' , hamcrest: '1.3' - , kcp_tool: '1.0.4' + , kcp_tool: '1.0.5' , netty : '4.1.35.Final' , guava : '27.1-jre' , jmh : '1.2.1' From 176989782fde5d989ffd7f8984d0acf182dd5873 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Tue, 17 Sep 2019 22:39:48 +0800 Subject: [PATCH 100/476] *) use abstract method --- .../github/kuangcp/map/IterateMapTest.java | 2 +- .../kuangcp/map/RandomGetValueTest.java | 2 +- .../kuangcp/customer/dao/CustomerDao.java | 2 + .../github/kuangcp/order/dao/OrderDao.java | 2 + .../kuangcp/customer/dao/CustomerDaoTest.java | 9 +-- .../kuangcp/order/dao/OrderDaoTest.java | 4 +- .../com/github/kuangcp/aop/ExceptionTest.java | 5 +- .../kuangcp/aop/annotation/PersonTest.java | 5 +- .../github/kuangcp/aop/xml/PersonTest.java | 5 +- .../kuangcp/aop/xml/salary/SalaryTest.java | 6 +- .../kuangcp/di/di/annotation/PersonTest.java | 5 +- .../di/di/xml/constructor/PersonTest.java | 5 +- .../kuangcp/di/di/xml/set/PersonTest.java | 5 +- .../github/kuangcp/di/scan/PersonTest.java | 5 +- .../kuangcp/document/spring/DocumentTest.java | 5 +- .../com/github/kuangcp/extend/PersonTest.java | 6 +- .../hibernate/annotation/PersonTest.java | 5 +- .../kuangcp/hibernate/xml/PersonTest.java | 5 +- .../github/kuangcp/ioc/alias/AliasTest.java | 5 +- .../ioc/createobject/CreateObjectTest.java | 5 +- .../ioc/createobject/when/WhenTest.java | 5 +- .../ioc/initdestroy/InitDestroyTest.java | 5 +- .../github/kuangcp/ioc/scope/ScopeTest.java | 5 +- .../kuangcp/jdbc/jdbc/DataSourceTest.java | 5 +- .../github/kuangcp/jdbc/jdbc/PersonTest.java | 5 +- .../jdbc/jdbc/transaction/PersonTest.java | 5 +- .../transaction/annotation/PersonTest.java | 5 +- .../kuangcp/jdbc/transaction/PersonTest.java | 5 +- .../com/github/kuangcp/mvc/PersonTest.java | 5 +- .../kuangcp/mvc/annotation/PersonTest.java | 5 +- .../com/github/kuangcp/proxy/salary/Readme.md | 6 ++ .../proxy/salary/aop/xml/PersonDaoImpl.java | 8 --- .../proxy/salary/aop/xml/PersonTest.java | 5 +- .../proxy/salary/jdkproxy/Interceptor.java | 9 +++ .../kuangcp/proxy/salary/jdkproxy/Logger.java | 13 ++-- .../proxy/salary/jdkproxy/Privilege.java | 17 +++--- .../proxy/salary/jdkproxy/ProxyTest.java | 59 ++++++++++--------- .../kuangcp/proxy/salary/jdkproxy/Readme.md | 3 + .../salary/jdkproxy/SalaryInterceptor.java | 55 ++++++++--------- .../proxy/salary/jdkproxy/SalaryManager.java | 3 +- .../salary/jdkproxy/SalaryManagerImpl.java | 14 +++-- .../proxy/salary/jdkproxy/Security.java | 11 +++- .../salary/jdkproxy/aspects/Interceptor.java | 9 --- .../proxy/salary/jdkproxy/aspects/Logger.java | 11 ---- .../salary/jdkproxy/aspects/Privilege.java | 11 ---- .../salary/jdkproxy/aspects/ProxyTest.java | 28 --------- .../jdkproxy/aspects/SalaryInterceptor.java | 46 --------------- .../jdkproxy/aspects/SalaryManager.java | 5 -- .../jdkproxy/aspects/SalaryManagerImpl.java | 8 --- .../salary/jdkproxy/aspects/Security.java | 11 ---- .../proxy/salary/{ => origin}/Logger.java | 2 +- .../proxy/salary/{ => origin}/Privilege.java | 2 +- .../kuangcp/proxy/salary/origin/Readme.md | 1 + .../salary/{ => origin}/SalaryManager.java | 2 +- .../proxy/salary/{ => origin}/SalaryTest.java | 2 +- .../proxy/salary/{ => origin}/Security.java | 2 +- .../kuangcp/proxy/salary/proxy/ProxyTest.java | 21 +++---- .../kuangcp/proxy/salary/proxy/Readme.md | 8 +-- .../proxy/salary/proxy/SalaryManagerImpl.java | 2 +- .../salary/proxy/SalaryManagerProxy.java | 46 +++++++-------- .../com/github/kuangcp/util/SpringHelper.java | 7 ++- .../proxy.salary}/applicationContext.xml | 0 62 files changed, 248 insertions(+), 322 deletions(-) create mode 100644 spring/src/test/java/com/github/kuangcp/proxy/salary/Readme.md create mode 100644 spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/Interceptor.java create mode 100644 spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/Readme.md delete mode 100644 spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/aspects/Interceptor.java delete mode 100644 spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/aspects/Logger.java delete mode 100644 spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/aspects/Privilege.java delete mode 100644 spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/aspects/ProxyTest.java delete mode 100644 spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/aspects/SalaryInterceptor.java delete mode 100644 spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/aspects/SalaryManager.java delete mode 100644 spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/aspects/SalaryManagerImpl.java delete mode 100644 spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/aspects/Security.java rename spring/src/test/java/com/github/kuangcp/proxy/salary/{ => origin}/Logger.java (65%) rename spring/src/test/java/com/github/kuangcp/proxy/salary/{ => origin}/Privilege.java (78%) create mode 100644 spring/src/test/java/com/github/kuangcp/proxy/salary/origin/Readme.md rename spring/src/test/java/com/github/kuangcp/proxy/salary/{ => origin}/SalaryManager.java (92%) rename spring/src/test/java/com/github/kuangcp/proxy/salary/{ => origin}/SalaryTest.java (88%) rename spring/src/test/java/com/github/kuangcp/proxy/salary/{ => origin}/Security.java (66%) rename spring/src/test/{java/com/github/kuangcp/proxy/salary/aop/xml => resources/proxy.salary}/applicationContext.xml (100%) diff --git a/collection/src/test/java/com/github/kuangcp/map/IterateMapTest.java b/collection/src/test/java/com/github/kuangcp/map/IterateMapTest.java index 695a4146..67ec3e84 100644 --- a/collection/src/test/java/com/github/kuangcp/map/IterateMapTest.java +++ b/collection/src/test/java/com/github/kuangcp/map/IterateMapTest.java @@ -1,6 +1,6 @@ package com.github.kuangcp.map; -import com.github.kuangcp.mock.map.MockMap; +import com.github.kuangcp.mock.MockMap; import java.util.Iterator; import java.util.Map; import lombok.extern.slf4j.Slf4j; diff --git a/collection/src/test/java/com/github/kuangcp/map/RandomGetValueTest.java b/collection/src/test/java/com/github/kuangcp/map/RandomGetValueTest.java index ef2b3195..fb2d14e9 100644 --- a/collection/src/test/java/com/github/kuangcp/map/RandomGetValueTest.java +++ b/collection/src/test/java/com/github/kuangcp/map/RandomGetValueTest.java @@ -3,7 +3,7 @@ import static org.hamcrest.CoreMatchers.hasItem; import static org.hamcrest.MatcherAssert.assertThat; -import com.github.kuangcp.mock.map.MockMap; +import com.github.kuangcp.mock.MockMap; import java.util.Map; import java.util.concurrent.ThreadLocalRandom; import lombok.extern.slf4j.Slf4j; diff --git a/mybatis/src/main/java/com/github/kuangcp/customer/dao/CustomerDao.java b/mybatis/src/main/java/com/github/kuangcp/customer/dao/CustomerDao.java index e2a89a37..fed16137 100644 --- a/mybatis/src/main/java/com/github/kuangcp/customer/dao/CustomerDao.java +++ b/mybatis/src/main/java/com/github/kuangcp/customer/dao/CustomerDao.java @@ -5,7 +5,9 @@ import java.util.List; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Select; +import org.springframework.stereotype.Repository; +@Repository public interface CustomerDao extends BaseMapper { @Select({"select * from customer where name like #{name} "}) diff --git a/mybatis/src/main/java/com/github/kuangcp/order/dao/OrderDao.java b/mybatis/src/main/java/com/github/kuangcp/order/dao/OrderDao.java index a175386d..e7db06f6 100644 --- a/mybatis/src/main/java/com/github/kuangcp/order/dao/OrderDao.java +++ b/mybatis/src/main/java/com/github/kuangcp/order/dao/OrderDao.java @@ -2,7 +2,9 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.github.kuangcp.order.domain.Order; +import org.springframework.stereotype.Repository; +@Repository public interface OrderDao extends BaseMapper { } diff --git a/mybatis/src/test/java/com/github/kuangcp/customer/dao/CustomerDaoTest.java b/mybatis/src/test/java/com/github/kuangcp/customer/dao/CustomerDaoTest.java index 5de4b676..4891caa2 100644 --- a/mybatis/src/test/java/com/github/kuangcp/customer/dao/CustomerDaoTest.java +++ b/mybatis/src/test/java/com/github/kuangcp/customer/dao/CustomerDaoTest.java @@ -1,8 +1,8 @@ package com.github.kuangcp.customer.dao; import com.github.kuangcp.base.TestStarter; -import com.github.kuangcp.mock.map.MockValue; import com.github.kuangcp.customer.domain.Customer; +import com.github.kuangcp.mock.common.MockUsuallyValue; import java.util.List; import java.util.stream.LongStream; import lombok.extern.slf4j.Slf4j; @@ -22,13 +22,14 @@ public void testQuery() { customers.forEach(item -> log.info("item={}", item)); } - + @Test - public void testBulkInsert(){ + public void testBulkInsert() { + LongStream.rangeClosed(1, 1000) .mapToObj(i -> Customer.builder().id(i) .id(i) - .nation(MockValue.mock(Integer.class)) + .nation(MockUsuallyValue.mock(Integer.class)) .build()) .forEach(customerDao::insert); } diff --git a/mybatis/src/test/java/com/github/kuangcp/order/dao/OrderDaoTest.java b/mybatis/src/test/java/com/github/kuangcp/order/dao/OrderDaoTest.java index 7a8f5cdc..ccd09706 100644 --- a/mybatis/src/test/java/com/github/kuangcp/order/dao/OrderDaoTest.java +++ b/mybatis/src/test/java/com/github/kuangcp/order/dao/OrderDaoTest.java @@ -2,7 +2,7 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.github.kuangcp.base.TestStarter; -import com.github.kuangcp.mock.map.MockValue; +import com.github.kuangcp.mock.common.MockUsuallyValue; import com.github.kuangcp.order.domain.Order; import java.time.LocalDateTime; import java.util.ArrayList; @@ -35,7 +35,7 @@ public void testQuery() { public void testBulkInsert() throws InterruptedException { Consumer consumer = start -> { - Order temp = Order.builder().num(MockValue.mock(Integer.class)) + Order temp = Order.builder().num(MockUsuallyValue.mock(Integer.class)) .createTime(LocalDateTime.now()).build(); for (int i = 0; i < 10000; i++) { long id = (start + i); diff --git a/spring/src/test/java/com/github/kuangcp/aop/ExceptionTest.java b/spring/src/test/java/com/github/kuangcp/aop/ExceptionTest.java index 2a6cd1d6..87c95ba5 100644 --- a/spring/src/test/java/com/github/kuangcp/aop/ExceptionTest.java +++ b/spring/src/test/java/com/github/kuangcp/aop/ExceptionTest.java @@ -6,8 +6,9 @@ public class ExceptionTest extends SpringHelper { - static { - path = "aop/exception/applicationContext-exception.xml"; + @Override + public String getXmlPath() { + return "proxy/salary/applicationContext.xml"; } @Test diff --git a/spring/src/test/java/com/github/kuangcp/aop/annotation/PersonTest.java b/spring/src/test/java/com/github/kuangcp/aop/annotation/PersonTest.java index 7550bc58..4a2b3ef9 100644 --- a/spring/src/test/java/com/github/kuangcp/aop/annotation/PersonTest.java +++ b/spring/src/test/java/com/github/kuangcp/aop/annotation/PersonTest.java @@ -8,8 +8,9 @@ */ public class PersonTest extends SpringHelper { - static { - path = "cn/itcast/spring0909/aop/annotation/applicationContext.xml"; + @Override + public String getXmlPath() { + return "proxy/salary/applicationContext.xml"; } @Test diff --git a/spring/src/test/java/com/github/kuangcp/aop/xml/PersonTest.java b/spring/src/test/java/com/github/kuangcp/aop/xml/PersonTest.java index 0af5c36e..bf6bd05d 100644 --- a/spring/src/test/java/com/github/kuangcp/aop/xml/PersonTest.java +++ b/spring/src/test/java/com/github/kuangcp/aop/xml/PersonTest.java @@ -20,8 +20,9 @@ */ public class PersonTest extends SpringHelper { - static { - path = "cn/itcast/spring0909/aop/xml/applicationContext.xml"; + @Override + public String getXmlPath() { + return "proxy/salary/applicationContext.xml"; } @Test diff --git a/spring/src/test/java/com/github/kuangcp/aop/xml/salary/SalaryTest.java b/spring/src/test/java/com/github/kuangcp/aop/xml/salary/SalaryTest.java index 83f3d75c..abd3d538 100644 --- a/spring/src/test/java/com/github/kuangcp/aop/xml/salary/SalaryTest.java +++ b/spring/src/test/java/com/github/kuangcp/aop/xml/salary/SalaryTest.java @@ -5,8 +5,10 @@ public class SalaryTest extends SpringHelper { - static { - path = "cn/itcast/spring0909/aop/xml/salary/applicationContext.xml"; + + @Override + public String getXmlPath() { + return "proxy/salary/applicationContext.xml"; } @Test diff --git a/spring/src/test/java/com/github/kuangcp/di/di/annotation/PersonTest.java b/spring/src/test/java/com/github/kuangcp/di/di/annotation/PersonTest.java index 98c7cfad..4607c4b6 100644 --- a/spring/src/test/java/com/github/kuangcp/di/di/annotation/PersonTest.java +++ b/spring/src/test/java/com/github/kuangcp/di/di/annotation/PersonTest.java @@ -23,8 +23,9 @@ */ public class PersonTest extends SpringHelper { - static { - path = "cn/itcast/spring0909/di/annotation/applicationContext.xml"; + @Override + public String getXmlPath() { + return "proxy/salary/applicationContext.xml"; } @Test diff --git a/spring/src/test/java/com/github/kuangcp/di/di/xml/constructor/PersonTest.java b/spring/src/test/java/com/github/kuangcp/di/di/xml/constructor/PersonTest.java index 2bfe8e44..7157663e 100644 --- a/spring/src/test/java/com/github/kuangcp/di/di/xml/constructor/PersonTest.java +++ b/spring/src/test/java/com/github/kuangcp/di/di/xml/constructor/PersonTest.java @@ -6,8 +6,9 @@ public class PersonTest extends SpringHelper { - static { - path = "cn/itcast/spring0909/di/xml/constructor/applicationContext.xml"; + @Override + public String getXmlPath() { + return "proxy/salary/applicationContext.xml"; } @Test diff --git a/spring/src/test/java/com/github/kuangcp/di/di/xml/set/PersonTest.java b/spring/src/test/java/com/github/kuangcp/di/di/xml/set/PersonTest.java index be930491..380f6921 100644 --- a/spring/src/test/java/com/github/kuangcp/di/di/xml/set/PersonTest.java +++ b/spring/src/test/java/com/github/kuangcp/di/di/xml/set/PersonTest.java @@ -7,8 +7,9 @@ public class PersonTest extends SpringHelper { - static { - path = "cn/itcast/spring0909/di/xml/set/applicationContext.xml"; + @Override + public String getXmlPath() { + return "proxy/salary/applicationContext.xml"; } @Test diff --git a/spring/src/test/java/com/github/kuangcp/di/scan/PersonTest.java b/spring/src/test/java/com/github/kuangcp/di/scan/PersonTest.java index 5a324851..1605654e 100644 --- a/spring/src/test/java/com/github/kuangcp/di/scan/PersonTest.java +++ b/spring/src/test/java/com/github/kuangcp/di/scan/PersonTest.java @@ -16,8 +16,9 @@ */ public class PersonTest extends SpringHelper { - static { - path = "cn/itcast/spring0909/scan/applicationContext.xml"; + @Override + public String getXmlPath() { + return "proxy/salary/applicationContext.xml"; } @Test diff --git a/spring/src/test/java/com/github/kuangcp/document/spring/DocumentTest.java b/spring/src/test/java/com/github/kuangcp/document/spring/DocumentTest.java index 2402eb50..aabb0480 100644 --- a/spring/src/test/java/com/github/kuangcp/document/spring/DocumentTest.java +++ b/spring/src/test/java/com/github/kuangcp/document/spring/DocumentTest.java @@ -5,8 +5,9 @@ public class DocumentTest extends SpringHelper { - static { - path = "cn/itcast/spring0909/document/spring/applicationContext.xml"; + @Override + public String getXmlPath() { + return "proxy/salary/applicationContext.xml"; } @Test diff --git a/spring/src/test/java/com/github/kuangcp/extend/PersonTest.java b/spring/src/test/java/com/github/kuangcp/extend/PersonTest.java index 9e5de8e1..91721613 100644 --- a/spring/src/test/java/com/github/kuangcp/extend/PersonTest.java +++ b/spring/src/test/java/com/github/kuangcp/extend/PersonTest.java @@ -5,8 +5,10 @@ public class PersonTest extends SpringHelper { - static { - path = "cn/itcast/spring0909/extend/applicationContext.xml"; + + @Override + public String getXmlPath() { + return "proxy/salary/applicationContext.xml"; } @Test diff --git a/spring/src/test/java/com/github/kuangcp/hibernate/annotation/PersonTest.java b/spring/src/test/java/com/github/kuangcp/hibernate/annotation/PersonTest.java index 536b4d04..4c886039 100644 --- a/spring/src/test/java/com/github/kuangcp/hibernate/annotation/PersonTest.java +++ b/spring/src/test/java/com/github/kuangcp/hibernate/annotation/PersonTest.java @@ -5,8 +5,9 @@ public class PersonTest extends SpringHelper { - static { - path = "cn/itcast/spring0909/hibernate/annotation/applicationContext.xml"; + @Override + public String getXmlPath() { + return "proxy/salary/applicationContext.xml"; } @Test diff --git a/spring/src/test/java/com/github/kuangcp/hibernate/xml/PersonTest.java b/spring/src/test/java/com/github/kuangcp/hibernate/xml/PersonTest.java index 3df38764..62bf99b8 100644 --- a/spring/src/test/java/com/github/kuangcp/hibernate/xml/PersonTest.java +++ b/spring/src/test/java/com/github/kuangcp/hibernate/xml/PersonTest.java @@ -6,8 +6,9 @@ public class PersonTest extends SpringHelper { - static { - path = "cn/itcast/spring0909/hibernate/transaction/xml/applicationContext.xml"; + @Override + public String getXmlPath() { + return "proxy/salary/applicationContext.xml"; } @Test diff --git a/spring/src/test/java/com/github/kuangcp/ioc/alias/AliasTest.java b/spring/src/test/java/com/github/kuangcp/ioc/alias/AliasTest.java index eeab766e..3560c98f 100644 --- a/spring/src/test/java/com/github/kuangcp/ioc/alias/AliasTest.java +++ b/spring/src/test/java/com/github/kuangcp/ioc/alias/AliasTest.java @@ -9,8 +9,9 @@ public class AliasTest extends SpringHelper { * 无论这两个类之间有什么继承关系,静态代码块比方法先执行 * alia是对一个bean取别名 */ - static { - path = "cn/itcast/spring0909/alias/applicationContext.xml"; + @Override + public String getXmlPath() { + return "proxy/salary/applicationContext.xml"; } @Test diff --git a/spring/src/test/java/com/github/kuangcp/ioc/createobject/CreateObjectTest.java b/spring/src/test/java/com/github/kuangcp/ioc/createobject/CreateObjectTest.java index 350b4f16..0d1b5bc8 100644 --- a/spring/src/test/java/com/github/kuangcp/ioc/createobject/CreateObjectTest.java +++ b/spring/src/test/java/com/github/kuangcp/ioc/createobject/CreateObjectTest.java @@ -6,8 +6,9 @@ public class CreateObjectTest extends SpringHelper { - static { - path = "cn/itcast/spring0909/createobject/applicationContext.xml"; + @Override + public String getXmlPath() { + return "proxy/salary/applicationContext.xml"; } /** diff --git a/spring/src/test/java/com/github/kuangcp/ioc/createobject/when/WhenTest.java b/spring/src/test/java/com/github/kuangcp/ioc/createobject/when/WhenTest.java index 2f7997df..92586e4b 100644 --- a/spring/src/test/java/com/github/kuangcp/ioc/createobject/when/WhenTest.java +++ b/spring/src/test/java/com/github/kuangcp/ioc/createobject/when/WhenTest.java @@ -5,8 +5,9 @@ public class WhenTest extends SpringHelper { - static { - path = "cn/itcast/spring0909/createobject/when/applicationContext.xml"; + @Override + public String getXmlPath() { + return "proxy/salary/applicationContext.xml"; } @Test diff --git a/spring/src/test/java/com/github/kuangcp/ioc/initdestroy/InitDestroyTest.java b/spring/src/test/java/com/github/kuangcp/ioc/initdestroy/InitDestroyTest.java index bf1834b9..2df763ee 100644 --- a/spring/src/test/java/com/github/kuangcp/ioc/initdestroy/InitDestroyTest.java +++ b/spring/src/test/java/com/github/kuangcp/ioc/initdestroy/InitDestroyTest.java @@ -7,8 +7,9 @@ public class InitDestroyTest extends SpringHelper { - static { - path = "cn/itcast/spring0909/initdestroy/applicationContext.xml"; + @Override + public String getXmlPath() { + return "proxy/salary/applicationContext.xml"; } @Test diff --git a/spring/src/test/java/com/github/kuangcp/ioc/scope/ScopeTest.java b/spring/src/test/java/com/github/kuangcp/ioc/scope/ScopeTest.java index fc081480..2dc07166 100644 --- a/spring/src/test/java/com/github/kuangcp/ioc/scope/ScopeTest.java +++ b/spring/src/test/java/com/github/kuangcp/ioc/scope/ScopeTest.java @@ -11,8 +11,9 @@ */ public class ScopeTest extends SpringHelper { - static { - path = "cn/itcast/spring0909/scope/applicationContext.xml"; + @Override + public String getXmlPath() { + return "proxy/salary/applicationContext.xml"; } @Test diff --git a/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/DataSourceTest.java b/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/DataSourceTest.java index 2388a7b4..d85a01fa 100644 --- a/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/DataSourceTest.java +++ b/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/DataSourceTest.java @@ -6,8 +6,9 @@ public class DataSourceTest extends SpringHelper { - static { - path = "cn/itcast/spring0909/jdbc/applicationContext.xml"; + @Override + public String getXmlPath() { + return "proxy/salary/applicationContext.xml"; } @Test diff --git a/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/PersonTest.java b/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/PersonTest.java index 275b21ed..12cf745d 100644 --- a/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/PersonTest.java +++ b/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/PersonTest.java @@ -7,8 +7,9 @@ public class PersonTest extends SpringHelper { - static { - path = "cn/itcast/spring0909/jdbc/applicationContext.xml"; + @Override + public String getXmlPath() { + return "proxy/salary/applicationContext.xml"; } @Test diff --git a/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/PersonTest.java b/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/PersonTest.java index 456cf813..5422b9fd 100644 --- a/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/PersonTest.java +++ b/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/PersonTest.java @@ -5,8 +5,9 @@ public class PersonTest extends SpringHelper { - static { - path = "cn/itcast/spring0909/jdbc/transaction/applicationContext.xml"; + @Override + public String getXmlPath() { + return "proxy/salary/applicationContext.xml"; } @Test diff --git a/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/annotation/PersonTest.java b/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/annotation/PersonTest.java index 35473320..ae16e793 100644 --- a/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/annotation/PersonTest.java +++ b/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/annotation/PersonTest.java @@ -6,8 +6,9 @@ public class PersonTest extends SpringHelper { - static { - path = "cn/itcast/spring0909/jdbc/transaction/annotation/applicationContext.xml"; + @Override + public String getXmlPath() { + return "proxy/salary/applicationContext.xml"; } @Test diff --git a/spring/src/test/java/com/github/kuangcp/jdbc/transaction/PersonTest.java b/spring/src/test/java/com/github/kuangcp/jdbc/transaction/PersonTest.java index d8f59f28..7ee3b5e9 100644 --- a/spring/src/test/java/com/github/kuangcp/jdbc/transaction/PersonTest.java +++ b/spring/src/test/java/com/github/kuangcp/jdbc/transaction/PersonTest.java @@ -5,8 +5,9 @@ public class PersonTest extends SpringHelper { - static { - path = "cn/itcast/spring0909/jdbc/transaction/applicationContext.xml"; + @Override + public String getXmlPath() { + return "proxy/salary/applicationContext.xml"; } @Test diff --git a/spring/src/test/java/com/github/kuangcp/mvc/PersonTest.java b/spring/src/test/java/com/github/kuangcp/mvc/PersonTest.java index 4efe566c..d96dfafe 100644 --- a/spring/src/test/java/com/github/kuangcp/mvc/PersonTest.java +++ b/spring/src/test/java/com/github/kuangcp/mvc/PersonTest.java @@ -6,8 +6,9 @@ public class PersonTest extends SpringHelper { - static { - path = "cn/itcast/spring0909/mvc/spring/applicationContext.xml"; + @Override + public String getXmlPath() { + return "proxy/salary/applicationContext.xml"; } @Test diff --git a/spring/src/test/java/com/github/kuangcp/mvc/annotation/PersonTest.java b/spring/src/test/java/com/github/kuangcp/mvc/annotation/PersonTest.java index 131c6797..87aa9f46 100644 --- a/spring/src/test/java/com/github/kuangcp/mvc/annotation/PersonTest.java +++ b/spring/src/test/java/com/github/kuangcp/mvc/annotation/PersonTest.java @@ -6,8 +6,9 @@ public class PersonTest extends SpringHelper { - static { - path = "cn/itcast/spring0909/mvc/spring/annotation/applicationContext.xml"; + @Override + public String getXmlPath() { + return "proxy/salary/applicationContext.xml"; } @Test diff --git a/spring/src/test/java/com/github/kuangcp/proxy/salary/Readme.md b/spring/src/test/java/com/github/kuangcp/proxy/salary/Readme.md new file mode 100644 index 00000000..e7106a94 --- /dev/null +++ b/spring/src/test/java/com/github/kuangcp/proxy/salary/Readme.md @@ -0,0 +1,6 @@ +# 代理实现方式 + +1. 不使用代理 +1. 手动硬编码代理 +1. JDK代理 +1. Spring AOP \ No newline at end of file diff --git a/spring/src/test/java/com/github/kuangcp/proxy/salary/aop/xml/PersonDaoImpl.java b/spring/src/test/java/com/github/kuangcp/proxy/salary/aop/xml/PersonDaoImpl.java index 9d5cb318..c9be0895 100644 --- a/spring/src/test/java/com/github/kuangcp/proxy/salary/aop/xml/PersonDaoImpl.java +++ b/spring/src/test/java/com/github/kuangcp/proxy/salary/aop/xml/PersonDaoImpl.java @@ -7,25 +7,18 @@ public class PersonDaoImpl { public void savePerson() { - // TODO Auto-generated method stub System.out.println("save person"); } - public void updatePerson() { - // TODO Auto-generated method stub System.out.println("update person"); } - public void deletePerson() { - // TODO Auto-generated method stub System.out.println("delete person"); } - public List getPerson() { - // TODO Auto-generated method stub Person person = new Person(); person.setPid(1L); @@ -37,5 +30,4 @@ public List getPerson() { } return personList; } - } diff --git a/spring/src/test/java/com/github/kuangcp/proxy/salary/aop/xml/PersonTest.java b/spring/src/test/java/com/github/kuangcp/proxy/salary/aop/xml/PersonTest.java index 47dff422..922443e8 100644 --- a/spring/src/test/java/com/github/kuangcp/proxy/salary/aop/xml/PersonTest.java +++ b/spring/src/test/java/com/github/kuangcp/proxy/salary/aop/xml/PersonTest.java @@ -19,8 +19,9 @@ */ public class PersonTest extends SpringHelper { - static { - path = "cn/itcast/spring0909/aop/xml/applicationContext.xml"; + @Override + public String getXmlPath() { + return "proxy/salary/applicationContext.xml"; } @Test diff --git a/spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/Interceptor.java b/spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/Interceptor.java new file mode 100644 index 00000000..0c01757b --- /dev/null +++ b/spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/Interceptor.java @@ -0,0 +1,9 @@ +package com.github.kuangcp.proxy.salary.jdkproxy; + +/** + * 切面的总的接口 + */ +public interface Interceptor { + + void interceptor(); +} diff --git a/spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/Logger.java b/spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/Logger.java index 48ab21ba..8bcf254f 100644 --- a/spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/Logger.java +++ b/spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/Logger.java @@ -1,7 +1,12 @@ package com.github.kuangcp.proxy.salary.jdkproxy; -public class Logger { - public void logging(){ - System.out.println("logging"); - } +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class Logger implements Interceptor { + + @Override + public void interceptor() { + log.info("logging"); + } } diff --git a/spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/Privilege.java b/spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/Privilege.java index 9c856c65..45b587ff 100644 --- a/spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/Privilege.java +++ b/spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/Privilege.java @@ -1,11 +1,12 @@ package com.github.kuangcp.proxy.salary.jdkproxy; -public class Privilege { - private String access; - public String getAccess() { - return access; - } - public void setAccess(String access) { - this.access = access; - } +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class Privilege implements Interceptor { + + @Override + public void interceptor() { + log.info("privilege"); + } } diff --git a/spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/ProxyTest.java b/spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/ProxyTest.java index 55154cd3..fa77ffb7 100644 --- a/spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/ProxyTest.java +++ b/spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/ProxyTest.java @@ -1,34 +1,39 @@ package com.github.kuangcp.proxy.salary.jdkproxy; import java.lang.reflect.Proxy; +import java.util.ArrayList; +import java.util.List; import org.junit.Test; -/** - * 1、拦截器的作用究竟是什么? - * * 给目标类及其他的类赋值 - * * 拦截器中的invoke方法的内容就是代理对象的方法的内容 - * 2、代理对象的方法体是什么? - * 就是拦截器中invoke方法中的内容 - * 3、在拦截器中有一个方法为invoke方法的第二个参数method,该参数是什么时候传递进去的 - * 代理对象调用方法的时候,就进入了拦截器中invoke方法中 - * 4、动态代理模式到底解决了什么问题 - * @author Administrator - */ public class ProxyTest { - @Test - public void test(){ - Logger logger = new Logger(); - Privilege privilege = new Privilege(); - privilege.setAccess("aaaa"); - Security security = new Security(); - SalaryManager target = new SalaryManagerImpl(); - SalaryInterceptor interceptor = new SalaryInterceptor(logger, security, privilege, target); - /** - * 1、目标类的类加载器 - * 2、目标类的所有的接口 - * 3、拦截器 - */ - SalaryManager proxy = (SalaryManager) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), interceptor); - proxy.showSalary();//代理对象的代理方法 - } + + + @Test + public void test() { + SalaryManager target = new SalaryManagerImpl(); + // 代理对象 + SalaryInterceptor interceptor = new SalaryInterceptor(target, getInterceptors()); + + /* + * 1、目标类的类加载器 + * 2、目标类的所有的接口 + * 3、拦截器 + */ + SalaryManager proxy = (SalaryManager) Proxy.newProxyInstance(target.getClass().getClassLoader(), + target.getClass().getInterfaces(), interceptor); + proxy.showSalary();//代理对象的代理方法 + } + + private List getInterceptors() { + Logger logger = new Logger(); + Privilege privilege = new Privilege(); + Security security = new Security(); + + List interceptorList = new ArrayList<>(); + + interceptorList.add(logger); + interceptorList.add(privilege); + interceptorList.add(security); + return interceptorList; + } } diff --git a/spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/Readme.md b/spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/Readme.md new file mode 100644 index 00000000..43e4cb4a --- /dev/null +++ b/spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/Readme.md @@ -0,0 +1,3 @@ +# JDK 中的代理来实现 + +Proxy diff --git a/spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/SalaryInterceptor.java b/spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/SalaryInterceptor.java index e9238e02..c7b9114c 100644 --- a/spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/SalaryInterceptor.java +++ b/spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/SalaryInterceptor.java @@ -2,44 +2,37 @@ import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; +import java.util.List; +import lombok.extern.slf4j.Slf4j; /** * 1、把日志、安全性框架、权限导入进去 * 2、把目标类导入进去 * 3、上述两类通过构造函数赋值 - * @author Administrator * + * @author Administrator */ -public class SalaryInterceptor implements InvocationHandler{ - - private Logger logger; - private Security security; - private Privilege privilege; - - private Object target; - - public SalaryInterceptor(Logger logger,Security security,Privilege privilege,Object target){ - this.logger = logger; - this.security = security; - this.privilege = privilege; - this.target = target; - } +@Slf4j +public class SalaryInterceptor implements InvocationHandler { + + private List interceptorList; + + private Object target; - @Override - public Object invoke(Object proxy, Method method, Object[] args) - throws Throwable { - // TODO Auto-generated method stub - System.out.println("aaaaaa"); - this.logger.logging(); - this.security.security(); - if("admin".equals(this.privilege.getAccess())){ - //调用目标类的目标方法 - method.invoke(this.target, args); - }else{ - System.out.println("您没有该权限"); - } - System.out.println("bbbbbb"); - return null; - } + public SalaryInterceptor(Object target, List interceptorList) { + this.target = target; + this.interceptorList = interceptorList; + } + @Override + public Object invoke(Object proxy, Method method, Object[] args) + throws Throwable { + // 执行所有的切面中的通知 + for (Interceptor interceptor : interceptorList) { + interceptor.interceptor(); + } + log.debug("invoke real method"); + method.invoke(this.target, args); + return null; + } } diff --git a/spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/SalaryManager.java b/spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/SalaryManager.java index 7df3af63..a4891477 100644 --- a/spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/SalaryManager.java +++ b/spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/SalaryManager.java @@ -1,5 +1,6 @@ package com.github.kuangcp.proxy.salary.jdkproxy; public interface SalaryManager { - public void showSalary(); + + void showSalary(); } diff --git a/spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/SalaryManagerImpl.java b/spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/SalaryManagerImpl.java index d5941459..0384d7ba 100644 --- a/spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/SalaryManagerImpl.java +++ b/spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/SalaryManagerImpl.java @@ -1,8 +1,12 @@ package com.github.kuangcp.proxy.salary.jdkproxy; -public class SalaryManagerImpl implements SalaryManager{ - @Override - public void showSalary() { - System.out.println("正在查看工资:哦哦,涨了2W闽比"); - } +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class SalaryManagerImpl implements SalaryManager { + + @Override + public void showSalary() { + log.info("reading salary detail"); + } } diff --git a/spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/Security.java b/spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/Security.java index 5e7c8046..b2d0c225 100644 --- a/spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/Security.java +++ b/spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/Security.java @@ -1,7 +1,12 @@ package com.github.kuangcp.proxy.salary.jdkproxy; -public class Security { - public void security(){ - System.out.println("security"); +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class Security implements Interceptor{ + + @Override + public void interceptor() { + log.info("security control"); } } diff --git a/spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/aspects/Interceptor.java b/spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/aspects/Interceptor.java deleted file mode 100644 index 67b26de5..00000000 --- a/spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/aspects/Interceptor.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.github.kuangcp.proxy.salary.jdkproxy.aspects; -/** - * 切面的总的接口 - * @author Administrator - * - */ -public interface Interceptor { - public void interceptor(); -} diff --git a/spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/aspects/Logger.java b/spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/aspects/Logger.java deleted file mode 100644 index 9fc17d2a..00000000 --- a/spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/aspects/Logger.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.github.kuangcp.proxy.salary.jdkproxy.aspects; - -public class Logger implements Interceptor{ - - @Override - public void interceptor() { - // TODO Auto-generated method stub - System.out.println("logging"); - } - -} diff --git a/spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/aspects/Privilege.java b/spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/aspects/Privilege.java deleted file mode 100644 index 565ac544..00000000 --- a/spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/aspects/Privilege.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.github.kuangcp.proxy.salary.jdkproxy.aspects; - -public class Privilege implements Interceptor{ - - @Override - public void interceptor() { - // TODO Auto-generated method stub - System.out.println("privilege"); - } - -} diff --git a/spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/aspects/ProxyTest.java b/spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/aspects/ProxyTest.java deleted file mode 100644 index fa48343c..00000000 --- a/spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/aspects/ProxyTest.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.github.kuangcp.proxy.salary.jdkproxy.aspects; - -import java.lang.reflect.Proxy; -import java.util.ArrayList; -import java.util.List; -import org.junit.Test; - -public class ProxyTest { - @Test - public void test(){ - Logger logger = new Logger(); - Privilege privilege = new Privilege(); - Security security = new Security(); - List interceptorList = new ArrayList(); - interceptorList.add(logger); - interceptorList.add(privilege); - interceptorList.add(security); - SalaryManager target = new SalaryManagerImpl(); - SalaryInterceptor interceptor = new SalaryInterceptor(logger, security, privilege, target,interceptorList); - /** - * 1、目标类的类加载器 - * 2、目标类的所有的接口 - * 3、拦截器 - */ - SalaryManager proxy = (SalaryManager) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), interceptor); - proxy.showSalary();//代理对象的代理方法 - } -} diff --git a/spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/aspects/SalaryInterceptor.java b/spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/aspects/SalaryInterceptor.java deleted file mode 100644 index 2b238a09..00000000 --- a/spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/aspects/SalaryInterceptor.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.github.kuangcp.proxy.salary.jdkproxy.aspects; - -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Method; -import java.util.List; - -/** - * 1、把日志、安全性框架、权限导入进去 - * 2、把目标类导入进去 - * 3、上述两类通过构造函数赋值 - * @author Administrator - * - */ -public class SalaryInterceptor implements InvocationHandler{ - - private Logger logger; - private Security security; - private Privilege privilege; - - private List interceptorList; - - private Object target; - - public SalaryInterceptor(Logger logger,Security security,Privilege privilege,Object target,List interceptorList){ - this.logger = logger; - this.security = security; - this.privilege = privilege; - this.target = target; - this.interceptorList = interceptorList; - } - - @Override - public Object invoke(Object proxy, Method method, Object[] args) - throws Throwable { - // TODO Auto-generated method stub - /** - * 执行所有的切面中的通知 - */ - for(Interceptor interceptor:interceptorList){ - interceptor.interceptor(); - } - method.invoke(this.target, args); - return null; - } - -} diff --git a/spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/aspects/SalaryManager.java b/spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/aspects/SalaryManager.java deleted file mode 100644 index 7cb9020e..00000000 --- a/spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/aspects/SalaryManager.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.github.kuangcp.proxy.salary.jdkproxy.aspects; - -public interface SalaryManager { - public void showSalary(); -} diff --git a/spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/aspects/SalaryManagerImpl.java b/spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/aspects/SalaryManagerImpl.java deleted file mode 100644 index 60b05386..00000000 --- a/spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/aspects/SalaryManagerImpl.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.github.kuangcp.proxy.salary.jdkproxy.aspects; - -public class SalaryManagerImpl implements SalaryManager{ - @Override - public void showSalary() { - System.out.println("正在查看工资:哦哦,涨了2W闽比"); - } -} diff --git a/spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/aspects/Security.java b/spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/aspects/Security.java deleted file mode 100644 index 5a252f6b..00000000 --- a/spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/aspects/Security.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.github.kuangcp.proxy.salary.jdkproxy.aspects; - -public class Security implements Interceptor{ - - @Override - public void interceptor() { - // TODO Auto-generated method stub - System.out.println("security"); - } - -} diff --git a/spring/src/test/java/com/github/kuangcp/proxy/salary/Logger.java b/spring/src/test/java/com/github/kuangcp/proxy/salary/origin/Logger.java similarity index 65% rename from spring/src/test/java/com/github/kuangcp/proxy/salary/Logger.java rename to spring/src/test/java/com/github/kuangcp/proxy/salary/origin/Logger.java index 529d2966..bdcb0f71 100644 --- a/spring/src/test/java/com/github/kuangcp/proxy/salary/Logger.java +++ b/spring/src/test/java/com/github/kuangcp/proxy/salary/origin/Logger.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.proxy.salary; +package com.github.kuangcp.proxy.salary.origin; public class Logger { diff --git a/spring/src/test/java/com/github/kuangcp/proxy/salary/Privilege.java b/spring/src/test/java/com/github/kuangcp/proxy/salary/origin/Privilege.java similarity index 78% rename from spring/src/test/java/com/github/kuangcp/proxy/salary/Privilege.java rename to spring/src/test/java/com/github/kuangcp/proxy/salary/origin/Privilege.java index 01e6291b..c371ae18 100644 --- a/spring/src/test/java/com/github/kuangcp/proxy/salary/Privilege.java +++ b/spring/src/test/java/com/github/kuangcp/proxy/salary/origin/Privilege.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.proxy.salary; +package com.github.kuangcp.proxy.salary.origin; public class Privilege { diff --git a/spring/src/test/java/com/github/kuangcp/proxy/salary/origin/Readme.md b/spring/src/test/java/com/github/kuangcp/proxy/salary/origin/Readme.md new file mode 100644 index 00000000..6f89635d --- /dev/null +++ b/spring/src/test/java/com/github/kuangcp/proxy/salary/origin/Readme.md @@ -0,0 +1 @@ +# 原始的紧耦合方式组合功能 diff --git a/spring/src/test/java/com/github/kuangcp/proxy/salary/SalaryManager.java b/spring/src/test/java/com/github/kuangcp/proxy/salary/origin/SalaryManager.java similarity index 92% rename from spring/src/test/java/com/github/kuangcp/proxy/salary/SalaryManager.java rename to spring/src/test/java/com/github/kuangcp/proxy/salary/origin/SalaryManager.java index 1b1dc4a3..40a1bab6 100644 --- a/spring/src/test/java/com/github/kuangcp/proxy/salary/SalaryManager.java +++ b/spring/src/test/java/com/github/kuangcp/proxy/salary/origin/SalaryManager.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.proxy.salary; +package com.github.kuangcp.proxy.salary.origin; public class SalaryManager { diff --git a/spring/src/test/java/com/github/kuangcp/proxy/salary/SalaryTest.java b/spring/src/test/java/com/github/kuangcp/proxy/salary/origin/SalaryTest.java similarity index 88% rename from spring/src/test/java/com/github/kuangcp/proxy/salary/SalaryTest.java rename to spring/src/test/java/com/github/kuangcp/proxy/salary/origin/SalaryTest.java index de99dab0..85a2025c 100644 --- a/spring/src/test/java/com/github/kuangcp/proxy/salary/SalaryTest.java +++ b/spring/src/test/java/com/github/kuangcp/proxy/salary/origin/SalaryTest.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.proxy.salary; +package com.github.kuangcp.proxy.salary.origin; import org.junit.Test; diff --git a/spring/src/test/java/com/github/kuangcp/proxy/salary/Security.java b/spring/src/test/java/com/github/kuangcp/proxy/salary/origin/Security.java similarity index 66% rename from spring/src/test/java/com/github/kuangcp/proxy/salary/Security.java rename to spring/src/test/java/com/github/kuangcp/proxy/salary/origin/Security.java index cda622c4..49dbc0fe 100644 --- a/spring/src/test/java/com/github/kuangcp/proxy/salary/Security.java +++ b/spring/src/test/java/com/github/kuangcp/proxy/salary/origin/Security.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.proxy.salary; +package com.github.kuangcp.proxy.salary.origin; public class Security { diff --git a/spring/src/test/java/com/github/kuangcp/proxy/salary/proxy/ProxyTest.java b/spring/src/test/java/com/github/kuangcp/proxy/salary/proxy/ProxyTest.java index 51630b54..121c9b09 100644 --- a/spring/src/test/java/com/github/kuangcp/proxy/salary/proxy/ProxyTest.java +++ b/spring/src/test/java/com/github/kuangcp/proxy/salary/proxy/ProxyTest.java @@ -3,14 +3,15 @@ import org.junit.Test; public class ProxyTest { - @Test - public void test(){ - Logger logger = new Logger(); - Privilege privilege = new Privilege(); - privilege.setAccess("asdf"); - Security security = new Security(); - SalaryManager target = new SalaryManagerImpl(); - SalaryManagerProxy proxy = new SalaryManagerProxy(logger, security, privilege, target); - proxy.showSalary(); - } + + @Test + public void test() { + Logger logger = new Logger(); + Privilege privilege = new Privilege(); + privilege.setAccess("admin"); + Security security = new Security(); + SalaryManager target = new SalaryManagerImpl(); + SalaryManagerProxy proxy = new SalaryManagerProxy(logger, security, privilege, target); + proxy.showSalary(); + } } diff --git a/spring/src/test/java/com/github/kuangcp/proxy/salary/proxy/Readme.md b/spring/src/test/java/com/github/kuangcp/proxy/salary/proxy/Readme.md index ba93525d..d88ac9b9 100644 --- a/spring/src/test/java/com/github/kuangcp/proxy/salary/proxy/Readme.md +++ b/spring/src/test/java/com/github/kuangcp/proxy/salary/proxy/Readme.md @@ -1,7 +1,3 @@ -没有使用其他类只用创建的类来实现 +# 手动实现代理 - -将基本增强 -目标类 -用一个 实现了目标类的接口的类来实现业务逻辑 做代理 -耦合度高,不易扩展 \ No newline at end of file +增强类 目标类 实现相同接口,目标类组合入代理类 耦合度高,不易扩展 \ No newline at end of file diff --git a/spring/src/test/java/com/github/kuangcp/proxy/salary/proxy/SalaryManagerImpl.java b/spring/src/test/java/com/github/kuangcp/proxy/salary/proxy/SalaryManagerImpl.java index 78124a38..a2a57e94 100644 --- a/spring/src/test/java/com/github/kuangcp/proxy/salary/proxy/SalaryManagerImpl.java +++ b/spring/src/test/java/com/github/kuangcp/proxy/salary/proxy/SalaryManagerImpl.java @@ -4,6 +4,6 @@ public class SalaryManagerImpl implements SalaryManager { @Override public void showSalary() { - System.out.println("正在查看工资:哦哦,涨了2W闽比"); + System.out.println("read salary"); } } diff --git a/spring/src/test/java/com/github/kuangcp/proxy/salary/proxy/SalaryManagerProxy.java b/spring/src/test/java/com/github/kuangcp/proxy/salary/proxy/SalaryManagerProxy.java index e54807b6..2d9e6a2c 100644 --- a/spring/src/test/java/com/github/kuangcp/proxy/salary/proxy/SalaryManagerProxy.java +++ b/spring/src/test/java/com/github/kuangcp/proxy/salary/proxy/SalaryManagerProxy.java @@ -2,33 +2,33 @@ /** * 1、导入日志、安全性框架、权限 2、目标对象 3、在代理对象的方法中调用上述的方法 - * + * * @author Administrator - * */ public class SalaryManagerProxy implements SalaryManager { - private Logger logger; - private Security security; - private Privilege privilege; - private SalaryManager target; - public SalaryManagerProxy(Logger logger, Security security, - Privilege privilege, SalaryManager target) { - this.logger = logger; - this.security = security; - this.privilege = privilege; - this.target = target; - } + private Logger logger; + private Security security; + private Privilege privilege; + private SalaryManager target; - @Override - public void showSalary() { - this.logger.logging();// 启动日志 - this.security.security();// 安全性框架 - if ("admin".equals(this.privilege.getAccess())) { - this.target.showSalary(); - } else { - System.out.println("您没有权限"); - } - } + public SalaryManagerProxy(Logger logger, Security security, + Privilege privilege, SalaryManager target) { + this.logger = logger; + this.security = security; + this.privilege = privilege; + this.target = target; + } + + @Override + public void showSalary() { + this.logger.logging();// 启动日志 + this.security.security();// 安全性框架 + if ("admin".equals(this.privilege.getAccess())) { + this.target.showSalary(); + } else { + System.out.println("您没有权限"); + } + } } diff --git a/spring/src/test/java/com/github/kuangcp/util/SpringHelper.java b/spring/src/test/java/com/github/kuangcp/util/SpringHelper.java index 147b3757..2c3ea1f1 100644 --- a/spring/src/test/java/com/github/kuangcp/util/SpringHelper.java +++ b/spring/src/test/java/com/github/kuangcp/util/SpringHelper.java @@ -4,13 +4,14 @@ import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; -public class SpringHelper { +public abstract class SpringHelper { public static ApplicationContext context; - public static String path; + + public abstract String getXmlPath(); @Before public void startSpring() { - context = new ClassPathXmlApplicationContext(path); + context = new ClassPathXmlApplicationContext(getXmlPath()); } } diff --git a/spring/src/test/java/com/github/kuangcp/proxy/salary/aop/xml/applicationContext.xml b/spring/src/test/resources/proxy.salary/applicationContext.xml similarity index 100% rename from spring/src/test/java/com/github/kuangcp/proxy/salary/aop/xml/applicationContext.xml rename to spring/src/test/resources/proxy.salary/applicationContext.xml From 4f3effe22d71156a1cf8dfda9c070d3c2176dbbe Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Tue, 17 Sep 2019 23:12:57 +0800 Subject: [PATCH 101/476] x) config right --- .../com/github/kuangcp/aop/ExceptionTest.java | 2 +- .../kuangcp/proxy/salary/aop/xml/Person.java | 26 +----- .../proxy/salary/aop/xml/PersonDao.java | 2 - .../proxy/salary/aop/xml/PersonDaoImpl.java | 23 ++--- .../proxy/salary/aop/xml/PersonTest.java | 26 +++--- .../proxy/salary/aop/xml/Transaction.java | 92 ++++++++++--------- .../salary}/applicationContext.xml | 31 +++---- 7 files changed, 89 insertions(+), 113 deletions(-) rename spring/src/test/resources/{proxy.salary => proxy/salary}/applicationContext.xml (85%) diff --git a/spring/src/test/java/com/github/kuangcp/aop/ExceptionTest.java b/spring/src/test/java/com/github/kuangcp/aop/ExceptionTest.java index 87c95ba5..6cd74128 100644 --- a/spring/src/test/java/com/github/kuangcp/aop/ExceptionTest.java +++ b/spring/src/test/java/com/github/kuangcp/aop/ExceptionTest.java @@ -8,7 +8,7 @@ public class ExceptionTest extends SpringHelper { @Override public String getXmlPath() { - return "proxy/salary/applicationContext.xml"; + return "aop/exception/applicationContext-exception.xml"; } @Test diff --git a/spring/src/test/java/com/github/kuangcp/proxy/salary/aop/xml/Person.java b/spring/src/test/java/com/github/kuangcp/proxy/salary/aop/xml/Person.java index 540dba98..5e4eef37 100644 --- a/spring/src/test/java/com/github/kuangcp/proxy/salary/aop/xml/Person.java +++ b/spring/src/test/java/com/github/kuangcp/proxy/salary/aop/xml/Person.java @@ -1,32 +1,10 @@ package com.github.kuangcp.proxy.salary.aop.xml; -import java.lang.reflect.Method; -import org.junit.Test; +import lombok.Data; +@Data public class Person { private Long pid; private String pname; - - public Long getPid() throws Exception { - return pid; - } - - public void setPid(Long pid) { - this.pid = pid; - } - - public String getPname() { - return pname; - } - - public void setPname(String pname) { - this.pname = pname; - } - - @Test - public void test() throws Exception { - Method method = Person.class.getMethod("getPid"); - System.out.println(method.toString()); - } } diff --git a/spring/src/test/java/com/github/kuangcp/proxy/salary/aop/xml/PersonDao.java b/spring/src/test/java/com/github/kuangcp/proxy/salary/aop/xml/PersonDao.java index 1f639b55..fa97c3e6 100644 --- a/spring/src/test/java/com/github/kuangcp/proxy/salary/aop/xml/PersonDao.java +++ b/spring/src/test/java/com/github/kuangcp/proxy/salary/aop/xml/PersonDao.java @@ -4,8 +4,6 @@ /** * 目标接口 - * - * @author Administrator */ public interface PersonDao { diff --git a/spring/src/test/java/com/github/kuangcp/proxy/salary/aop/xml/PersonDaoImpl.java b/spring/src/test/java/com/github/kuangcp/proxy/salary/aop/xml/PersonDaoImpl.java index c9be0895..bb8c5757 100644 --- a/spring/src/test/java/com/github/kuangcp/proxy/salary/aop/xml/PersonDaoImpl.java +++ b/spring/src/test/java/com/github/kuangcp/proxy/salary/aop/xml/PersonDaoImpl.java @@ -1,33 +1,26 @@ package com.github.kuangcp.proxy.salary.aop.xml; -import java.util.ArrayList; +import java.util.Collections; import java.util.List; +import lombok.extern.slf4j.Slf4j; +@Slf4j public class PersonDaoImpl { - public void savePerson() { - System.out.println("save person"); + log.info("save person"); } public void updatePerson() { - System.out.println("update person"); + log.info("update person"); } public void deletePerson() { - System.out.println("delete person"); + log.info("delete person"); } public List getPerson() { - Person person = new Person(); - - person.setPid(1L); - person.setPname("aaa"); - List personList = new ArrayList(); - personList.add(person); - for (Person person2 : personList) { - System.out.println(person2.getPname()); - } - return personList; + log.info("getPerson"); + return Collections.emptyList(); } } diff --git a/spring/src/test/java/com/github/kuangcp/proxy/salary/aop/xml/PersonTest.java b/spring/src/test/java/com/github/kuangcp/proxy/salary/aop/xml/PersonTest.java index 922443e8..43d7ae6d 100644 --- a/spring/src/test/java/com/github/kuangcp/proxy/salary/aop/xml/PersonTest.java +++ b/spring/src/test/java/com/github/kuangcp/proxy/salary/aop/xml/PersonTest.java @@ -5,17 +5,15 @@ /** * 原理 - * * 加载配置文件,启动spring容器 - * * spring容器为bean创建对象 - * * 解析aop的配置,会解析切入点表达式 - * * 看纳入spring管理的那个类和切入点表达式匹配,如果匹配则会为该类创建代理对象 - * * 代理对象的方法体的形成就是目标方法+通知 - * * 客户端在context.getBean时,如果该bean有代理对象,则返回代理对象,如果没有代理对象则返回原来的对象 - * 说明: - * 如果目标类实现了接口,则spring容器会采用jdkproxy,如果目标类没有实现接口,则spring容器会采用 - * cglibproxy + * 加载配置文件,启动spring容器 + * spring容器为bean创建对象 + * 解析aop的配置,会解析切入点表达式 + * 看纳入spring管理的那个类和切入点表达式匹配,如果匹配则会为该类创建代理对象 + * 代理对象的方法体的形成就是目标方法+通知 + * 客户端在context.getBean时,如果该bean有代理对象,则返回代理对象,如果没有代理对象则返回原来的对象 * - * @author Administrator + * 说明: + * 如果目标类实现了接口,则spring容器会采用jdkproxy,如果目标类没有实现接口,则spring容器会采用 cglibproxy */ public class PersonTest extends SpringHelper { @@ -25,8 +23,14 @@ public String getXmlPath() { } @Test - public void test() { + public void testGet() { PersonDaoImpl personDao = (PersonDaoImpl) context.getBean("personDao"); personDao.getPerson(); + + personDao.updatePerson(); + + personDao.savePerson(); + + personDao.deletePerson(); } } diff --git a/spring/src/test/java/com/github/kuangcp/proxy/salary/aop/xml/Transaction.java b/spring/src/test/java/com/github/kuangcp/proxy/salary/aop/xml/Transaction.java index d0cfa455..9d336baf 100644 --- a/spring/src/test/java/com/github/kuangcp/proxy/salary/aop/xml/Transaction.java +++ b/spring/src/test/java/com/github/kuangcp/proxy/salary/aop/xml/Transaction.java @@ -1,52 +1,56 @@ package com.github.kuangcp.proxy.salary.aop.xml; import java.util.List; +import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; +@Slf4j public class Transaction { - /** - * 前置通知 - * 通过JoinPoint获取连接点的信息 - */ - public void beginTransaction(JoinPoint joinPoint){ - joinPoint.getArgs();//获取方法的参数 - String methodName = joinPoint.getSignature().getName(); - System.out.println(methodName); - System.out.println("begin transaction"); - } - /** - * 后置通知 - */ - public void commit(JoinPoint joinPoint,Object val){ - List personList = (List)val; - System.out.println(personList.size()); - System.out.println("commit"); - } - /** - * 最终通知 - */ - public void finallyMethod(){ - System.out.println("finally method"); - } - - /** - * 异常通知 - */ - public void exceptionMethod(Throwable ex){ - System.out.println(ex.getMessage()); - } - /** - * 环绕通知 - * 能控制目标方法的执行 - * @param joinPoint - * @throws Throwable - */ - public void aroundMethod(ProceedingJoinPoint joinPoint) throws Throwable{ - System.out.println("aaaa"); - String methodName = joinPoint.getSignature().getName(); - if("savePerson".equals(methodName)){ - joinPoint.proceed(); - } - } + + /** + * 前置通知 + * 通过JoinPoint获取连接点的信息 + */ + public void beginTransaction(JoinPoint joinPoint) { + joinPoint.getArgs();//获取方法的参数 + String methodName = joinPoint.getSignature().getName(); + log.info(methodName); + log.info("begin transaction"); + } + + /** + * 后置通知 + */ + public void commit(JoinPoint joinPoint, Object val) { + List personList = (List) val; + log.info("size={}", personList.size()); + log.info("commit"); + } + + /** + * 最终通知 + */ + public void finallyMethod() { + log.info("finally method"); + } + + /** + * 异常通知 + */ + public void exceptionMethod(Throwable ex) { + log.info(ex.getMessage()); + } + + /** + * 环绕通知 能控制目标方法的执行 + */ + public void aroundMethod(ProceedingJoinPoint joinPoint) throws Throwable { + log.info("before proceed {}", joinPoint.getSignature().getName()); + String methodName = joinPoint.getSignature().getName(); + if ("savePerson".equals(methodName)) { + joinPoint.proceed(); + } + log.info("after proceed {}", joinPoint.getSignature().getName()); + } } diff --git a/spring/src/test/resources/proxy.salary/applicationContext.xml b/spring/src/test/resources/proxy/salary/applicationContext.xml similarity index 85% rename from spring/src/test/resources/proxy.salary/applicationContext.xml rename to spring/src/test/resources/proxy/salary/applicationContext.xml index 9d56bb15..0f3a755c 100644 --- a/spring/src/test/resources/proxy.salary/applicationContext.xml +++ b/spring/src/test/resources/proxy/salary/applicationContext.xml @@ -13,12 +13,14 @@ 4、拦截器 由spring内部实现 5、aop的配置 --> - - + + + + + + + - + + + @@ -61,16 +63,13 @@ - + + - + + + \ No newline at end of file From c342805da438fac7ca01416d028419834f4f9a33 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Tue, 17 Sep 2019 23:24:50 +0800 Subject: [PATCH 102/476] *) optimized --- .../dao/base => aop/common}/Person.java | 2 +- .../github/kuangcp/aop/common}/PersonDao.java | 4 +-- .../proxy/dao/base/CustomInterceptor.java | 9 +++---- .../kuangcp/proxy/dao/jdkproxy/PersonDao.java | 2 +- .../proxy/dao/jdkproxy/PersonDaoImpl.java | 2 +- .../github/kuangcp/aop/annotation/Person.java | 27 ------------------- .../kuangcp/aop/annotation/PersonDaoImpl.java | 18 ++++--------- .../kuangcp/aop/annotation/PersonTest.java | 4 +-- .../kuangcp/aop/annotation/Transaction.java | 12 +++++---- .../aop/{ => exception}/ExceptionTest.java | 2 +- .../kuangcp/aop/exception/MyException.java | 16 ++++++----- .../github/kuangcp/aop/exception/Readme.md | 2 +- .../kuangcp/aop/exception/dao/PersonDao.java | 11 ++++---- .../aop/exception/dao/PersonDaoImpl.java | 4 --- .../aop/exception/service/PersonService.java | 7 ++--- .../exception/service/PersonServiceImpl.java | 3 --- .../aop/annotation/applicationContext.xml | 18 +++++++------ .../applicationContext-exception.xml | 2 +- 18 files changed, 53 insertions(+), 92 deletions(-) rename spring/src/main/java/com/github/kuangcp/{proxy/dao/base => aop/common}/Person.java (69%) rename spring/src/{test/java/com/github/kuangcp/aop/annotation => main/java/com/github/kuangcp/aop/common}/PersonDao.java (71%) delete mode 100644 spring/src/test/java/com/github/kuangcp/aop/annotation/Person.java rename spring/src/test/java/com/github/kuangcp/aop/{ => exception}/ExceptionTest.java (91%) rename spring/src/test/{java/com/github/kuangcp => resources}/aop/annotation/applicationContext.xml (51%) diff --git a/spring/src/main/java/com/github/kuangcp/proxy/dao/base/Person.java b/spring/src/main/java/com/github/kuangcp/aop/common/Person.java similarity index 69% rename from spring/src/main/java/com/github/kuangcp/proxy/dao/base/Person.java rename to spring/src/main/java/com/github/kuangcp/aop/common/Person.java index 76999002..4e57f912 100644 --- a/spring/src/main/java/com/github/kuangcp/proxy/dao/base/Person.java +++ b/spring/src/main/java/com/github/kuangcp/aop/common/Person.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.proxy.dao.base; +package com.github.kuangcp.aop.common; import lombok.Data; diff --git a/spring/src/test/java/com/github/kuangcp/aop/annotation/PersonDao.java b/spring/src/main/java/com/github/kuangcp/aop/common/PersonDao.java similarity index 71% rename from spring/src/test/java/com/github/kuangcp/aop/annotation/PersonDao.java rename to spring/src/main/java/com/github/kuangcp/aop/common/PersonDao.java index 0dcdf041..4ce545f0 100644 --- a/spring/src/test/java/com/github/kuangcp/aop/annotation/PersonDao.java +++ b/spring/src/main/java/com/github/kuangcp/aop/common/PersonDao.java @@ -1,11 +1,9 @@ -package com.github.kuangcp.aop.annotation; +package com.github.kuangcp.aop.common; import java.util.List; /** * 目标接口 - * - * @author Administrator */ public interface PersonDao { diff --git a/spring/src/main/java/com/github/kuangcp/proxy/dao/base/CustomInterceptor.java b/spring/src/main/java/com/github/kuangcp/proxy/dao/base/CustomInterceptor.java index c129d08e..6d26a70c 100644 --- a/spring/src/main/java/com/github/kuangcp/proxy/dao/base/CustomInterceptor.java +++ b/spring/src/main/java/com/github/kuangcp/proxy/dao/base/CustomInterceptor.java @@ -6,11 +6,8 @@ public interface CustomInterceptor { default boolean isNeedTransaction(String methodName) { - return "savePerson".equals(methodName) || "updatePerson".equals(methodName) || - "deletePerson".equals(methodName); - } - - static boolean a() { - return true; + return "savePerson".equals(methodName) + || "updatePerson".equals(methodName) + || "deletePerson".equals(methodName); } } diff --git a/spring/src/main/java/com/github/kuangcp/proxy/dao/jdkproxy/PersonDao.java b/spring/src/main/java/com/github/kuangcp/proxy/dao/jdkproxy/PersonDao.java index 28b4b7dc..2edccc97 100644 --- a/spring/src/main/java/com/github/kuangcp/proxy/dao/jdkproxy/PersonDao.java +++ b/spring/src/main/java/com/github/kuangcp/proxy/dao/jdkproxy/PersonDao.java @@ -1,6 +1,6 @@ package com.github.kuangcp.proxy.dao.jdkproxy; -import com.github.kuangcp.proxy.dao.base.Person; +import com.github.kuangcp.aop.common.Person; import java.util.List; /** diff --git a/spring/src/main/java/com/github/kuangcp/proxy/dao/jdkproxy/PersonDaoImpl.java b/spring/src/main/java/com/github/kuangcp/proxy/dao/jdkproxy/PersonDaoImpl.java index 2c928879..19edc481 100644 --- a/spring/src/main/java/com/github/kuangcp/proxy/dao/jdkproxy/PersonDaoImpl.java +++ b/spring/src/main/java/com/github/kuangcp/proxy/dao/jdkproxy/PersonDaoImpl.java @@ -1,6 +1,6 @@ package com.github.kuangcp.proxy.dao.jdkproxy; -import com.github.kuangcp.proxy.dao.base.Person; +import com.github.kuangcp.aop.common.Person; import java.util.List; import lombok.extern.slf4j.Slf4j; diff --git a/spring/src/test/java/com/github/kuangcp/aop/annotation/Person.java b/spring/src/test/java/com/github/kuangcp/aop/annotation/Person.java deleted file mode 100644 index 0c1e718d..00000000 --- a/spring/src/test/java/com/github/kuangcp/aop/annotation/Person.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.github.kuangcp.aop.annotation; - -import java.lang.reflect.Method; -import org.junit.Test; - -public class Person { - private Long pid; - private String pname; - public Long getPid() throws Exception{ - return pid; - } - public void setPid(Long pid) { - this.pid = pid; - } - public String getPname() { - return pname; - } - public void setPname(String pname) { - this.pname = pname; - } - - @Test - public void test() throws Exception { - Method method = Person.class.getMethod("getPid"); - System.out.println(method.toString()); - } -} diff --git a/spring/src/test/java/com/github/kuangcp/aop/annotation/PersonDaoImpl.java b/spring/src/test/java/com/github/kuangcp/aop/annotation/PersonDaoImpl.java index 2d5c5f84..ce5cbaea 100644 --- a/spring/src/test/java/com/github/kuangcp/aop/annotation/PersonDaoImpl.java +++ b/spring/src/test/java/com/github/kuangcp/aop/annotation/PersonDaoImpl.java @@ -1,5 +1,6 @@ package com.github.kuangcp.aop.annotation; +import com.github.kuangcp.aop.common.Person; import java.util.ArrayList; import java.util.List; import org.springframework.stereotype.Component; @@ -7,37 +8,28 @@ @Component("personDao") public class PersonDaoImpl { - public void savePerson() { - // TODO Auto-generated method stub System.out.println("save person"); } - public void updatePerson() { - // TODO Auto-generated method stub System.out.println("update person"); } - public void deletePerson() { - // TODO Auto-generated method stub System.out.println("delete person"); } - public List getPerson() { - // TODO Auto-generated method stub Person person = new Person(); - person.setPid(1L); - person.setPname("aaa"); - List personList = new ArrayList(); + person.setId(1L); + person.setName("who"); + List personList = new ArrayList<>(); personList.add(person); for (Person person2 : personList) { - System.out.println(person2.getPname()); + System.out.println(person2.getName()); } return personList; } - } diff --git a/spring/src/test/java/com/github/kuangcp/aop/annotation/PersonTest.java b/spring/src/test/java/com/github/kuangcp/aop/annotation/PersonTest.java index 4a2b3ef9..f76d6b70 100644 --- a/spring/src/test/java/com/github/kuangcp/aop/annotation/PersonTest.java +++ b/spring/src/test/java/com/github/kuangcp/aop/annotation/PersonTest.java @@ -10,11 +10,11 @@ public class PersonTest extends SpringHelper { @Override public String getXmlPath() { - return "proxy/salary/applicationContext.xml"; + return "aop/annotation/applicationContext.xml"; } @Test - public void test() { + public void testAnnotation() { PersonDaoImpl personDao = (PersonDaoImpl) context.getBean("personDao"); personDao.getPerson(); } diff --git a/spring/src/test/java/com/github/kuangcp/aop/annotation/Transaction.java b/spring/src/test/java/com/github/kuangcp/aop/annotation/Transaction.java index 3abdacad..dc23aed4 100644 --- a/spring/src/test/java/com/github/kuangcp/aop/annotation/Transaction.java +++ b/spring/src/test/java/com/github/kuangcp/aop/annotation/Transaction.java @@ -1,23 +1,25 @@ package com.github.kuangcp.aop.annotation; +import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.springframework.stereotype.Component; /** - * @author Administrator + * */ -@Component("transaction") +@Slf4j @Aspect +@Component("transaction") public class Transaction { @Pointcut("execution(* com.github.kuangcp.aop.annotation.PersonDaoImpl.*(..))") - private void aa() { + private void aopConfig() { }//方法签名 返回值必须是void 方法的修饰符最好是private - @Before("aa()") + @Before("aopConfig()") public void beginTransaction() { - System.out.println("begin transaction"); + log.info("begin transaction"); } } diff --git a/spring/src/test/java/com/github/kuangcp/aop/ExceptionTest.java b/spring/src/test/java/com/github/kuangcp/aop/exception/ExceptionTest.java similarity index 91% rename from spring/src/test/java/com/github/kuangcp/aop/ExceptionTest.java rename to spring/src/test/java/com/github/kuangcp/aop/exception/ExceptionTest.java index 6cd74128..dfc181d4 100644 --- a/spring/src/test/java/com/github/kuangcp/aop/ExceptionTest.java +++ b/spring/src/test/java/com/github/kuangcp/aop/exception/ExceptionTest.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.aop; +package com.github.kuangcp.aop.exception; import com.github.kuangcp.aop.exception.action.PersonAction; import com.github.kuangcp.util.SpringHelper; diff --git a/spring/src/test/java/com/github/kuangcp/aop/exception/MyException.java b/spring/src/test/java/com/github/kuangcp/aop/exception/MyException.java index d9ddc02d..03e47c63 100644 --- a/spring/src/test/java/com/github/kuangcp/aop/exception/MyException.java +++ b/spring/src/test/java/com/github/kuangcp/aop/exception/MyException.java @@ -1,14 +1,16 @@ package com.github.kuangcp.aop.exception; + /** * 切面 - * @author Administrator * + * @author Administrator */ public class MyException { - /** - * 写一个通知为异常通知 - */ - public void getExcpetionMessage(Throwable ex){ - System.out.println(ex.getMessage()); - } + + /** + * 写一个通知为异常通知 + */ + public void getExceptionMessage(Throwable ex) { + System.out.println(ex.getMessage()); + } } diff --git a/spring/src/test/java/com/github/kuangcp/aop/exception/Readme.md b/spring/src/test/java/com/github/kuangcp/aop/exception/Readme.md index dba74ad2..ff53fdaa 100644 --- a/spring/src/test/java/com/github/kuangcp/aop/exception/Readme.md +++ b/spring/src/test/java/com/github/kuangcp/aop/exception/Readme.md @@ -1 +1 @@ -AOP 异常处理 \ No newline at end of file +## AOP 异常处理 \ No newline at end of file diff --git a/spring/src/test/java/com/github/kuangcp/aop/exception/dao/PersonDao.java b/spring/src/test/java/com/github/kuangcp/aop/exception/dao/PersonDao.java index f8bfc6f0..a4a927d8 100644 --- a/spring/src/test/java/com/github/kuangcp/aop/exception/dao/PersonDao.java +++ b/spring/src/test/java/com/github/kuangcp/aop/exception/dao/PersonDao.java @@ -1,10 +1,11 @@ package com.github.kuangcp.aop.exception.dao; public interface PersonDao { - public void savePerson() throws Exception; - - public void updatePerson() throws Exception; - - public void deletePerson() throws Exception; + + void savePerson() throws Exception; + + void updatePerson() throws Exception; + + void deletePerson() throws Exception; } diff --git a/spring/src/test/java/com/github/kuangcp/aop/exception/dao/PersonDaoImpl.java b/spring/src/test/java/com/github/kuangcp/aop/exception/dao/PersonDaoImpl.java index ded2e14a..d021c090 100644 --- a/spring/src/test/java/com/github/kuangcp/aop/exception/dao/PersonDaoImpl.java +++ b/spring/src/test/java/com/github/kuangcp/aop/exception/dao/PersonDaoImpl.java @@ -4,23 +4,19 @@ public class PersonDaoImpl implements PersonDao { @Override public void savePerson() throws Exception { - // TODO Auto-generated method stub System.out.println("save person"); int a = 1 / 0; } @Override public void updatePerson() throws Exception { - // TODO Auto-generated method stub System.out.println("update person"); int a = 1 / 0; } @Override public void deletePerson() throws Exception { - // TODO Auto-generated method stub System.out.println("delete person"); int a = 1 / 0; } - } diff --git a/spring/src/test/java/com/github/kuangcp/aop/exception/service/PersonService.java b/spring/src/test/java/com/github/kuangcp/aop/exception/service/PersonService.java index 4489fca8..fa15b360 100644 --- a/spring/src/test/java/com/github/kuangcp/aop/exception/service/PersonService.java +++ b/spring/src/test/java/com/github/kuangcp/aop/exception/service/PersonService.java @@ -1,10 +1,11 @@ package com.github.kuangcp.aop.exception.service; public interface PersonService { - public void savePerson() throws Exception; - public void updatePerson() throws Exception; + void savePerson() throws Exception; - public void deletePerson() throws Exception; + void updatePerson() throws Exception; + + void deletePerson() throws Exception; } diff --git a/spring/src/test/java/com/github/kuangcp/aop/exception/service/PersonServiceImpl.java b/spring/src/test/java/com/github/kuangcp/aop/exception/service/PersonServiceImpl.java index adb107a9..b3ebdacb 100644 --- a/spring/src/test/java/com/github/kuangcp/aop/exception/service/PersonServiceImpl.java +++ b/spring/src/test/java/com/github/kuangcp/aop/exception/service/PersonServiceImpl.java @@ -17,19 +17,16 @@ public void setPersonDao(PersonDao personDao) { @Override public void savePerson() throws Exception { - // TODO Auto-generated method stub this.personDao.savePerson(); } @Override public void updatePerson() throws Exception { - // TODO Auto-generated method stub this.personDao.updatePerson(); } @Override public void deletePerson() throws Exception { - // TODO Auto-generated method stub this.personDao.deletePerson(); } diff --git a/spring/src/test/java/com/github/kuangcp/aop/annotation/applicationContext.xml b/spring/src/test/resources/aop/annotation/applicationContext.xml similarity index 51% rename from spring/src/test/java/com/github/kuangcp/aop/annotation/applicationContext.xml rename to spring/src/test/resources/aop/annotation/applicationContext.xml index f638304d..44a6ee87 100644 --- a/spring/src/test/java/com/github/kuangcp/aop/annotation/applicationContext.xml +++ b/spring/src/test/resources/aop/annotation/applicationContext.xml @@ -1,16 +1,18 @@ - - - + + + + \ No newline at end of file diff --git a/spring/src/test/resources/aop/exception/applicationContext-exception.xml b/spring/src/test/resources/aop/exception/applicationContext-exception.xml index aea6f297..4001690c 100644 --- a/spring/src/test/resources/aop/exception/applicationContext-exception.xml +++ b/spring/src/test/resources/aop/exception/applicationContext-exception.xml @@ -31,7 +31,7 @@ - + \ No newline at end of file From df16260f54515b0301c41cda37ecfda175d30441 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Mon, 23 Sep 2019 00:04:27 +0800 Subject: [PATCH 103/476] *) rename class, disable log file --- common-config/src/main/resources/logback.xml | 81 ++++++++++--------- .../lambda/firstdemo/ElementAction.java | 15 ++++ .../lambda/firstdemo/ElementContainer.java | 18 +++++ .../kuangcp/lambda/firstdemo/MainTest.java | 22 ++--- .../kuangcp/lambda/firstdemo/PointAction.java | 12 --- .../lambda/firstdemo/PointArrayList.java | 16 ---- .../lambda/firstdemo/TranslateByOne.java | 6 +- 7 files changed, 88 insertions(+), 82 deletions(-) create mode 100644 java8/src/test/java/com/github/kuangcp/lambda/firstdemo/ElementAction.java create mode 100644 java8/src/test/java/com/github/kuangcp/lambda/firstdemo/ElementContainer.java delete mode 100644 java8/src/test/java/com/github/kuangcp/lambda/firstdemo/PointAction.java delete mode 100644 java8/src/test/java/com/github/kuangcp/lambda/firstdemo/PointArrayList.java diff --git a/common-config/src/main/resources/logback.xml b/common-config/src/main/resources/logback.xml index 5e375431..969c059d 100644 --- a/common-config/src/main/resources/logback.xml +++ b/common-config/src/main/resources/logback.xml @@ -1,7 +1,8 @@ - + + @@ -28,49 +29,49 @@ - - ${log.base}warn.log - true - - ${log.base}warn.%d{yyyy-MM-dd}.log - - - %d{MM-dd HH:mm:ss.SSS} |-%-5level %logger{36}:%L - %msg%n - - - WARN - - + + + + + + + + + + + + + - - ${log.base}info.log - true - - ${log.base}info.%d{yyyy-MM-dd}.log - - - %d{MM-dd HH:mm:ss.SSS} |-%-5level %logger{36}:%L - %msg%n - - - INFO - - + + + + + + + + + + + + + - - ${log.base}error.log - true - - ${log.base}error.%d{yyyy-MM-dd}.log - - - %d{MM-dd HH:mm:ss.SSS} | %thread |-%-5level %logger{36}:%L - %msg%n - - - ERROR - - + + + + + + + + + + + + + diff --git a/java8/src/test/java/com/github/kuangcp/lambda/firstdemo/ElementAction.java b/java8/src/test/java/com/github/kuangcp/lambda/firstdemo/ElementAction.java new file mode 100644 index 00000000..885f8286 --- /dev/null +++ b/java8/src/test/java/com/github/kuangcp/lambda/firstdemo/ElementAction.java @@ -0,0 +1,15 @@ +package com.github.kuangcp.lambda.firstdemo; + +import java.awt.Point; + +/** + * 一个接口只有一个方法,就已经是函数式接口了,注解仅标记作用 + * 等价于 Consumer 如 TranslateByOne8 的实现 + * + * @author kuangcp on 18-11-22-下午6:14 + */ +@FunctionalInterface +interface ElementAction { + + void action(Point point); +} diff --git a/java8/src/test/java/com/github/kuangcp/lambda/firstdemo/ElementContainer.java b/java8/src/test/java/com/github/kuangcp/lambda/firstdemo/ElementContainer.java new file mode 100644 index 00000000..1f2b52af --- /dev/null +++ b/java8/src/test/java/com/github/kuangcp/lambda/firstdemo/ElementContainer.java @@ -0,0 +1,18 @@ +package com.github.kuangcp.lambda.firstdemo; + +import java.awt.Point; +import java.util.ArrayList; + +/** + * 对所有元素 执行逻辑 + * + * @author kuangcp on 18-11-22-下午6:13 + */ +class ElementContainer extends ArrayList { + + void forEach(ElementAction pointAction) { + for (T point : this) { + pointAction.action(point); + } + } +} diff --git a/java8/src/test/java/com/github/kuangcp/lambda/firstdemo/MainTest.java b/java8/src/test/java/com/github/kuangcp/lambda/firstdemo/MainTest.java index 1c599042..5d36d3c5 100644 --- a/java8/src/test/java/com/github/kuangcp/lambda/firstdemo/MainTest.java +++ b/java8/src/test/java/com/github/kuangcp/lambda/firstdemo/MainTest.java @@ -2,16 +2,14 @@ import java.awt.Point; import java.util.function.Consumer; +import lombok.extern.slf4j.Slf4j; import org.junit.Before; import org.junit.Test; -/** - * Created by Administrator on 2017/05/09 - * TODO 整理 - */ +@Slf4j public class MainTest { - private PointArrayList pointArrayList = new PointArrayList(); + private ElementContainer pointArrayList = new ElementContainer<>(); @Before public void init() { @@ -25,16 +23,16 @@ public void testByPolymorphic() { pointArrayList.forEach(new TranslateByOne()); for (Point point : pointArrayList) { - System.out.println("for each " + point.toString()); + log.info("for each " + point.toString()); } } - // 2. 利用java8的 Consumer 的accept方法 (针对于接口单方法的一个抽象接口) + // 2. 利用 Java8 的函数式接口 Consumer 的accept方法 (针对于接口单方法的一个抽象接口) @Test public void testConsumer() { pointArrayList.forEach(new TranslateByOne8()); for (Point point : pointArrayList) { - System.out.println("Java8\t " + point.toString()); + log.info("Java8\t " + point.toString()); } } @@ -45,7 +43,7 @@ public void testAnonymousInnerClass() { @Override public void accept(Point point) { point.translate(1, 1); - System.out.println("AnonymousInnerClass " + point); + log.info("AnonymousInnerClass " + point); } }); } @@ -53,11 +51,13 @@ public void accept(Point point) { // 4. lambda 表达式 @Test public void testLambda() { - pointArrayList.forEach((Consumer) point -> point.translate(1, 1)); +// pointArrayList.forEach((Consumer) point -> point.translate(1, 1)); + pointArrayList.forEach((ElementAction) point -> point.translate(2, 1)); + // 将lambda参数列表映射为假想的方法的参数列表 // 裁剪了匿名内部类定义的额外代码,实现lambda for (Point point : pointArrayList) { - System.out.println("lambda\t " + point.toString()); + log.info("lambda\t " + point.toString()); } } } diff --git a/java8/src/test/java/com/github/kuangcp/lambda/firstdemo/PointAction.java b/java8/src/test/java/com/github/kuangcp/lambda/firstdemo/PointAction.java deleted file mode 100644 index 752a5fbb..00000000 --- a/java8/src/test/java/com/github/kuangcp/lambda/firstdemo/PointAction.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.github.kuangcp.lambda.firstdemo; - -import java.awt.Point; - -/** - * @author kuangcp on 18-11-22-下午6:14 - * 自定义接口和方法 - */ -interface PointAction { - - void doForPoint(Point point); -} diff --git a/java8/src/test/java/com/github/kuangcp/lambda/firstdemo/PointArrayList.java b/java8/src/test/java/com/github/kuangcp/lambda/firstdemo/PointArrayList.java deleted file mode 100644 index ca70b32a..00000000 --- a/java8/src/test/java/com/github/kuangcp/lambda/firstdemo/PointArrayList.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.github.kuangcp.lambda.firstdemo; - -import java.awt.Point; -import java.util.ArrayList; - -/** - * @author kuangcp on 18-11-22-下午6:13 - */ -class PointArrayList extends ArrayList { - - void forEach(PointAction pointAction) { - for (Point point : this) { - pointAction.doForPoint(point); - } - } -} diff --git a/java8/src/test/java/com/github/kuangcp/lambda/firstdemo/TranslateByOne.java b/java8/src/test/java/com/github/kuangcp/lambda/firstdemo/TranslateByOne.java index e1dd6929..685030f5 100644 --- a/java8/src/test/java/com/github/kuangcp/lambda/firstdemo/TranslateByOne.java +++ b/java8/src/test/java/com/github/kuangcp/lambda/firstdemo/TranslateByOne.java @@ -5,10 +5,10 @@ /** * @author kuangcp on 18-11-22-下午6:14 */ -class TranslateByOne implements PointAction { +class TranslateByOne implements ElementAction { @Override - public void doForPoint(Point point) { - point.translate(1, 1); + public void action(Point point) { + point.translate(1, 2); } } \ No newline at end of file From 06bc1afb7208213d2d035bf46e1cf80bae48feef Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Sun, 29 Sep 2019 02:07:27 +0800 Subject: [PATCH 104/476] +) learn the diffrent for proxy and aop --- common-config/src/main/resources/logback.xml | 2 +- .../proxy/dao/base/CustomInterceptor.java | 13 --- .../kuangcp/proxy/dao/base/Transaction.java | 15 ---- .../proxy/dao/cglibproxy/PersonDaoImpl.java | 11 +++ ...eptor.java => TransactionInterceptor.java} | 27 +++---- .../proxy/dao/common/InterceptorLogic.java | 51 ++++++++++++ .../kuangcp/proxy/dao/common/Permission.java | 15 ++++ .../kuangcp/proxy/dao/common/Transaction.java | 19 +++++ .../dao/jdkproxy/PermissionInterceptor.java | 28 +++++++ .../kuangcp/proxy/dao/jdkproxy/PersonDao.java | 4 +- .../proxy/dao/jdkproxy/PersonDaoImpl.java | 4 +- .../dao/jdkproxy/PersonDaoInterceptor.java | 37 --------- .../dao/jdkproxy/TransactionInterceptor.java | 28 +++++++ .../proxy/dao/cglibproxy/CGlibProxyTest.java | 13 ++- .../proxy/dao/jdkproxy/JDKProxyTest.java | 80 ++++++++++++++++--- .../kuangcp/proxy/dao/jdkproxy/Readme.md | 16 ++-- 16 files changed, 252 insertions(+), 111 deletions(-) delete mode 100644 spring/src/main/java/com/github/kuangcp/proxy/dao/base/CustomInterceptor.java delete mode 100644 spring/src/main/java/com/github/kuangcp/proxy/dao/base/Transaction.java rename spring/src/main/java/com/github/kuangcp/proxy/dao/cglibproxy/{PersonDaoInterceptor.java => TransactionInterceptor.java} (51%) create mode 100644 spring/src/main/java/com/github/kuangcp/proxy/dao/common/InterceptorLogic.java create mode 100644 spring/src/main/java/com/github/kuangcp/proxy/dao/common/Permission.java create mode 100644 spring/src/main/java/com/github/kuangcp/proxy/dao/common/Transaction.java create mode 100644 spring/src/main/java/com/github/kuangcp/proxy/dao/jdkproxy/PermissionInterceptor.java delete mode 100644 spring/src/main/java/com/github/kuangcp/proxy/dao/jdkproxy/PersonDaoInterceptor.java create mode 100644 spring/src/main/java/com/github/kuangcp/proxy/dao/jdkproxy/TransactionInterceptor.java diff --git a/common-config/src/main/resources/logback.xml b/common-config/src/main/resources/logback.xml index 969c059d..787da32b 100644 --- a/common-config/src/main/resources/logback.xml +++ b/common-config/src/main/resources/logback.xml @@ -6,7 +6,7 @@ - %d{HH:mm:ss.SSS} %highlight(%-5level) [%thread] %logger{30}:%yellow(%-3L) %msg%n + %d{HH:mm:ss.SSS} %highlight(%-5level) [%thread] %logger{30} %highlight(%M):%yellow(%-3L) %msg%n DEBUG diff --git a/spring/src/main/java/com/github/kuangcp/proxy/dao/base/CustomInterceptor.java b/spring/src/main/java/com/github/kuangcp/proxy/dao/base/CustomInterceptor.java deleted file mode 100644 index 6d26a70c..00000000 --- a/spring/src/main/java/com/github/kuangcp/proxy/dao/base/CustomInterceptor.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.github.kuangcp.proxy.dao.base; - -/** - * @author kuangcp on 2019-04-22 6:05 PM - */ -public interface CustomInterceptor { - - default boolean isNeedTransaction(String methodName) { - return "savePerson".equals(methodName) - || "updatePerson".equals(methodName) - || "deletePerson".equals(methodName); - } -} diff --git a/spring/src/main/java/com/github/kuangcp/proxy/dao/base/Transaction.java b/spring/src/main/java/com/github/kuangcp/proxy/dao/base/Transaction.java deleted file mode 100644 index 42821537..00000000 --- a/spring/src/main/java/com/github/kuangcp/proxy/dao/base/Transaction.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.github.kuangcp.proxy.dao.base; - -import lombok.extern.slf4j.Slf4j; - -@Slf4j -public class Transaction { - - public void beginTransaction() { - log.info("begin transaction"); - } - - public void commit() { - log.info("commit"); - } -} diff --git a/spring/src/main/java/com/github/kuangcp/proxy/dao/cglibproxy/PersonDaoImpl.java b/spring/src/main/java/com/github/kuangcp/proxy/dao/cglibproxy/PersonDaoImpl.java index 9f7b79f9..2ffb099e 100644 --- a/spring/src/main/java/com/github/kuangcp/proxy/dao/cglibproxy/PersonDaoImpl.java +++ b/spring/src/main/java/com/github/kuangcp/proxy/dao/cglibproxy/PersonDaoImpl.java @@ -1,7 +1,13 @@ package com.github.kuangcp.proxy.dao.cglibproxy; +import com.github.kuangcp.aop.common.Person; +import java.util.Collections; +import java.util.List; import lombok.extern.slf4j.Slf4j; +/** + * cglib 实现 无需实现接口 + */ @Slf4j public class PersonDaoImpl { @@ -16,4 +22,9 @@ public void updatePerson() { public void deletePerson() { log.info("delete person"); } + + public List getPerson() { + log.info("get person list"); + return Collections.emptyList(); + } } diff --git a/spring/src/main/java/com/github/kuangcp/proxy/dao/cglibproxy/PersonDaoInterceptor.java b/spring/src/main/java/com/github/kuangcp/proxy/dao/cglibproxy/TransactionInterceptor.java similarity index 51% rename from spring/src/main/java/com/github/kuangcp/proxy/dao/cglibproxy/PersonDaoInterceptor.java rename to spring/src/main/java/com/github/kuangcp/proxy/dao/cglibproxy/TransactionInterceptor.java index df6cb02f..25d07c00 100644 --- a/spring/src/main/java/com/github/kuangcp/proxy/dao/cglibproxy/PersonDaoInterceptor.java +++ b/spring/src/main/java/com/github/kuangcp/proxy/dao/cglibproxy/TransactionInterceptor.java @@ -1,22 +1,21 @@ package com.github.kuangcp.proxy.dao.cglibproxy; -import com.github.kuangcp.proxy.dao.base.CustomInterceptor; -import com.github.kuangcp.proxy.dao.base.Transaction; +import com.github.kuangcp.proxy.dao.common.InterceptorLogic; +import com.github.kuangcp.proxy.dao.common.Transaction; import java.lang.reflect.Method; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; -public class PersonDaoInterceptor implements MethodInterceptor, CustomInterceptor { +@Slf4j +@AllArgsConstructor +public class TransactionInterceptor implements MethodInterceptor { private Transaction transaction; private Object target; - public PersonDaoInterceptor(Transaction transaction, Object target) { - this.transaction = transaction; - this.target = target; - } - // cglib 方式就是动态创建一个子类 public Object createProxy() { Enhancer enhancer = new Enhancer(); @@ -30,14 +29,8 @@ public Object createProxy() { @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { - Object result; - if (isNeedTransaction(method.getName())) { - this.transaction.beginTransaction(); - result = method.invoke(this.target, method);//调用目标类的目标方法 - this.transaction.commit(); - } else { - result = method.invoke(this.target, method);//调用目标类的目标方法 - } - return result; + log.info("method {} proxy object ={}", method.getName(), proxy); + + return InterceptorLogic.transactionLogic(transaction, target, method, args); } } diff --git a/spring/src/main/java/com/github/kuangcp/proxy/dao/common/InterceptorLogic.java b/spring/src/main/java/com/github/kuangcp/proxy/dao/common/InterceptorLogic.java new file mode 100644 index 00000000..4369ff96 --- /dev/null +++ b/spring/src/main/java/com/github/kuangcp/proxy/dao/common/InterceptorLogic.java @@ -0,0 +1,51 @@ +package com.github.kuangcp.proxy.dao.common; + +import java.lang.reflect.Method; +import lombok.extern.slf4j.Slf4j; + +/** + * 代理部分公共逻辑 + * 思考将 Transaction 和 Permission 抽象一层就成为了 Aspect AOP 这套设计了 + * + * @author https://github.com/kuangcp on 2019-09-29 01:20 + */ +@Slf4j +public class InterceptorLogic { + + public static Object transactionLogic(Transaction transaction, Object target, + Method method, Object[] args) throws Throwable { + Object result; + //对指定的方法增强 + if (isNeedTransaction(method.getName())) { + transaction.beginTransaction(); + try { + //调用目标类的目标方法 + result = method.invoke(target, args); + transaction.commit(); + } catch (Exception e) { + log.error("", e); + transaction.rollback(); + throw e; + } + } else { + //调用目标类的目标方法 + result = method.invoke(target, args); + } + return result; + } + + public static Object permissionLogic(Permission permission, Object target, + Method method, Object[] args) throws Throwable { + boolean hasPermission = permission.hasPermission(target); + if (!hasPermission) { + throw new RuntimeException("forbid invoke this method: " + method.getName()); + } + return method.invoke(target, args); + } + + private static boolean isNeedTransaction(String methodName) { + return "savePerson".equals(methodName) + || "updatePerson".equals(methodName) + || "deletePerson".equals(methodName); + } +} diff --git a/spring/src/main/java/com/github/kuangcp/proxy/dao/common/Permission.java b/spring/src/main/java/com/github/kuangcp/proxy/dao/common/Permission.java new file mode 100644 index 00000000..c2241c1a --- /dev/null +++ b/spring/src/main/java/com/github/kuangcp/proxy/dao/common/Permission.java @@ -0,0 +1,15 @@ +package com.github.kuangcp.proxy.dao.common; + +import lombok.extern.slf4j.Slf4j; + +/** + * @author https://github.com/kuangcp on 2019-09-29 01:30 + */ +@Slf4j +public class Permission { + + boolean hasPermission(Object role) { + log.info("role={}", role); + return true; + } +} diff --git a/spring/src/main/java/com/github/kuangcp/proxy/dao/common/Transaction.java b/spring/src/main/java/com/github/kuangcp/proxy/dao/common/Transaction.java new file mode 100644 index 00000000..cd679bc6 --- /dev/null +++ b/spring/src/main/java/com/github/kuangcp/proxy/dao/common/Transaction.java @@ -0,0 +1,19 @@ +package com.github.kuangcp.proxy.dao.common; + +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class Transaction { + + void beginTransaction() { + log.info("begin transaction"); + } + + void commit() { + log.info("commit"); + } + + void rollback() { + log.info("rollback"); + } +} diff --git a/spring/src/main/java/com/github/kuangcp/proxy/dao/jdkproxy/PermissionInterceptor.java b/spring/src/main/java/com/github/kuangcp/proxy/dao/jdkproxy/PermissionInterceptor.java new file mode 100644 index 00000000..4de59fe6 --- /dev/null +++ b/spring/src/main/java/com/github/kuangcp/proxy/dao/jdkproxy/PermissionInterceptor.java @@ -0,0 +1,28 @@ +package com.github.kuangcp.proxy.dao.jdkproxy; + +import com.github.kuangcp.proxy.dao.common.InterceptorLogic; +import com.github.kuangcp.proxy.dao.common.Permission; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +/** + * 动态增强,织入权限控制 + * + * @author https://github.com/kuangcp on 2019-09-29 01:33 + */ +@Slf4j +@AllArgsConstructor +public class PermissionInterceptor implements InvocationHandler { + + private Permission permission; + private Object target; + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + log.debug("method={} proxy={}", method.getName(), proxy); + + return InterceptorLogic.permissionLogic(permission, target, method, args); + } +} diff --git a/spring/src/main/java/com/github/kuangcp/proxy/dao/jdkproxy/PersonDao.java b/spring/src/main/java/com/github/kuangcp/proxy/dao/jdkproxy/PersonDao.java index 2edccc97..2eaa77c5 100644 --- a/spring/src/main/java/com/github/kuangcp/proxy/dao/jdkproxy/PersonDao.java +++ b/spring/src/main/java/com/github/kuangcp/proxy/dao/jdkproxy/PersonDao.java @@ -4,9 +4,7 @@ import java.util.List; /** - * 目标接口 - * - * @author Administrator + * 代理对象需实现接口 */ public interface PersonDao { diff --git a/spring/src/main/java/com/github/kuangcp/proxy/dao/jdkproxy/PersonDaoImpl.java b/spring/src/main/java/com/github/kuangcp/proxy/dao/jdkproxy/PersonDaoImpl.java index 19edc481..c40d630a 100644 --- a/spring/src/main/java/com/github/kuangcp/proxy/dao/jdkproxy/PersonDaoImpl.java +++ b/spring/src/main/java/com/github/kuangcp/proxy/dao/jdkproxy/PersonDaoImpl.java @@ -1,6 +1,7 @@ package com.github.kuangcp.proxy.dao.jdkproxy; import com.github.kuangcp.aop.common.Person; +import java.util.Collections; import java.util.List; import lombok.extern.slf4j.Slf4j; @@ -24,7 +25,6 @@ public void deletePerson() { @Override public List getPerson() { - return null; + return Collections.emptyList(); } - } diff --git a/spring/src/main/java/com/github/kuangcp/proxy/dao/jdkproxy/PersonDaoInterceptor.java b/spring/src/main/java/com/github/kuangcp/proxy/dao/jdkproxy/PersonDaoInterceptor.java deleted file mode 100644 index 674728fa..00000000 --- a/spring/src/main/java/com/github/kuangcp/proxy/dao/jdkproxy/PersonDaoInterceptor.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.github.kuangcp.proxy.dao.jdkproxy; - -import com.github.kuangcp.proxy.dao.base.CustomInterceptor; -import com.github.kuangcp.proxy.dao.base.Transaction; -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Method; - -/** - * 感觉像是织入器 将增强和切点类作为属性 - * - * @author Myth - */ -public class PersonDaoInterceptor implements InvocationHandler, CustomInterceptor { - - private Transaction transaction; - private Object target; - - public PersonDaoInterceptor(Transaction transaction, Object target) { - this.transaction = transaction; - this.target = target; - } - - @Override - public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { - Object result; - //对指定的方法增强 - if (isNeedTransaction(method.getName())) { - this.transaction.beginTransaction(); - result = method.invoke(this.target, args);//调用目标类的目标方法 - this.transaction.commit(); - } else { - result = method.invoke(this.target, args);//调用目标类的目标方法 - } - return result; - } - -} diff --git a/spring/src/main/java/com/github/kuangcp/proxy/dao/jdkproxy/TransactionInterceptor.java b/spring/src/main/java/com/github/kuangcp/proxy/dao/jdkproxy/TransactionInterceptor.java new file mode 100644 index 00000000..8e0663c4 --- /dev/null +++ b/spring/src/main/java/com/github/kuangcp/proxy/dao/jdkproxy/TransactionInterceptor.java @@ -0,0 +1,28 @@ +package com.github.kuangcp.proxy.dao.jdkproxy; + +import com.github.kuangcp.proxy.dao.common.InterceptorLogic; +import com.github.kuangcp.proxy.dao.common.Transaction; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +/** + * 动态增强,织入事务控制 + * + * @author Myth + */ +@Slf4j +@AllArgsConstructor +public class TransactionInterceptor implements InvocationHandler { + + private Transaction transaction; + private Object target; + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + log.debug("method={} proxy={}", method.getName(), proxy); + + return InterceptorLogic.transactionLogic(transaction, target, method, args); + } +} diff --git a/spring/src/test/java/com/github/kuangcp/proxy/dao/cglibproxy/CGlibProxyTest.java b/spring/src/test/java/com/github/kuangcp/proxy/dao/cglibproxy/CGlibProxyTest.java index b4a96b67..faf7f812 100644 --- a/spring/src/test/java/com/github/kuangcp/proxy/dao/cglibproxy/CGlibProxyTest.java +++ b/spring/src/test/java/com/github/kuangcp/proxy/dao/cglibproxy/CGlibProxyTest.java @@ -1,16 +1,23 @@ package com.github.kuangcp.proxy.dao.cglibproxy; -import com.github.kuangcp.proxy.dao.base.Transaction; +import com.github.kuangcp.proxy.dao.common.Transaction; import org.junit.Test; public class CGlibProxyTest { @Test - public void testMain() { + public void testTransaction() { Transaction transaction = new Transaction(); PersonDaoImpl target = new PersonDaoImpl(); - PersonDaoInterceptor interceptor = new PersonDaoInterceptor(transaction, target); + TransactionInterceptor interceptor = new TransactionInterceptor(transaction, target); + PersonDaoImpl proxy = (PersonDaoImpl) interceptor.createProxy(); proxy.updatePerson(); + System.out.println(); + proxy.getPerson(); + System.out.println(); + proxy.savePerson(); + System.out.println(); + proxy.deletePerson(); } } diff --git a/spring/src/test/java/com/github/kuangcp/proxy/dao/jdkproxy/JDKProxyTest.java b/spring/src/test/java/com/github/kuangcp/proxy/dao/jdkproxy/JDKProxyTest.java index be326074..fb0c3d37 100644 --- a/spring/src/test/java/com/github/kuangcp/proxy/dao/jdkproxy/JDKProxyTest.java +++ b/spring/src/test/java/com/github/kuangcp/proxy/dao/jdkproxy/JDKProxyTest.java @@ -1,24 +1,82 @@ package com.github.kuangcp.proxy.dao.jdkproxy; -import com.github.kuangcp.proxy.dao.base.Transaction; +import com.github.kuangcp.proxy.dao.common.Permission; +import com.github.kuangcp.proxy.dao.common.Transaction; import java.lang.reflect.Proxy; +import java.util.Arrays; +import lombok.extern.slf4j.Slf4j; import org.junit.Test; +@Slf4j public class JDKProxyTest { @Test - public void testMain() { - //增强 - Transaction transaction = new Transaction(); - //切点 + public void testTransaction() { + // 被代理对象 PersonDao target = new PersonDaoImpl(); - //拦截器 重写了invoke方法,将需要增强的对象target传入 - PersonDaoInterceptor interceptor = new PersonDaoInterceptor(transaction, target); + PersonDao proxy = getTransactionProxy(target); + proxy.getPerson(); + proxy.savePerson(); + proxy.deletePerson(); + } + + @Test + public void testPermission() { + // 被代理对象 + PersonDao target = new PersonDaoImpl(); + PersonDao proxy = getPermissionProxy(target); + proxy.getPerson(); + proxy.savePerson(); + proxy.deletePerson(); + } + + /** + * 嵌套代理,权限 -> 事务 -> 被代理对象 + */ + @Test + public void testMix() { + // 被代理对象 + PersonDao target = new PersonDaoImpl(); + PersonDao proxy = getTransactionProxy(target); + PersonDao permissionProxy = getPermissionProxy(proxy); + + System.out.println(); + permissionProxy.getPerson(); + System.out.println(); + permissionProxy.savePerson(); + System.out.println(); + permissionProxy.deletePerson(); + } + + private PersonDao getTransactionProxy(PersonDao target) { + // 代理逻辑 + Transaction transaction = new Transaction(); + + // 重写了invoke方法,将需要增强的对象target传入 + TransactionInterceptor interceptor = new TransactionInterceptor(transaction, target); + //使用jdk的代理对象来实例化, //参数解释:切点类装载器,切点类接口,实现了接口的(将增强织入切点的)实例 - PersonDao proxy = (PersonDao) Proxy - .newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), - interceptor); - proxy.deletePerson(); + Class[] interfaces = target.getClass().getInterfaces(); + log.debug(": interfaces={}", Arrays.toString(interfaces)); + + return (PersonDao) Proxy + .newProxyInstance(target.getClass().getClassLoader(), interfaces, interceptor); + } + + private PersonDao getPermissionProxy(PersonDao target) { + // 代理逻辑 + Permission permission = new Permission(); + + // 重写了invoke方法,将需要增强的对象target传入 + PermissionInterceptor interceptor = new PermissionInterceptor(permission, target); + + //使用jdk的代理对象来实例化, + //参数解释:切点类装载器,切点类接口,实现了接口的(将增强织入切点的)实例 + Class[] interfaces = target.getClass().getInterfaces(); + log.debug(": interfaces={}", Arrays.toString(interfaces)); + + return (PersonDao) Proxy + .newProxyInstance(target.getClass().getClassLoader(), interfaces, interceptor); } } diff --git a/spring/src/test/java/com/github/kuangcp/proxy/dao/jdkproxy/Readme.md b/spring/src/test/java/com/github/kuangcp/proxy/dao/jdkproxy/Readme.md index 743dc9ce..22372722 100644 --- a/spring/src/test/java/com/github/kuangcp/proxy/dao/jdkproxy/Readme.md +++ b/spring/src/test/java/com/github/kuangcp/proxy/dao/jdkproxy/Readme.md @@ -1,12 +1,10 @@ -## 使用 JDK 的内置类来实现 AOP +## 使用 JDK 的内置类来实现 代理 -1. 具有基本对象 -2. 对象的操作接口 dao -3. 对象的操作接口的实现类 daoimpl 切点 目标类 -4. 增强类 若干 -5. 实现了invocationHandler接口的织入器 inteceptor +1. 被代理类 +1. 被代理对象被代理的方法的接口 +1. 代理逻辑类 +1. 代理和被代理对象结合的逻辑 -织入器将所有增强 和 目标类(切点类)在invoke(之后的代理对象调用任何方法都是调用这个方法) 方法中进行业务逻辑组合 +运行:实例化 被代理对象 代理逻辑类 -运行:实例化 切点类和增强,实例化织入器(传入切点和增强) -使用Proxy实例化代理对象 dao接收,然后调用方法就是增强后的方法 +使用Proxy实例化代理对象 为 被代理类接口类型,然后调用对应的方法就是插入代理逻辑后的方法 From 70c2921c2827a8340d93ea6ae60339ff46bb6903 Mon Sep 17 00:00:00 2001 From: kcp Date: Fri, 11 Oct 2019 20:54:48 +0800 Subject: [PATCH 105/476] +) time wheel --- .../java/com/github/kuangcp/time/wheel/TimeWheel.java | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 algorithms/src/main/java/com/github/kuangcp/time/wheel/TimeWheel.java diff --git a/algorithms/src/main/java/com/github/kuangcp/time/wheel/TimeWheel.java b/algorithms/src/main/java/com/github/kuangcp/time/wheel/TimeWheel.java new file mode 100644 index 00000000..b1865b6c --- /dev/null +++ b/algorithms/src/main/java/com/github/kuangcp/time/wheel/TimeWheel.java @@ -0,0 +1,10 @@ +package com.github.kuangcp.time.wheel; + +/** + * https://github.com/wolaiye1010/zdc-java-script + * + * @author https://github.com/kuangcp on 2019-10-11 20:53 + */ +public class TimeWheel { + +} From 1b56d00af5049b34069f009ce2a9422534605cd8 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Sun, 27 Oct 2019 13:24:42 +0800 Subject: [PATCH 106/476] +) half of timewheel --- .../github/kuangcp/time/wheel/TimeWheel.java | 234 ++++++++++++++++++ .../kuangcp/time/wheel/TimeWheelTest.java | 57 +++++ .../src/test/resources/logback-test.xml | 2 +- .../com/github/kuangcp/time/DurationTest.java | 21 ++ 4 files changed, 313 insertions(+), 1 deletion(-) create mode 100644 algorithms/src/test/java/com/github/kuangcp/time/wheel/TimeWheelTest.java create mode 100644 java8/src/test/java/com/github/kuangcp/time/DurationTest.java diff --git a/algorithms/src/main/java/com/github/kuangcp/time/wheel/TimeWheel.java b/algorithms/src/main/java/com/github/kuangcp/time/wheel/TimeWheel.java index b1865b6c..3b5abefc 100644 --- a/algorithms/src/main/java/com/github/kuangcp/time/wheel/TimeWheel.java +++ b/algorithms/src/main/java/com/github/kuangcp/time/wheel/TimeWheel.java @@ -1,10 +1,244 @@ package com.github.kuangcp.time.wheel; +import java.time.Duration; +import java.time.temporal.ChronoUnit; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.Callable; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.atomic.AtomicInteger; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; + /** * https://github.com/wolaiye1010/zdc-java-script * + * 类似于 HashMap的设计结构 + * 设计支持 30 天内延迟任务 + * * @author https://github.com/kuangcp on 2019-10-11 20:53 */ +@Slf4j public class TimeWheel { + private volatile boolean stop = false; + private volatile boolean needExitVM = false; + + private volatile long startEmptyTime = -1; + private volatile long maxTimeout = 10L; + + private AtomicInteger currentSecond = new AtomicInteger(0); + private AtomicInteger currentMinute = new AtomicInteger(0); + private AtomicInteger currentHour = new AtomicInteger(0); + private AtomicInteger currentDay = new AtomicInteger(0); + + private Map wheels = new ConcurrentHashMap<>(); + + { + LinkedList[] secondsSlot = new LinkedList[60]; + wheels.put(ChronoUnit.SECONDS, secondsSlot); + LinkedList[] minutesSlot = new LinkedList[60]; + wheels.put(ChronoUnit.MINUTES, minutesSlot); + LinkedList[] hoursSlot = new LinkedList[24]; + wheels.put(ChronoUnit.HOURS, hoursSlot); + LinkedList[] daysSlot = new LinkedList[30]; + wheels.put(ChronoUnit.DAYS, daysSlot); + } + + private static class LinkedList { + + private Node head; + private Node tail; + private int len; + + boolean add(Node node) { + len++; + if (Objects.isNull(head)) { + head = node; + tail = node; + return true; + } + + this.tail.next = node; + this.tail = node; + return true; + } + + void clear() { + len = 0; + this.head = null; + this.tail = null; + } + + boolean isEmpty() { + return Objects.isNull(head); + } + } + + public TimeWheel() { + } + + public TimeWheel(long maxTimeout, boolean needExitVM) { + this.maxTimeout = maxTimeout; + this.needExitVM = needExitVM; + } + + @Data + private static class Node { + + private String id; + private Node next; + private long runTime; + + boolean isTail() { + return Objects.isNull(next); + } + + public Node(String id, Node next, long runTime) { + this.id = id; + this.next = next; + this.runTime = runTime; + } + + } + + private ExecutorService pool = Executors.newFixedThreadPool(6); + private ExecutorService master = Executors.newSingleThreadExecutor(); + + // id -> task + private final Map> cacheTasks = new ConcurrentHashMap<>(); + + public void start() { + master.execute(() -> { + while (!stop) { + if (cacheTasks.isEmpty()) { + long current = System.currentTimeMillis(); + if (startEmptyTime == -1) { + startEmptyTime = current; + } else if (current - startEmptyTime > maxTimeout) { + log.warn("no any tasks with timeout"); + shutdown(); + continue; + } + } + + try { + Thread.sleep(1000); + int seconds = this.currentSecond.incrementAndGet(); + push(seconds); + + LinkedList[] lists = wheels.get(ChronoUnit.SECONDS); + LinkedList list = lists[seconds % 60]; + this.invokeAll(seconds, list); + } catch (Exception e) { + log.error("", e); + } + } + }); + } + + private void push(int seconds) { + if (seconds > 59) { + this.currentSecond.set(0); + this.currentMinute.incrementAndGet(); + } + } + + private void invokeAll(int seconds, LinkedList list) { + if (Objects.isNull(list) || list.isEmpty()) { + log.warn("no tasks need invoke"); + return; + } + + Node pointer = list.head; + while (Objects.nonNull(pointer)) { + String id = pointer.getId(); + Callable func = cacheTasks.get(id); + log.debug("[{}]before invoke: id={}", seconds, id); + try { + Future result = pool.submit(func); + log.info("[{}]invoke: id={} result={}", seconds, id, result.get()); + cacheTasks.remove(id); + } catch (Exception e) { + log.error("", e); + } + pointer = pointer.next; + } + list.clear(); + log.info("[{}]invoke all tasks successful.", seconds); + } + + public void shutdown() { + this.stop = true; + log.warn("shutdown"); + if (needExitVM) { + System.exit(1); + } + } + + public boolean add(String id, Callable func, Duration delay) { + if (Objects.isNull(delay)) { + log.warn("delay is null: id={}", id); + return false; + } + if (Objects.isNull(func)) { + log.warn("func is null: id={}", id); + return false; + } + boolean result = insertWheel(id, delay); + if (result) { + cacheTasks.put(id, func); + } + return result; + } + + private boolean insertWheel(String id, Duration delay) { + if (cacheTasks.containsKey(id)) { + log.warn("task has already exist"); + return false; + } + + long days = delay.toDays(); + if (days > 30) { + log.warn("out of timeWheel max delay bound"); + return false; + } + + long runTime = delay.toMillis() + System.currentTimeMillis(); + if (days > 0) { + return insertWheel(ChronoUnit.DAYS, days, id, runTime); + } + + long hours = delay.toHours(); + if (hours > 0) { + return insertWheel(ChronoUnit.HOURS, hours, id, runTime); + } + + long minutes = delay.toMinutes(); + if (minutes > 0) { + return insertWheel(ChronoUnit.MINUTES, minutes, id, runTime); + } + + long seconds = delay.getSeconds(); + if (seconds > 0) { + return insertWheel(ChronoUnit.SECONDS, seconds, id, runTime); + } + + log.warn("task is expire: id={}", id); + return false; + } + + private boolean insertWheel(ChronoUnit unit, long index, String id, long runTime) { + int idx = (int) index; + LinkedList[] lists = wheels.get(unit); + LinkedList list = lists[idx]; + if (Objects.isNull(list)) { + list = new LinkedList(); + lists[idx] = list; + } + return list.add(new Node(id, null, runTime)); + } } diff --git a/algorithms/src/test/java/com/github/kuangcp/time/wheel/TimeWheelTest.java b/algorithms/src/test/java/com/github/kuangcp/time/wheel/TimeWheelTest.java new file mode 100644 index 00000000..9882575e --- /dev/null +++ b/algorithms/src/test/java/com/github/kuangcp/time/wheel/TimeWheelTest.java @@ -0,0 +1,57 @@ +package com.github.kuangcp.time.wheel; + +import java.time.Duration; +import java.util.concurrent.TimeUnit; +import lombok.extern.slf4j.Slf4j; +import org.junit.Assert; +import org.junit.Test; + +/** + * @author https://github.com/kuangcp on 2019-10-27 12:15 + */ +@Slf4j +public class TimeWheelTest { + + private TimeWheel timeWheel = new TimeWheel(3000L, true); + + @Test + public void testAdd() throws Exception { + boolean result = timeWheel.add("id", () -> null, Duration.ofMillis(10000)); + Assert.assertTrue(result); + } + + @Test + public void testRun1() { + for (int i = 0; i < 7; i++) { + boolean result = timeWheel.add("id" + i, () -> null, Duration.ofMillis(i * 3000)); + log.debug(": result={}", result); + } + timeWheel.start(); + join(); + } + + private void join() { + try { + Thread.currentThread().join(); + } catch (Exception e) { + log.error("", e); + } + } + + @Test + public void testRun2() { + for (int i = 0; i < 13; i++) { + boolean result = timeWheel.add("id" + i, () -> "fds", Duration.ofMillis(5000)); + log.info(": result={}", result); + } + timeWheel.start(); + + try { + TimeUnit.SECONDS.sleep(10); + timeWheel.shutdown(); + } catch (Exception e) { + log.error("", e); + } + } + +} diff --git a/algorithms/src/test/resources/logback-test.xml b/algorithms/src/test/resources/logback-test.xml index 9b387ff2..62673ee8 100644 --- a/algorithms/src/test/resources/logback-test.xml +++ b/algorithms/src/test/resources/logback-test.xml @@ -12,7 +12,7 @@ - + diff --git a/java8/src/test/java/com/github/kuangcp/time/DurationTest.java b/java8/src/test/java/com/github/kuangcp/time/DurationTest.java new file mode 100644 index 00000000..e1f6fbc3 --- /dev/null +++ b/java8/src/test/java/com/github/kuangcp/time/DurationTest.java @@ -0,0 +1,21 @@ +package com.github.kuangcp.time; + +import java.time.Duration; +import java.time.temporal.ChronoUnit; +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; + +/** + * @author https://github.com/kuangcp on 2019-10-27 11:43 + */ +@Slf4j +public class DurationTest { + + @Test + public void testConvert(){ + + Duration duration = Duration.ofHours(23); + duration = duration.plus(59, ChronoUnit.MINUTES); + log.info("duration={}", duration.toMinutes()); + } +} From a679d3f979fd26b75ded80d0f1b51e93a11bb01c Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Mon, 28 Oct 2019 01:22:59 +0800 Subject: [PATCH 107/476] +) complete timewheel --- .../github/kuangcp/time/wheel/TimeWheel.java | 153 ++++++++++++++---- .../kuangcp/time/wheel/TimeWheelTest.java | 23 +++ .../src/test/resources/logback-test.xml | 2 +- .../com/github/kuangcp/time/DurationTest.java | 7 +- 4 files changed, 153 insertions(+), 32 deletions(-) diff --git a/algorithms/src/main/java/com/github/kuangcp/time/wheel/TimeWheel.java b/algorithms/src/main/java/com/github/kuangcp/time/wheel/TimeWheel.java index 3b5abefc..feff3113 100644 --- a/algorithms/src/main/java/com/github/kuangcp/time/wheel/TimeWheel.java +++ b/algorithms/src/main/java/com/github/kuangcp/time/wheel/TimeWheel.java @@ -2,6 +2,8 @@ import java.time.Duration; import java.time.temporal.ChronoUnit; +import java.util.ArrayList; +import java.util.List; import java.util.Map; import java.util.Objects; import java.util.concurrent.Callable; @@ -9,7 +11,9 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; import lombok.Data; import lombok.extern.slf4j.Slf4j; @@ -30,21 +34,31 @@ public class TimeWheel { private volatile long startEmptyTime = -1; private volatile long maxTimeout = 10L; + private static final int SECONDS_SLOT = 60; + private static final int MINUTES_SLOT = 60; + private static final int HOURS_SLOT = 24; + private static final int DAYS_SLOT = 30; + private AtomicInteger currentSecond = new AtomicInteger(0); private AtomicInteger currentMinute = new AtomicInteger(0); private AtomicInteger currentHour = new AtomicInteger(0); private AtomicInteger currentDay = new AtomicInteger(0); - private Map wheels = new ConcurrentHashMap<>(); + private ExecutorService pool = Executors.newFixedThreadPool(6); + private ExecutorService master = Executors.newSingleThreadExecutor(); + + // id -> task + private final Map> cacheTasks = new ConcurrentHashMap<>(); + private final Map wheels = new ConcurrentHashMap<>(); { - LinkedList[] secondsSlot = new LinkedList[60]; + LinkedList[] secondsSlot = new LinkedList[SECONDS_SLOT]; wheels.put(ChronoUnit.SECONDS, secondsSlot); - LinkedList[] minutesSlot = new LinkedList[60]; + LinkedList[] minutesSlot = new LinkedList[MINUTES_SLOT]; wheels.put(ChronoUnit.MINUTES, minutesSlot); - LinkedList[] hoursSlot = new LinkedList[24]; + LinkedList[] hoursSlot = new LinkedList[HOURS_SLOT]; wheels.put(ChronoUnit.HOURS, hoursSlot); - LinkedList[] daysSlot = new LinkedList[30]; + LinkedList[] daysSlot = new LinkedList[DAYS_SLOT]; wheels.put(ChronoUnit.DAYS, daysSlot); } @@ -73,6 +87,24 @@ void clear() { this.tail = null; } + List toList() { + List result = new ArrayList<>(len); + Node pointer = this.head; + while (Objects.nonNull(pointer)) { + result.add(pointer); + pointer = pointer.next; + } + return result; + } + + String toSimpleString() { + List nodes = toList(); + if (nodes.isEmpty()) { + return ""; + } + return nodes.stream().map(Node::getId).collect(Collectors.joining(",")); + } + boolean isEmpty() { return Objects.isNull(head); } @@ -93,11 +125,7 @@ private static class Node { private Node next; private long runTime; - boolean isTail() { - return Objects.isNull(next); - } - - public Node(String id, Node next, long runTime) { + Node(String id, Node next, long runTime) { this.id = id; this.next = next; this.runTime = runTime; @@ -105,15 +133,11 @@ public Node(String id, Node next, long runTime) { } - private ExecutorService pool = Executors.newFixedThreadPool(6); - private ExecutorService master = Executors.newSingleThreadExecutor(); - - // id -> task - private final Map> cacheTasks = new ConcurrentHashMap<>(); public void start() { master.execute(() -> { while (!stop) { + log.debug("cache: keys={}", cacheTasks.keySet()); if (cacheTasks.isEmpty()) { long current = System.currentTimeMillis(); if (startEmptyTime == -1) { @@ -126,13 +150,15 @@ public void start() { } try { - Thread.sleep(1000); + TimeUnit.SECONDS.sleep(1); + int seconds = this.currentSecond.incrementAndGet(); - push(seconds); + this.rushLoop(seconds); LinkedList[] lists = wheels.get(ChronoUnit.SECONDS); - LinkedList list = lists[seconds % 60]; + LinkedList list = lists[seconds % SECONDS_SLOT]; this.invokeAll(seconds, list); + } catch (Exception e) { log.error("", e); } @@ -140,10 +166,61 @@ public void start() { }); } - private void push(int seconds) { - if (seconds > 59) { + private void removeAndAdd(ChronoUnit unit, int index) { + LinkedList[] lists = wheels.get(unit); + LinkedList list = lists[index]; + if (Objects.nonNull(list) && !list.isEmpty()) { + List nodes = list.toList(); + list.clear(); + for (Node node : nodes) { + long millis = node.runTime - System.currentTimeMillis(); + if (millis < 0) { + log.error("system lose task: node={}", node); +// cacheTasks.remove(node.getId()); +// continue; + } + + Duration delay = Duration.ofMillis(millis); + log.debug(": delay={}", delay); + insertWheel(node.getId(), delay); + } + } + } + + private void rushLoop(int seconds) { + int costMin = -1; + int costHour = -1; + int costDay = -1; + + if (seconds >= SECONDS_SLOT) { this.currentSecond.set(0); - this.currentMinute.incrementAndGet(); + costMin = this.currentMinute.incrementAndGet(); + } + if (costMin > MINUTES_SLOT) { + this.currentMinute.set(0); + costHour = this.currentHour.incrementAndGet(); + } + if (costHour > HOURS_SLOT) { + this.currentHour.set(0); + costDay = this.currentDay.incrementAndGet(); + } + + if (costDay > DAYS_SLOT) { + this.currentDay.set(0); + this.removeAndAdd(ChronoUnit.DAYS, 0); + costDay = -1; + } + + if (costDay != -1) { + this.removeAndAdd(ChronoUnit.DAYS, costDay); + } + + if (costHour != -1) { + this.removeAndAdd(ChronoUnit.HOURS, costHour); + } + + if (costMin != -1) { + this.removeAndAdd(ChronoUnit.MINUTES, costMin); } } @@ -153,9 +230,9 @@ private void invokeAll(int seconds, LinkedList list) { return; } - Node pointer = list.head; - while (Objects.nonNull(pointer)) { - String id = pointer.getId(); + List nodes = list.toList(); + for (Node node : nodes) { + String id = node.getId(); Callable func = cacheTasks.get(id); log.debug("[{}]before invoke: id={}", seconds, id); try { @@ -165,7 +242,6 @@ private void invokeAll(int seconds, LinkedList list) { } catch (Exception e) { log.error("", e); } - pointer = pointer.next; } list.clear(); log.info("[{}]invoke all tasks successful.", seconds); @@ -188,6 +264,12 @@ public boolean add(String id, Callable func, Duration delay) { log.warn("func is null: id={}", id); return false; } + + if (cacheTasks.containsKey(id)) { + log.warn("task has already exist"); + return false; + } + boolean result = insertWheel(id, delay); if (result) { cacheTasks.put(id, func); @@ -195,12 +277,19 @@ public boolean add(String id, Callable func, Duration delay) { return result; } - private boolean insertWheel(String id, Duration delay) { - if (cacheTasks.containsKey(id)) { - log.warn("task has already exist"); - return false; + public void printWheel() { + for (ChronoUnit unit : wheels.keySet()) { + LinkedList[] lists = wheels.get(unit); + for (int i = 0; i < lists.length; i++) { + LinkedList list = lists[i]; + if (Objects.nonNull(list) && !list.isEmpty()) { + log.info("{} [{}] ids={}", unit.toString(), i, list.toSimpleString()); + } + } } + } + private boolean insertWheel(String id, Duration delay) { long days = delay.toDays(); if (days > 30) { log.warn("out of timeWheel max delay bound"); @@ -223,11 +312,15 @@ private boolean insertWheel(String id, Duration delay) { } long seconds = delay.getSeconds(); + // TODO expire + if (seconds >= -2 && seconds <= 0) { + return insertWheel(ChronoUnit.SECONDS, 0, id, runTime); + } if (seconds > 0) { return insertWheel(ChronoUnit.SECONDS, seconds, id, runTime); } - log.warn("task is expire: id={}", id); + log.warn("task is expire: id={} delay={}", id, delay); return false; } diff --git a/algorithms/src/test/java/com/github/kuangcp/time/wheel/TimeWheelTest.java b/algorithms/src/test/java/com/github/kuangcp/time/wheel/TimeWheelTest.java index 9882575e..507748d0 100644 --- a/algorithms/src/test/java/com/github/kuangcp/time/wheel/TimeWheelTest.java +++ b/algorithms/src/test/java/com/github/kuangcp/time/wheel/TimeWheelTest.java @@ -54,4 +54,27 @@ public void testRun2() { } } + + @Test + public void testRushLoop() { + for (int i = 1; i < 7; i++) { + boolean result = timeWheel.add("id" + i, () -> "fds", Duration.ofMillis(i * 10000)); + log.info(": result={}", result); + } + timeWheel.printWheel(); + timeWheel.start(); + + new Thread(() -> { + while (true) { + timeWheel.printWheel(); + try { + TimeUnit.SECONDS.sleep(2); + } catch (InterruptedException e) { + log.error("", e); + } + } + }).start(); + join(); + } + } diff --git a/algorithms/src/test/resources/logback-test.xml b/algorithms/src/test/resources/logback-test.xml index 62673ee8..964f08d6 100644 --- a/algorithms/src/test/resources/logback-test.xml +++ b/algorithms/src/test/resources/logback-test.xml @@ -5,7 +5,7 @@ - %d{HH:mm:ss.SSS} %highlight(%-5level) [%thread] %logger{30}:%yellow(%-3L) %msg%n + %d{HH:mm:ss.SSS} %highlight(%-5level) [%thread] %logger{30}:%yellow(%M %-3L) %msg%n DEBUG diff --git a/java8/src/test/java/com/github/kuangcp/time/DurationTest.java b/java8/src/test/java/com/github/kuangcp/time/DurationTest.java index e1f6fbc3..1459d2be 100644 --- a/java8/src/test/java/com/github/kuangcp/time/DurationTest.java +++ b/java8/src/test/java/com/github/kuangcp/time/DurationTest.java @@ -13,9 +13,14 @@ public class DurationTest { @Test public void testConvert(){ - Duration duration = Duration.ofHours(23); duration = duration.plus(59, ChronoUnit.MINUTES); log.info("duration={}", duration.toMinutes()); } + + @Test + public void testNegative(){ + Duration duration = Duration.ofMillis(-1000); + System.out.println(duration.getSeconds()); + } } From 9a085107f89e92eb1fd0b9554e12691e3b9020d3 Mon Sep 17 00:00:00 2001 From: kcp Date: Mon, 28 Oct 2019 13:32:52 +0800 Subject: [PATCH 108/476] *) make query more frequtenly --- .../github/kuangcp/time/wheel/LinkedList.java | 57 +++++++++++ .../com/github/kuangcp/time/wheel/Node.java | 20 ++++ .../github/kuangcp/time/wheel/TimeWheel.java | 98 ++++--------------- .../kuangcp/time/wheel/TimeWheelTest.java | 11 ++- 4 files changed, 106 insertions(+), 80 deletions(-) create mode 100644 algorithms/src/main/java/com/github/kuangcp/time/wheel/LinkedList.java create mode 100644 algorithms/src/main/java/com/github/kuangcp/time/wheel/Node.java diff --git a/algorithms/src/main/java/com/github/kuangcp/time/wheel/LinkedList.java b/algorithms/src/main/java/com/github/kuangcp/time/wheel/LinkedList.java new file mode 100644 index 00000000..4425b63a --- /dev/null +++ b/algorithms/src/main/java/com/github/kuangcp/time/wheel/LinkedList.java @@ -0,0 +1,57 @@ +package com.github.kuangcp.time.wheel; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * @author https://github.com/kuangcp on 2019-10-28 13:10 + */ +class LinkedList { + + private Node head; + private Node tail; + private int len; + + boolean add(Node node) { + len++; + if (Objects.isNull(head)) { + head = node; + tail = node; + return true; + } + + this.tail.setNext(node); + this.tail = node; + return true; + } + + void clear() { + len = 0; + this.head = null; + this.tail = null; + } + + List toList() { + List result = new ArrayList<>(len); + Node pointer = this.head; + while (Objects.nonNull(pointer)) { + result.add(pointer); + pointer = pointer.getNext(); + } + return result; + } + + String toSimpleString() { + List nodes = toList(); + if (nodes.isEmpty()) { + return ""; + } + return nodes.stream().map(Node::getId).collect(Collectors.joining(",")); + } + + boolean isEmpty() { + return Objects.isNull(head); + } +} diff --git a/algorithms/src/main/java/com/github/kuangcp/time/wheel/Node.java b/algorithms/src/main/java/com/github/kuangcp/time/wheel/Node.java new file mode 100644 index 00000000..514012f7 --- /dev/null +++ b/algorithms/src/main/java/com/github/kuangcp/time/wheel/Node.java @@ -0,0 +1,20 @@ +package com.github.kuangcp.time.wheel; + +import lombok.Data; + +/** + * @author https://github.com/kuangcp on 2019-10-28 13:10 + */ +@Data +class Node { + + private String id; + private Node next; + private long runTime; + + Node(String id, Node next, long runTime) { + this.id = id; + this.next = next; + this.runTime = runTime; + } +} diff --git a/algorithms/src/main/java/com/github/kuangcp/time/wheel/TimeWheel.java b/algorithms/src/main/java/com/github/kuangcp/time/wheel/TimeWheel.java index feff3113..130cb721 100644 --- a/algorithms/src/main/java/com/github/kuangcp/time/wheel/TimeWheel.java +++ b/algorithms/src/main/java/com/github/kuangcp/time/wheel/TimeWheel.java @@ -33,12 +33,14 @@ public class TimeWheel { private volatile long startEmptyTime = -1; private volatile long maxTimeout = 10L; + private volatile long currentMills; private static final int SECONDS_SLOT = 60; private static final int MINUTES_SLOT = 60; private static final int HOURS_SLOT = 24; private static final int DAYS_SLOT = 30; + private AtomicInteger currentSecond = new AtomicInteger(0); private AtomicInteger currentMinute = new AtomicInteger(0); private AtomicInteger currentHour = new AtomicInteger(0); @@ -62,54 +64,6 @@ public class TimeWheel { wheels.put(ChronoUnit.DAYS, daysSlot); } - private static class LinkedList { - - private Node head; - private Node tail; - private int len; - - boolean add(Node node) { - len++; - if (Objects.isNull(head)) { - head = node; - tail = node; - return true; - } - - this.tail.next = node; - this.tail = node; - return true; - } - - void clear() { - len = 0; - this.head = null; - this.tail = null; - } - - List toList() { - List result = new ArrayList<>(len); - Node pointer = this.head; - while (Objects.nonNull(pointer)) { - result.add(pointer); - pointer = pointer.next; - } - return result; - } - - String toSimpleString() { - List nodes = toList(); - if (nodes.isEmpty()) { - return ""; - } - return nodes.stream().map(Node::getId).collect(Collectors.joining(",")); - } - - boolean isEmpty() { - return Objects.isNull(head); - } - } - public TimeWheel() { } @@ -118,40 +72,28 @@ public TimeWheel(long maxTimeout, boolean needExitVM) { this.needExitVM = needExitVM; } - @Data - private static class Node { - - private String id; - private Node next; - private long runTime; - - Node(String id, Node next, long runTime) { - this.id = id; - this.next = next; - this.runTime = runTime; - } - - } - - public void start() { master.execute(() -> { while (!stop) { - log.debug("cache: keys={}", cacheTasks.keySet()); - if (cacheTasks.isEmpty()) { - long current = System.currentTimeMillis(); - if (startEmptyTime == -1) { - startEmptyTime = current; - } else if (current - startEmptyTime > maxTimeout) { - log.warn("no any tasks with timeout"); - shutdown(); + long currentMills = System.currentTimeMillis(); + try { + TimeUnit.MICROSECONDS.sleep(20); + if (currentMills - this.currentMills < 1000) { continue; } - } - try { - TimeUnit.SECONDS.sleep(1); + log.debug("cache: keys={}", cacheTasks.keySet()); + if (cacheTasks.isEmpty()) { + if (startEmptyTime == -1) { + startEmptyTime = currentMills; + } else if (currentMills - startEmptyTime > maxTimeout) { + log.warn("no any tasks with timeout"); + shutdown(); + continue; + } + } + this.currentMills = currentMills; int seconds = this.currentSecond.incrementAndGet(); this.rushLoop(seconds); @@ -173,11 +115,9 @@ private void removeAndAdd(ChronoUnit unit, int index) { List nodes = list.toList(); list.clear(); for (Node node : nodes) { - long millis = node.runTime - System.currentTimeMillis(); + long millis = node.getRunTime() - System.currentTimeMillis(); if (millis < 0) { log.error("system lose task: node={}", node); -// cacheTasks.remove(node.getId()); -// continue; } Duration delay = Duration.ofMillis(millis); @@ -334,4 +274,4 @@ private boolean insertWheel(ChronoUnit unit, long index, String id, long runTime } return list.add(new Node(id, null, runTime)); } -} +} \ No newline at end of file diff --git a/algorithms/src/test/java/com/github/kuangcp/time/wheel/TimeWheelTest.java b/algorithms/src/test/java/com/github/kuangcp/time/wheel/TimeWheelTest.java index 507748d0..f17a2bf4 100644 --- a/algorithms/src/test/java/com/github/kuangcp/time/wheel/TimeWheelTest.java +++ b/algorithms/src/test/java/com/github/kuangcp/time/wheel/TimeWheelTest.java @@ -55,10 +55,19 @@ public void testRun2() { } + private long calculate() { + long sum = 0; + for (int i = 0; i < 10000000; i++) { + Math.sqrt(sum); + sum += i; + } + return sum; + } + @Test public void testRushLoop() { for (int i = 1; i < 7; i++) { - boolean result = timeWheel.add("id" + i, () -> "fds", Duration.ofMillis(i * 10000)); + boolean result = timeWheel.add("id" + i, this::calculate, Duration.ofMillis(i * 10000)); log.info(": result={}", result); } timeWheel.printWheel(); From 7e1801674519bb73f583eff521c1a9b27dd06c53 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Mon, 28 Oct 2019 21:59:43 +0800 Subject: [PATCH 109/476] +) ignore idea pid file --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 003ebd9b..a398522a 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ target/ *.iml out/ *.ipr +.attach_pid* # eclipse # bin/ @@ -22,4 +23,4 @@ Servers/ # Gradle # .gradle build/ -.gradletasknamecache \ No newline at end of file +.gradletasknamecache From 6597949332636b238718f4df61f4119cbabdebc1 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Tue, 29 Oct 2019 00:33:48 +0800 Subject: [PATCH 110/476] *) opptimize linkedlist --- .../github/kuangcp/time/wheel/LinkedList.java | 20 +-- .../com/github/kuangcp/time/wheel/Node.java | 19 +-- .../github/kuangcp/time/wheel/TaskNode.java | 22 +++ .../github/kuangcp/time/wheel/TimeWheel.java | 154 +++++++++++------- .../kuangcp/time/wheel/TimeWheelTest.java | 96 ++++++++++- .../src/test/resources/logback-test.xml | 5 +- 6 files changed, 217 insertions(+), 99 deletions(-) create mode 100644 algorithms/src/main/java/com/github/kuangcp/time/wheel/TaskNode.java diff --git a/algorithms/src/main/java/com/github/kuangcp/time/wheel/LinkedList.java b/algorithms/src/main/java/com/github/kuangcp/time/wheel/LinkedList.java index 4425b63a..56832a31 100644 --- a/algorithms/src/main/java/com/github/kuangcp/time/wheel/LinkedList.java +++ b/algorithms/src/main/java/com/github/kuangcp/time/wheel/LinkedList.java @@ -8,13 +8,13 @@ /** * @author https://github.com/kuangcp on 2019-10-28 13:10 */ -class LinkedList { +class LinkedList { - private Node head; - private Node tail; + private T head; + private T tail; private int len; - boolean add(Node node) { + boolean add(T node) { len++; if (Objects.isNull(head)) { head = node; @@ -33,22 +33,22 @@ void clear() { this.tail = null; } - List toList() { - List result = new ArrayList<>(len); - Node pointer = this.head; + List toList() { + List result = new ArrayList<>(len); + T pointer = this.head; while (Objects.nonNull(pointer)) { result.add(pointer); - pointer = pointer.getNext(); + pointer = (T)pointer.getNext(); } return result; } String toSimpleString() { - List nodes = toList(); + List nodes = toList(); if (nodes.isEmpty()) { return ""; } - return nodes.stream().map(Node::getId).collect(Collectors.joining(",")); + return nodes.stream().map(T::getId).collect(Collectors.joining(",")); } boolean isEmpty() { diff --git a/algorithms/src/main/java/com/github/kuangcp/time/wheel/Node.java b/algorithms/src/main/java/com/github/kuangcp/time/wheel/Node.java index 514012f7..72f2d5f9 100644 --- a/algorithms/src/main/java/com/github/kuangcp/time/wheel/Node.java +++ b/algorithms/src/main/java/com/github/kuangcp/time/wheel/Node.java @@ -1,20 +1,13 @@ package com.github.kuangcp.time.wheel; -import lombok.Data; - /** - * @author https://github.com/kuangcp on 2019-10-28 13:10 + * @author https://github.com/kuangcp on 2019-10-28 23:53 */ -@Data -class Node { +public interface Node { + + String getId(); - private String id; - private Node next; - private long runTime; + Node getNext(); - Node(String id, Node next, long runTime) { - this.id = id; - this.next = next; - this.runTime = runTime; - } + void setNext(Node nodeAble); } diff --git a/algorithms/src/main/java/com/github/kuangcp/time/wheel/TaskNode.java b/algorithms/src/main/java/com/github/kuangcp/time/wheel/TaskNode.java new file mode 100644 index 00000000..48366b3b --- /dev/null +++ b/algorithms/src/main/java/com/github/kuangcp/time/wheel/TaskNode.java @@ -0,0 +1,22 @@ +package com.github.kuangcp.time.wheel; + +import lombok.AllArgsConstructor; +import lombok.Data; + +/** + * @author https://github.com/kuangcp on 2019-10-28 13:10 + */ +@Data +@AllArgsConstructor +class TaskNode implements Node { + + private String id; + private TaskNode next; + private long lastTime; + private long delayMills; + + @Override + public void setNext(Node nodeAble) { + next = (TaskNode) nodeAble; + } +} diff --git a/algorithms/src/main/java/com/github/kuangcp/time/wheel/TimeWheel.java b/algorithms/src/main/java/com/github/kuangcp/time/wheel/TimeWheel.java index 130cb721..984fdc5e 100644 --- a/algorithms/src/main/java/com/github/kuangcp/time/wheel/TimeWheel.java +++ b/algorithms/src/main/java/com/github/kuangcp/time/wheel/TimeWheel.java @@ -2,7 +2,8 @@ import java.time.Duration; import java.time.temporal.ChronoUnit; -import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; @@ -13,8 +14,6 @@ import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; -import java.util.stream.Collectors; -import lombok.Data; import lombok.extern.slf4j.Slf4j; /** @@ -51,17 +50,37 @@ public class TimeWheel { // id -> task private final Map> cacheTasks = new ConcurrentHashMap<>(); - private final Map wheels = new ConcurrentHashMap<>(); + private final Map[]> wheels = new ConcurrentHashMap<>(); + private final Map counters = new ConcurrentHashMap<>(); + private final Map slots = new ConcurrentHashMap<>(); + private final List sortedSlots = Arrays + .asList(ChronoUnit.SECONDS, ChronoUnit.MINUTES, ChronoUnit.HOURS, ChronoUnit.DAYS); { + init(); + } + + @SuppressWarnings("unchecked") + private void init() { LinkedList[] secondsSlot = new LinkedList[SECONDS_SLOT]; wheels.put(ChronoUnit.SECONDS, secondsSlot); + counters.put(ChronoUnit.SECONDS, currentSecond); + slots.put(ChronoUnit.SECONDS, SECONDS_SLOT); + LinkedList[] minutesSlot = new LinkedList[MINUTES_SLOT]; wheels.put(ChronoUnit.MINUTES, minutesSlot); + counters.put(ChronoUnit.MINUTES, currentMinute); + slots.put(ChronoUnit.MINUTES, MINUTES_SLOT); + LinkedList[] hoursSlot = new LinkedList[HOURS_SLOT]; wheels.put(ChronoUnit.HOURS, hoursSlot); + counters.put(ChronoUnit.HOURS, currentHour); + slots.put(ChronoUnit.HOURS, HOURS_SLOT); + LinkedList[] daysSlot = new LinkedList[DAYS_SLOT]; wheels.put(ChronoUnit.DAYS, daysSlot); + counters.put(ChronoUnit.DAYS, currentDay); + slots.put(ChronoUnit.DAYS, DAYS_SLOT); } public TimeWheel() { @@ -95,10 +114,10 @@ public void start() { this.currentMills = currentMills; int seconds = this.currentSecond.incrementAndGet(); - this.rushLoop(seconds); + this.pushTimeWheel(seconds); - LinkedList[] lists = wheels.get(ChronoUnit.SECONDS); - LinkedList list = lists[seconds % SECONDS_SLOT]; + LinkedList[] lists = wheels.get(ChronoUnit.SECONDS); + LinkedList list = lists[seconds % SECONDS_SLOT]; this.invokeAll(seconds, list); } catch (Exception e) { @@ -109,13 +128,14 @@ public void start() { } private void removeAndAdd(ChronoUnit unit, int index) { - LinkedList[] lists = wheels.get(unit); - LinkedList list = lists[index]; + LinkedList[] lists = wheels.get(unit); + LinkedList list = lists[index]; if (Objects.nonNull(list) && !list.isEmpty()) { - List nodes = list.toList(); + List nodes = list.toList(); list.clear(); - for (Node node : nodes) { - long millis = node.getRunTime() - System.currentTimeMillis(); + for (TaskNode node : nodes) { + long millis = node.getLastTime() - System.currentTimeMillis(); + log.warn(": millis={}", millis); if (millis < 0) { log.error("system lose task: node={}", node); } @@ -127,51 +147,49 @@ private void removeAndAdd(ChronoUnit unit, int index) { } } - private void rushLoop(int seconds) { - int costMin = -1; - int costHour = -1; - int costDay = -1; - - if (seconds >= SECONDS_SLOT) { - this.currentSecond.set(0); - costMin = this.currentMinute.incrementAndGet(); - } - if (costMin > MINUTES_SLOT) { - this.currentMinute.set(0); - costHour = this.currentHour.incrementAndGet(); - } - if (costHour > HOURS_SLOT) { - this.currentHour.set(0); - costDay = this.currentDay.incrementAndGet(); - } - - if (costDay > DAYS_SLOT) { - this.currentDay.set(0); - this.removeAndAdd(ChronoUnit.DAYS, 0); - costDay = -1; - } - - if (costDay != -1) { - this.removeAndAdd(ChronoUnit.DAYS, costDay); - } + private void pushTimeWheel(int seconds) { + Map tempIndex = new HashMap<>(sortedSlots.size()); + for (int i = 0; i < sortedSlots.size(); i++) { + ChronoUnit unit = sortedSlots.get(i); + int temp = -1; + if (i == 0) { + if (seconds >= slots.get(unit)) { + AtomicInteger counter = counters.get(unit); + counter.set(0); + temp = counters.get(sortedSlots.get(i + 1)).incrementAndGet(); + } + } else if (i == sortedSlots.size() - 1) { + AtomicInteger counter = counters.get(unit); + counter.set(0); + this.removeAndAdd(unit, 0); + temp = -1; + } else { + if (temp > slots.get(unit)) { + AtomicInteger counter = counters.get(unit); + counter.set(0); + temp = counters.get(sortedSlots.get(i + 1)).incrementAndGet(); + } + } - if (costHour != -1) { - this.removeAndAdd(ChronoUnit.HOURS, costHour); + tempIndex.put(i + 1, temp); } - if (costMin != -1) { - this.removeAndAdd(ChronoUnit.MINUTES, costMin); + for (int i = sortedSlots.size() - 1; i > 0; i--) { + Integer nextIndex = tempIndex.get(i); + if (nextIndex != -1) { + this.removeAndAdd(sortedSlots.get(i), nextIndex); + } } } - private void invokeAll(int seconds, LinkedList list) { + private void invokeAll(int seconds, LinkedList list) { if (Objects.isNull(list) || list.isEmpty()) { - log.warn("no tasks need invoke"); + log.debug("no tasks need invoke"); return; } - List nodes = list.toList(); - for (Node node : nodes) { + List nodes = list.toList(); + for (TaskNode node : nodes) { String id = node.getId(); Callable func = cacheTasks.get(id); log.debug("[{}]before invoke: id={}", seconds, id); @@ -184,7 +202,7 @@ private void invokeAll(int seconds, LinkedList list) { } } list.clear(); - log.info("[{}]invoke all tasks successful.", seconds); + log.debug("[{}]invoke all tasks successful.", seconds); } public void shutdown() { @@ -220,12 +238,20 @@ public boolean add(String id, Callable func, Duration delay) { public void printWheel() { for (ChronoUnit unit : wheels.keySet()) { LinkedList[] lists = wheels.get(unit); + + boolean hasData = false; + StringBuilder builder = new StringBuilder(unit.toString()); for (int i = 0; i < lists.length; i++) { LinkedList list = lists[i]; if (Objects.nonNull(list) && !list.isEmpty()) { - log.info("{} [{}] ids={}", unit.toString(), i, list.toSimpleString()); + hasData = true; + builder.append(String.format("[%2s] ids=%s. ", i, list.toSimpleString())); } } + + if (hasData) { + log.info(builder.toString()); + } } } @@ -236,42 +262,44 @@ private boolean insertWheel(String id, Duration delay) { return false; } - long runTime = delay.toMillis() + System.currentTimeMillis(); + long current = System.currentTimeMillis(); + long delayMills = delay.toMillis(); + TaskNode node = new TaskNode(id, null, current, delayMills); if (days > 0) { - return insertWheel(ChronoUnit.DAYS, days, id, runTime); + return insertWheel(ChronoUnit.DAYS, this.currentDay.get() + days, node); } long hours = delay.toHours(); if (hours > 0) { - return insertWheel(ChronoUnit.HOURS, hours, id, runTime); + return insertWheel(ChronoUnit.HOURS, this.currentHour.get() + hours,node); } long minutes = delay.toMinutes(); if (minutes > 0) { - return insertWheel(ChronoUnit.MINUTES, minutes, id, runTime); + return insertWheel(ChronoUnit.MINUTES, this.currentMinute.get() + minutes, node); } long seconds = delay.getSeconds(); - // TODO expire - if (seconds >= -2 && seconds <= 0) { - return insertWheel(ChronoUnit.SECONDS, 0, id, runTime); + // TODO expire too long time + if (seconds >= -10 && seconds <= 1) { + return insertWheel(ChronoUnit.SECONDS, this.currentSecond.get() + 1, node); } - if (seconds > 0) { - return insertWheel(ChronoUnit.SECONDS, seconds, id, runTime); + if (seconds > 1) { + return insertWheel(ChronoUnit.SECONDS, this.currentSecond.get() + seconds, node); } log.warn("task is expire: id={} delay={}", id, delay); return false; } - private boolean insertWheel(ChronoUnit unit, long index, String id, long runTime) { + private boolean insertWheel(ChronoUnit unit, long index, TaskNode node) { int idx = (int) index; - LinkedList[] lists = wheels.get(unit); - LinkedList list = lists[idx]; + LinkedList[] lists = wheels.get(unit); + LinkedList list = lists[idx]; if (Objects.isNull(list)) { - list = new LinkedList(); + list = new LinkedList<>(); lists[idx] = list; } - return list.add(new Node(id, null, runTime)); + return list.add(node); } } \ No newline at end of file diff --git a/algorithms/src/test/java/com/github/kuangcp/time/wheel/TimeWheelTest.java b/algorithms/src/test/java/com/github/kuangcp/time/wheel/TimeWheelTest.java index f17a2bf4..adf008d3 100644 --- a/algorithms/src/test/java/com/github/kuangcp/time/wheel/TimeWheelTest.java +++ b/algorithms/src/test/java/com/github/kuangcp/time/wheel/TimeWheelTest.java @@ -1,7 +1,17 @@ package com.github.kuangcp.time.wheel; import java.time.Duration; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Optional; +import java.util.OptionalInt; import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; +import java.util.stream.IntStream; import lombok.extern.slf4j.Slf4j; import org.junit.Assert; import org.junit.Test; @@ -54,24 +64,92 @@ public void testRun2() { } } - private long calculate() { - long sum = 0; - for (int i = 0; i < 10000000; i++) { - Math.sqrt(sum); - sum += i; + runRecord.add(System.currentTimeMillis()); + + OptionalInt reduce = IntStream.rangeClosed(0, 990000).reduce(Integer::sum); + return reduce.orElse(0); + } + + private long calculateComplex() { + runRecord.add(System.currentTimeMillis()); + + Optional sumOpt = IntStream.rangeClosed(0, 990000) + .mapToObj(Long::valueOf) + .sorted(Comparator.comparing(Long::intValue).reversed()) + .sorted(Comparator.comparing(Long::intValue)) + .sorted(Comparator.comparing(Long::intValue).reversed()) + .sorted(Comparator.comparing(Long::intValue)) + .sorted(Comparator.comparing(Long::intValue).reversed()) + .sorted(Comparator.comparing(Long::intValue)) + .sorted(Comparator.comparing(Long::intValue).reversed()) + .sorted(Comparator.comparing(Long::intValue)) + .sorted(Comparator.comparing(Long::intValue).reversed()) + .sorted(Comparator.comparing(Long::intValue)) + .sorted(Comparator.comparing(Long::intValue).reversed()) + .sorted(Comparator.comparing(Long::intValue)) + .sorted(Comparator.comparing(Long::intValue).reversed()) + .sorted(Comparator.comparing(Long::intValue)) + .sorted(Comparator.comparing(Long::intValue).reversed()) + .sorted(Comparator.comparing(Long::intValue)) + .sorted(Comparator.comparing(Long::intValue).reversed()) + .sorted(Comparator.comparing(Long::intValue)) + .sorted(Comparator.comparing(Long::intValue).reversed()) + .sorted(Comparator.comparing(Long::intValue)) + .sorted(Comparator.comparing(Long::intValue).reversed()) + .sorted(Comparator.comparing(Long::intValue)) + .sorted(Comparator.comparing(Long::intValue).reversed()) + .sorted(Comparator.comparing(Long::intValue)) + .sorted(Comparator.comparing(Long::intValue).reversed()) + .sorted(Comparator.comparing(Long::intValue)) + .sorted(Comparator.comparing(Long::intValue).reversed()) + .sorted(Comparator.comparing(Long::intValue)) + .sorted(Comparator.comparing(Long::intValue).reversed()) + .sorted(Comparator.comparing(Long::intValue)) + .sorted(Comparator.comparing(Long::intValue).reversed()) + .sorted(Comparator.comparing(Long::intValue)) + .sorted(Comparator.comparing(Long::intValue).reversed()) + .reduce(Long::sum); + return sumOpt.orElse(0L); + } + + // id -> delay mills + private Map inputData = new HashMap<>(); + // id -> start run mills + private List runRecord = new ArrayList<>(); + private long startTime; + + private void initOverMinData() { + for (int i = 1; i < 12; i++) { + inputData.put("id" + i, i * 10000L); + } + } + + private void showRecords() { + List> entryList = inputData.entrySet().stream() + .sorted(Comparator.comparing(Entry::getValue)) + .collect(Collectors.toList()); + for (int i = 0; i < entryList.size(); i++) { + Long time = runRecord.get(i); + log.info("{}:{} out={} runTime={}", entryList.get(i), time - startTime, + entryList.get(i).getValue() - time + startTime, time); } - return sum; } + // task execute time should << time wheel min slot @Test public void testRushLoop() { - for (int i = 1; i < 7; i++) { - boolean result = timeWheel.add("id" + i, this::calculate, Duration.ofMillis(i * 10000)); - log.info(": result={}", result); + initOverMinData(); + for (Entry entry : inputData.entrySet()) { + boolean result = timeWheel + .add(entry.getKey(), this::calculateComplex, Duration.ofMillis(entry.getValue())); + log.info("add task: id={} result={}", entry.getKey(), result); } + timeWheel.printWheel(); timeWheel.start(); + startTime = System.currentTimeMillis(); + Runtime.getRuntime().addShutdownHook(new Thread(this::showRecords)); new Thread(() -> { while (true) { diff --git a/algorithms/src/test/resources/logback-test.xml b/algorithms/src/test/resources/logback-test.xml index 964f08d6..1b4cbe0f 100644 --- a/algorithms/src/test/resources/logback-test.xml +++ b/algorithms/src/test/resources/logback-test.xml @@ -7,12 +7,9 @@ %d{HH:mm:ss.SSS} %highlight(%-5level) [%thread] %logger{30}:%yellow(%M %-3L) %msg%n - - DEBUG - - + From ad36853eddaf007d0ad905aa285536fa7922d11c Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Tue, 29 Oct 2019 01:15:22 +0800 Subject: [PATCH 111/476] +) debugMode ignore time --- .../github/kuangcp/time/wheel/TimeWheel.java | 27 ++++++++++++------- .../kuangcp/time/wheel/TimeWheelTest.java | 13 ++++----- 2 files changed, 25 insertions(+), 15 deletions(-) diff --git a/algorithms/src/main/java/com/github/kuangcp/time/wheel/TimeWheel.java b/algorithms/src/main/java/com/github/kuangcp/time/wheel/TimeWheel.java index 984fdc5e..9e662a83 100644 --- a/algorithms/src/main/java/com/github/kuangcp/time/wheel/TimeWheel.java +++ b/algorithms/src/main/java/com/github/kuangcp/time/wheel/TimeWheel.java @@ -27,6 +27,7 @@ @Slf4j public class TimeWheel { + private boolean debugMode = false; private volatile boolean stop = false; private volatile boolean needExitVM = false; @@ -86,9 +87,10 @@ private void init() { public TimeWheel() { } - public TimeWheel(long maxTimeout, boolean needExitVM) { + public TimeWheel(long maxTimeout, boolean needExitVM, boolean debugMode) { this.maxTimeout = maxTimeout; this.needExitVM = needExitVM; + this.debugMode = debugMode; } public void start() { @@ -96,9 +98,11 @@ public void start() { while (!stop) { long currentMills = System.currentTimeMillis(); try { - TimeUnit.MICROSECONDS.sleep(20); - if (currentMills - this.currentMills < 1000) { - continue; + if (!debugMode) { + TimeUnit.MICROSECONDS.sleep(20); + if (currentMills - this.currentMills < 1000) { + continue; + } } log.debug("cache: keys={}", cacheTasks.keySet()); @@ -119,7 +123,6 @@ public void start() { LinkedList[] lists = wheels.get(ChronoUnit.SECONDS); LinkedList list = lists[seconds % SECONDS_SLOT]; this.invokeAll(seconds, list); - } catch (Exception e) { log.error("", e); } @@ -134,7 +137,10 @@ private void removeAndAdd(ChronoUnit unit, int index) { List nodes = list.toList(); list.clear(); for (TaskNode node : nodes) { - long millis = node.getLastTime() - System.currentTimeMillis(); + long millis = node.getLastTime() + node.getDelayMills() - System.currentTimeMillis(); + if (debugMode) { + millis -= SECONDS_SLOT * 1000; + } log.warn(": millis={}", millis); if (millis < 0) { log.error("system lose task: node={}", node); @@ -152,11 +158,13 @@ private void pushTimeWheel(int seconds) { for (int i = 0; i < sortedSlots.size(); i++) { ChronoUnit unit = sortedSlots.get(i); int temp = -1; + Integer threshold = slots.get(unit); if (i == 0) { - if (seconds >= slots.get(unit)) { + if (seconds >= threshold) { AtomicInteger counter = counters.get(unit); counter.set(0); temp = counters.get(sortedSlots.get(i + 1)).incrementAndGet(); + temp %= threshold; } } else if (i == sortedSlots.size() - 1) { AtomicInteger counter = counters.get(unit); @@ -164,10 +172,11 @@ private void pushTimeWheel(int seconds) { this.removeAndAdd(unit, 0); temp = -1; } else { - if (temp > slots.get(unit)) { + if (temp > threshold) { AtomicInteger counter = counters.get(unit); counter.set(0); temp = counters.get(sortedSlots.get(i + 1)).incrementAndGet(); + temp %= threshold; } } @@ -271,7 +280,7 @@ private boolean insertWheel(String id, Duration delay) { long hours = delay.toHours(); if (hours > 0) { - return insertWheel(ChronoUnit.HOURS, this.currentHour.get() + hours,node); + return insertWheel(ChronoUnit.HOURS, this.currentHour.get() + hours, node); } long minutes = delay.toMinutes(); diff --git a/algorithms/src/test/java/com/github/kuangcp/time/wheel/TimeWheelTest.java b/algorithms/src/test/java/com/github/kuangcp/time/wheel/TimeWheelTest.java index adf008d3..eb2cf9e4 100644 --- a/algorithms/src/test/java/com/github/kuangcp/time/wheel/TimeWheelTest.java +++ b/algorithms/src/test/java/com/github/kuangcp/time/wheel/TimeWheelTest.java @@ -22,7 +22,7 @@ @Slf4j public class TimeWheelTest { - private TimeWheel timeWheel = new TimeWheel(3000L, true); + private TimeWheel timeWheel = new TimeWheel(3000L, true, false); @Test public void testAdd() throws Exception { @@ -67,7 +67,7 @@ public void testRun2() { private long calculate() { runRecord.add(System.currentTimeMillis()); - OptionalInt reduce = IntStream.rangeClosed(0, 990000).reduce(Integer::sum); + OptionalInt reduce = IntStream.rangeClosed(0, 200).reduce(Integer::sum); return reduce.orElse(0); } @@ -120,8 +120,8 @@ private long calculateComplex() { private long startTime; private void initOverMinData() { - for (int i = 1; i < 12; i++) { - inputData.put("id" + i, i * 10000L); + for (int i = 1; i < 30; i++) { + inputData.put("id" + i, i * 6070L); } } @@ -131,8 +131,9 @@ private void showRecords() { .collect(Collectors.toList()); for (int i = 0; i < entryList.size(); i++) { Long time = runRecord.get(i); + Long delayMills = entryList.get(i).getValue(); log.info("{}:{} out={} runTime={}", entryList.get(i), time - startTime, - entryList.get(i).getValue() - time + startTime, time); + time - startTime - delayMills, time); } } @@ -142,7 +143,7 @@ public void testRushLoop() { initOverMinData(); for (Entry entry : inputData.entrySet()) { boolean result = timeWheel - .add(entry.getKey(), this::calculateComplex, Duration.ofMillis(entry.getValue())); + .add(entry.getKey(), this::calculate, Duration.ofMillis(entry.getValue())); log.info("add task: id={} result={}", entry.getKey(), result); } From e85d086805907881bd94a3f26c6ca21cf214d93a Mon Sep 17 00:00:00 2001 From: kcp Date: Tue, 29 Oct 2019 13:31:52 +0800 Subject: [PATCH 112/476] *) optmimized test --- .../github/kuangcp/time/wheel/TimeWheel.java | 22 ++++++++++++------- .../kuangcp/time/wheel/TimeWheelTest.java | 10 +++++---- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/algorithms/src/main/java/com/github/kuangcp/time/wheel/TimeWheel.java b/algorithms/src/main/java/com/github/kuangcp/time/wheel/TimeWheel.java index 9e662a83..4e71aab9 100644 --- a/algorithms/src/main/java/com/github/kuangcp/time/wheel/TimeWheel.java +++ b/algorithms/src/main/java/com/github/kuangcp/time/wheel/TimeWheel.java @@ -19,8 +19,7 @@ /** * https://github.com/wolaiye1010/zdc-java-script * - * 类似于 HashMap的设计结构 - * 设计支持 30 天内延迟任务 + * 类似于 HashMap的设计结构 设计支持 30 天内延迟任务 * * @author https://github.com/kuangcp on 2019-10-11 20:53 */ @@ -33,7 +32,7 @@ public class TimeWheel { private volatile long startEmptyTime = -1; private volatile long maxTimeout = 10L; - private volatile long currentMills; + private volatile long lastCheckMills; private static final int SECONDS_SLOT = 60; private static final int MINUTES_SLOT = 60; @@ -100,7 +99,7 @@ public void start() { try { if (!debugMode) { TimeUnit.MICROSECONDS.sleep(20); - if (currentMills - this.currentMills < 1000) { + if (currentMills - this.lastCheckMills < 1000) { continue; } } @@ -116,7 +115,7 @@ public void start() { } } - this.currentMills = currentMills; + this.lastCheckMills = currentMills; int seconds = this.currentSecond.incrementAndGet(); this.pushTimeWheel(seconds); @@ -141,9 +140,8 @@ private void removeAndAdd(ChronoUnit unit, int index) { if (debugMode) { millis -= SECONDS_SLOT * 1000; } - log.warn(": millis={}", millis); if (millis < 0) { - log.error("system lose task: node={}", node); + log.error("system invoke task delay: delay={} node={}", millis, node); } Duration delay = Duration.ofMillis(millis); @@ -153,6 +151,7 @@ private void removeAndAdd(ChronoUnit unit, int index) { } } + // TODO BUG private void pushTimeWheel(int seconds) { Map tempIndex = new HashMap<>(sortedSlots.size()); for (int i = 0; i < sortedSlots.size(); i++) { @@ -177,6 +176,9 @@ private void pushTimeWheel(int seconds) { counter.set(0); temp = counters.get(sortedSlots.get(i + 1)).incrementAndGet(); temp %= threshold; + } else { + AtomicInteger counter = counters.get(unit); + temp = counter.incrementAndGet(); } } @@ -245,6 +247,8 @@ public boolean add(String id, Callable func, Duration delay) { } public void printWheel() { + log.info("sec:{} min:{} hour:{}", this.currentSecond.get(), this.currentMinute.get(), + this.currentHour.get()); for (ChronoUnit unit : wheels.keySet()) { LinkedList[] lists = wheels.get(unit); @@ -302,7 +306,9 @@ private boolean insertWheel(String id, Duration delay) { } private boolean insertWheel(ChronoUnit unit, long index, TaskNode node) { - int idx = (int) index; + Integer slot = slots.get(unit); + int idx = (int) index % slot; + LinkedList[] lists = wheels.get(unit); LinkedList list = lists[idx]; if (Objects.isNull(list)) { diff --git a/algorithms/src/test/java/com/github/kuangcp/time/wheel/TimeWheelTest.java b/algorithms/src/test/java/com/github/kuangcp/time/wheel/TimeWheelTest.java index eb2cf9e4..0673a35b 100644 --- a/algorithms/src/test/java/com/github/kuangcp/time/wheel/TimeWheelTest.java +++ b/algorithms/src/test/java/com/github/kuangcp/time/wheel/TimeWheelTest.java @@ -22,7 +22,7 @@ @Slf4j public class TimeWheelTest { - private TimeWheel timeWheel = new TimeWheel(3000L, true, false); + private TimeWheel timeWheel = new TimeWheel(10L, true, true); @Test public void testAdd() throws Exception { @@ -120,8 +120,10 @@ private long calculateComplex() { private long startTime; private void initOverMinData() { - for (int i = 1; i < 30; i++) { - inputData.put("id" + i, i * 6070L); + int dataSize = 60 * 60 * 2; + dataSize /= 1000; + for (int i = 1; i < dataSize; i++) { + inputData.put("id" + i, i * 1000000L); } } @@ -144,7 +146,7 @@ public void testRushLoop() { for (Entry entry : inputData.entrySet()) { boolean result = timeWheel .add(entry.getKey(), this::calculate, Duration.ofMillis(entry.getValue())); - log.info("add task: id={} result={}", entry.getKey(), result); + log.debug("add task: id={} result={}", entry.getKey(), result); } timeWheel.printWheel(); From 67ae5b97815035367e754ba1b9f9285e58d81d17 Mon Sep 17 00:00:00 2001 From: kcp Date: Sat, 2 Nov 2019 12:02:56 +0800 Subject: [PATCH 113/476] *) delete deprecated --- algorithms/build.gradle | 2 +- build.gradle | 4 ++-- concurrency/build.gradle | 4 ++-- java8/build.gradle | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/algorithms/build.gradle b/algorithms/build.gradle index 5730d827..ca03cdc0 100644 --- a/algorithms/build.gradle +++ b/algorithms/build.gradle @@ -1,3 +1,3 @@ dependencies { - compile group: 'com.github.jsonzou', name: 'jmockdata', version: '4.1.2' + implementation(group: 'com.github.jsonzou', name: 'jmockdata', version: '4.1.2') } \ No newline at end of file diff --git a/build.gradle b/build.gradle index 36145578..9a4d2d94 100644 --- a/build.gradle +++ b/build.gradle @@ -3,7 +3,7 @@ apply from: 'dependency.gradle' // parent and child project all use this config subprojects { apply plugin: 'java' - apply plugin: 'maven' + apply plugin: 'maven-publish' group = 'com.github.kuangcp' @@ -17,7 +17,7 @@ subprojects { repositories { mavenLocal() - def aliYun = "http://maven.aliyun.com/nexus/content/groups/public/" + def aliYun = "https://maven.aliyun.com/nexus/content/groups/public/" def kuangcp = "https://gitee.com/gin9/MavenRepos/raw/master" maven { diff --git a/concurrency/build.gradle b/concurrency/build.gradle index 4bf22a9e..e2d123d6 100644 --- a/concurrency/build.gradle +++ b/concurrency/build.gradle @@ -1,11 +1,11 @@ plugins { id 'groovy' - id 'maven' + id 'maven-publish' } dependencies { implementation libs['jedis'] implementation libs['groovy'] implementation libs['common-lang'] - testCompile libs['testng'] + testImplementation(libs['testng']) } \ No newline at end of file diff --git a/java8/build.gradle b/java8/build.gradle index 09e18ae2..043b5905 100644 --- a/java8/build.gradle +++ b/java8/build.gradle @@ -1,3 +1,3 @@ dependencies { - compile rootProject.libs['kcp-tool'] + implementation(rootProject.libs['kcp-tool']) } From 8bf50b2d61043617cdbf42d059f76d916196fd28 Mon Sep 17 00:00:00 2001 From: kcp Date: Sat, 2 Nov 2019 14:24:25 +0800 Subject: [PATCH 114/476] x) fixed dead loop, also have bug --- .../github/kuangcp/time/wheel/TimeWheel.java | 47 +++++++++++-------- .../kuangcp/time/wheel/TimeWheelTest.java | 4 +- 2 files changed, 30 insertions(+), 21 deletions(-) diff --git a/algorithms/src/main/java/com/github/kuangcp/time/wheel/TimeWheel.java b/algorithms/src/main/java/com/github/kuangcp/time/wheel/TimeWheel.java index 4e71aab9..b80c711c 100644 --- a/algorithms/src/main/java/com/github/kuangcp/time/wheel/TimeWheel.java +++ b/algorithms/src/main/java/com/github/kuangcp/time/wheel/TimeWheel.java @@ -6,6 +6,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Objects; import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentHashMap; @@ -153,42 +154,49 @@ private void removeAndAdd(ChronoUnit unit, int index) { // TODO BUG private void pushTimeWheel(int seconds) { - Map tempIndex = new HashMap<>(sortedSlots.size()); + Map tempIndex = new HashMap<>(sortedSlots.size()); for (int i = 0; i < sortedSlots.size(); i++) { ChronoUnit unit = sortedSlots.get(i); - int temp = -1; Integer threshold = slots.get(unit); + int upIndex = -1; + + // 最小轮 if (i == 0) { if (seconds >= threshold) { + int up = seconds / threshold; AtomicInteger counter = counters.get(unit); counter.set(0); - temp = counters.get(sortedSlots.get(i + 1)).incrementAndGet(); - temp %= threshold; + upIndex = counters.get(sortedSlots.get(i + 1)).addAndGet(up); + } else { + break; } + // 最大轮 } else if (i == sortedSlots.size() - 1) { AtomicInteger counter = counters.get(unit); counter.set(0); this.removeAndAdd(unit, 0); - temp = -1; + upIndex = -1; } else { - if (temp > threshold) { - AtomicInteger counter = counters.get(unit); + AtomicInteger counter = counters.get(unit); + if (counter.get() >= threshold) { counter.set(0); - temp = counters.get(sortedSlots.get(i + 1)).incrementAndGet(); - temp %= threshold; - } else { - AtomicInteger counter = counters.get(unit); - temp = counter.incrementAndGet(); + upIndex = counters.get(sortedSlots.get(i + 1)).incrementAndGet(); } } - tempIndex.put(i + 1, temp); + if (upIndex != -1) { + tempIndex.put(sortedSlots.get(i + 1), upIndex); + } } - - for (int i = sortedSlots.size() - 1; i > 0; i--) { - Integer nextIndex = tempIndex.get(i); - if (nextIndex != -1) { - this.removeAndAdd(sortedSlots.get(i), nextIndex); +// if (!tempIndex.isEmpty()) { +// log.info(": tempIndex={}", tempIndex); +// } + + if (!tempIndex.isEmpty()) { + for (Entry entry : tempIndex.entrySet()) { + ChronoUnit unit = entry.getKey(); + Integer value = entry.getValue(); + this.removeAndAdd(unit, value % slots.get(unit)); } } } @@ -206,7 +214,8 @@ private void invokeAll(int seconds, LinkedList list) { log.debug("[{}]before invoke: id={}", seconds, id); try { Future result = pool.submit(func); - log.info("[{}]invoke: id={} result={}", seconds, id, result.get()); + log.info("[{}:{}:{}] id={} result={}", this.currentSecond.get(), this.currentMinute.get(), + this.currentHour.get(), id, result.get()); cacheTasks.remove(id); } catch (Exception e) { log.error("", e); diff --git a/algorithms/src/test/java/com/github/kuangcp/time/wheel/TimeWheelTest.java b/algorithms/src/test/java/com/github/kuangcp/time/wheel/TimeWheelTest.java index 0673a35b..3b8cee46 100644 --- a/algorithms/src/test/java/com/github/kuangcp/time/wheel/TimeWheelTest.java +++ b/algorithms/src/test/java/com/github/kuangcp/time/wheel/TimeWheelTest.java @@ -121,9 +121,9 @@ private long calculateComplex() { private void initOverMinData() { int dataSize = 60 * 60 * 2; - dataSize /= 1000; + dataSize = 10; for (int i = 1; i < dataSize; i++) { - inputData.put("id" + i, i * 1000000L); + inputData.put("id" + i, i * 65_000L); } } From 1ac4c38e5c2fc9a4a8860d39936027b4c9d88259 Mon Sep 17 00:00:00 2001 From: kcp Date: Fri, 8 Nov 2019 19:04:51 +0800 Subject: [PATCH 115/476] +) remove special char --- class/src/test/java/syntax/string/StringTest.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/class/src/test/java/syntax/string/StringTest.java b/class/src/test/java/syntax/string/StringTest.java index f91c722e..6455d33c 100644 --- a/class/src/test/java/syntax/string/StringTest.java +++ b/class/src/test/java/syntax/string/StringTest.java @@ -96,4 +96,9 @@ public void testIntern() { String b = new StringBuilder("Ja").append("va").toString(); Assert.assertFalse(b.intern() == b); } + + @Test + public void testReplaceSpecialChar(){ + "".replaceAll("[\\ud800\\udc00-\\udbff\\udfff\\ud800-\\udfff]", "*"); + } } From aac52ee568b6fe8835a265f9bee361b3f3fb25e4 Mon Sep 17 00:00:00 2001 From: kcp Date: Thu, 14 Nov 2019 20:28:45 +0800 Subject: [PATCH 116/476] +) init config --- .../kuangcp/wrapper/KafkaProducerWrapper.java | 8 ++ .../com/github/kuangcp/wrapper/Message.java | 33 ++++++++ .../wrapper/config/KafkaConfigManager.java | 40 ++++++++++ .../wrapper/config/KafkaConsumerConfig.java | 63 +++++++++++++++ .../wrapper/config/KafkaProducerConfig.java | 76 +++++++++++++++++++ .../consumer/KafkaConsumerWrapper.java | 31 ++++++++ .../wrapper/consumer/MessageExecutor.java | 14 ++++ .../consumer/SingleTopicMessageExecutor.java | 9 +++ 8 files changed, 274 insertions(+) create mode 100644 kafka/src/main/java/com/github/kuangcp/wrapper/KafkaProducerWrapper.java create mode 100644 kafka/src/main/java/com/github/kuangcp/wrapper/Message.java create mode 100644 kafka/src/main/java/com/github/kuangcp/wrapper/config/KafkaConfigManager.java create mode 100644 kafka/src/main/java/com/github/kuangcp/wrapper/config/KafkaConsumerConfig.java create mode 100644 kafka/src/main/java/com/github/kuangcp/wrapper/config/KafkaProducerConfig.java create mode 100644 kafka/src/main/java/com/github/kuangcp/wrapper/consumer/KafkaConsumerWrapper.java create mode 100644 kafka/src/main/java/com/github/kuangcp/wrapper/consumer/MessageExecutor.java create mode 100644 kafka/src/main/java/com/github/kuangcp/wrapper/consumer/SingleTopicMessageExecutor.java diff --git a/kafka/src/main/java/com/github/kuangcp/wrapper/KafkaProducerWrapper.java b/kafka/src/main/java/com/github/kuangcp/wrapper/KafkaProducerWrapper.java new file mode 100644 index 00000000..f7550b6c --- /dev/null +++ b/kafka/src/main/java/com/github/kuangcp/wrapper/KafkaProducerWrapper.java @@ -0,0 +1,8 @@ +package com.github.kuangcp.wrapper; + +/** + * @author https://github.com/kuangcp on 2019-11-13 09:36 + */ +public class KafkaProducerWrapper { + +} diff --git a/kafka/src/main/java/com/github/kuangcp/wrapper/Message.java b/kafka/src/main/java/com/github/kuangcp/wrapper/Message.java new file mode 100644 index 00000000..92d80233 --- /dev/null +++ b/kafka/src/main/java/com/github/kuangcp/wrapper/Message.java @@ -0,0 +1,33 @@ +package com.github.kuangcp.wrapper; + +import java.util.Date; +import java.util.UUID; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author https://github.com/kuangcp on 2019-11-13 09:48 + */ +@Data +@NoArgsConstructor +public class Message { + + /** + * 消息Id + */ + private String id = UUID.randomUUID().toString(); + + /** + * 消息创建时间 + */ + private Date createTime = new Date(); + + /** + * 消息内容 + */ + private T content; + + public Message(T content) { + this.content = content; + } +} \ No newline at end of file diff --git a/kafka/src/main/java/com/github/kuangcp/wrapper/config/KafkaConfigManager.java b/kafka/src/main/java/com/github/kuangcp/wrapper/config/KafkaConfigManager.java new file mode 100644 index 00000000..1abd1c3e --- /dev/null +++ b/kafka/src/main/java/com/github/kuangcp/wrapper/config/KafkaConfigManager.java @@ -0,0 +1,40 @@ +package com.github.kuangcp.wrapper.config; + +import static com.github.kuangcp.hi.Constants.KAFKA_SERVER; + +import java.util.Properties; +import java.util.UUID; +import org.apache.kafka.common.serialization.StringDeserializer; + +/** + * @author https://github.com/kuangcp on 2019-11-13 09:37 + */ +public class KafkaConfigManager { + + public static Properties getConsumerConfig() { + Properties properties = new Properties(); + properties.put("bootstrap.servers", KAFKA_SERVER); + properties.put("group.id", "TestGroup-" + UUID.randomUUID().toString()); + properties.put("enable.auto.commit", "true"); + properties.put("auto.commit.interval.ms", "1000"); + properties.put("auto.offset.reset", "earliest"); + properties.put("session.timeout.ms", "30000"); + properties.put("key.deserializer", StringDeserializer.class.getName()); + properties.put("value.deserializer", StringDeserializer.class.getName()); + return properties; + } + + public static Properties getProducerConfig() { + Properties properties = new Properties(); + properties.put("bootstrap.servers", KAFKA_SERVER); + properties.put("acks", "all"); + properties.put("retries", 0); + properties.put("batch.size", 16384); + properties.put("linger.ms", 1); + properties.put("buffer.memory", 33554432); + + properties.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer"); + properties.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer"); + return properties; + } +} diff --git a/kafka/src/main/java/com/github/kuangcp/wrapper/config/KafkaConsumerConfig.java b/kafka/src/main/java/com/github/kuangcp/wrapper/config/KafkaConsumerConfig.java new file mode 100644 index 00000000..edbe1695 --- /dev/null +++ b/kafka/src/main/java/com/github/kuangcp/wrapper/config/KafkaConsumerConfig.java @@ -0,0 +1,63 @@ +package com.github.kuangcp.wrapper.config; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author https://github.com/kuangcp on 2019-11-13 09:43 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class KafkaConsumerConfig { + + //group.id + private String groupId; + + //zookeeper.connect + //zookeeper server list + private String zookeeperServerIpAndPortList; + + //zookeeper.session.timeout.ms + // default 6000 + private String zookeeperSessionTimeout; + //zookeeper.connection.timeout.ms + //timeout in ms for connecting to zookeeper + // default =zookeeperSessionTimeout =6000 + private String zookeeperConnectionTimeout; + //zookeeper.sync.time.ms + // default 2000 + private String zookeeperSyncTime; + + //consumer.timeout.ms + // default -1 + private String consumerTimeout; + + //auto.commit.enable + //如果设为true,consumer会定时向ZooKeeper发送已经获取到的消息的offset + // 当consumer进程挂掉时,已经提交的offset可以继续使用,让新的consumer继续工作 + // 如果为false,会重复读取挂掉的consumer已经读取过的消息 + //default true + private String autoCommitEnable; + + //auto.commit.interval.ms + //consumer向ZooKeeper发送offset的时间间隔 + //default 60000 + private String autoCommitInterval; + + //rebalance.backoff.ms + //该设置过短就会导致old consumer还没有来得及释放资源,new consumer重试失败多次到达阀值就退出了。 + // 抛出 ConsumerRebalanceFailedException + //所以要确保rebalanceMaxRetries * rebalanceMaxRetries > zookeeperSessionTimeout + // default =zookeeperSyncTime =2000 + private String reBalanceBackOff; + //rebalance.max.retries + // default 4 + private String rebalanceMaxRetries; + + //---还有更多配置,详见ConsumerConfig, + +} diff --git a/kafka/src/main/java/com/github/kuangcp/wrapper/config/KafkaProducerConfig.java b/kafka/src/main/java/com/github/kuangcp/wrapper/config/KafkaProducerConfig.java new file mode 100644 index 00000000..07410aec --- /dev/null +++ b/kafka/src/main/java/com/github/kuangcp/wrapper/config/KafkaProducerConfig.java @@ -0,0 +1,76 @@ +package com.github.kuangcp.wrapper.config; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author https://github.com/kuangcp on 2019-11-13 09:44 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class KafkaProducerConfig { + + //metadata.broker.list + //broker列表可以为kafka server的子集,因为producer需要从broker中获取metadata + //尽管每个broker都可以提供metadata,此处还是建议,将所有broker都列举出来 + private String kafkaBrokerIpAndPortList; + + //producer.type + //消息发送的模式,同步或异步,异步的时候消息将会在本地buffer,并适时批量发送 + //default sync + private String producerType; + + //batch.num.messages + //消息在producer端buffer的条数,仅在producer.type=async下有效 + //default 200 + private String batchNumMessages; + + //request.required.acks + //producer接收消息ack的时机,默认为0 + // 0:produce人不会等待broker发送ack + // 1:当leader接收到消息后发送ack + // 2:当所有的follower都同步消息成功后发送ack + private String requestRequiredAcks; + + //serializer.class + //消息序列化类 + //向kafka发送数据,默认支持String和byte[]2种类型,分别支持String和二进制 + //包括kafka.serializer.StringEncoder和kafka.serializer.DefaultEncoder 2个类 + //default kafka.serializer.DefaultEncoder + private String serializerClass; + //key.serializer.class + //default =serializer.class =kafka.serializer.StringEncoder + private String keySerializerClass; + + //compression.codec + //消息压缩算法,none,gzip,snappy + //default none + private String compressionCodec; + //compressed.topics + //default null + private String compressedTopics; + + //partitioner.class + //partitions路由类,消息在发送时将根据此实例的方法获得partition索引号 + //default kafka.producer.DefaultPartitioner + private String partitionerClass; + + + //message.send.max.retries + //default 3 + private String messageSendMaxRetries; + + //retry.backoff.ms + //default 100 + private String retryBackoff; + + //topic.metadata.refresh.interval.ms + //default 600000 + private String topicMetadataRefreshInterval; + + //---还有更多配置,详见ProducerConfig +} diff --git a/kafka/src/main/java/com/github/kuangcp/wrapper/consumer/KafkaConsumerWrapper.java b/kafka/src/main/java/com/github/kuangcp/wrapper/consumer/KafkaConsumerWrapper.java new file mode 100644 index 00000000..d13e42da --- /dev/null +++ b/kafka/src/main/java/com/github/kuangcp/wrapper/consumer/KafkaConsumerWrapper.java @@ -0,0 +1,31 @@ +package com.github.kuangcp.wrapper.consumer; + +import com.github.kuangcp.wrapper.config.KafkaConfigManager; +import java.time.Duration; +import java.util.Collection; +import java.util.Collections; +import java.util.Properties; +import lombok.extern.slf4j.Slf4j; +import org.apache.kafka.clients.consumer.ConsumerRecord; +import org.apache.kafka.clients.consumer.ConsumerRecords; +import org.apache.kafka.clients.consumer.KafkaConsumer; + +/** + * @author https://github.com/kuangcp on 2019-11-13 09:35 + */ +@Slf4j +public class KafkaConsumerWrapper { + + private static Properties properties = KafkaConfigManager.getConsumerConfig(); + + static void consumer(Duration duration, Collection topics) { + KafkaConsumer kafkaConsumer = new KafkaConsumer<>(properties); + kafkaConsumer.subscribe(topics); + while (true) { + ConsumerRecords records = kafkaConsumer.poll(duration); + for (ConsumerRecord record : records) { + log.info("offset = {}, value = {}", record.offset(), record.value()); + } + } + } +} diff --git a/kafka/src/main/java/com/github/kuangcp/wrapper/consumer/MessageExecutor.java b/kafka/src/main/java/com/github/kuangcp/wrapper/consumer/MessageExecutor.java new file mode 100644 index 00000000..146f9f17 --- /dev/null +++ b/kafka/src/main/java/com/github/kuangcp/wrapper/consumer/MessageExecutor.java @@ -0,0 +1,14 @@ +package com.github.kuangcp.wrapper.consumer; + +import com.github.kuangcp.wrapper.Message; + +/** + * @author https://github.com/kuangcp on 2019-11-13 09:47 + */ +public interface MessageExecutor { + + /** + * 处理消息 + */ + void execute(Message message); +} diff --git a/kafka/src/main/java/com/github/kuangcp/wrapper/consumer/SingleTopicMessageExecutor.java b/kafka/src/main/java/com/github/kuangcp/wrapper/consumer/SingleTopicMessageExecutor.java new file mode 100644 index 00000000..fde3ac24 --- /dev/null +++ b/kafka/src/main/java/com/github/kuangcp/wrapper/consumer/SingleTopicMessageExecutor.java @@ -0,0 +1,9 @@ +package com.github.kuangcp.wrapper.consumer; + +/** + * @author https://github.com/kuangcp on 2019-11-13 09:49 + */ +public interface SingleTopicMessageExecutor extends MessageExecutor { + + String getTopic(); +} From 1aa06fe9d3127ed9b07125ae6dafe849f442d97e Mon Sep 17 00:00:00 2001 From: kcp Date: Sat, 16 Nov 2019 16:12:47 +0800 Subject: [PATCH 117/476] +) update kafka client version --- dependency.gradle | 2 +- kafka/src/test/resources/logback.xml | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 kafka/src/test/resources/logback.xml diff --git a/dependency.gradle b/dependency.gradle index 30deb491..a01a1469 100644 --- a/dependency.gradle +++ b/dependency.gradle @@ -14,7 +14,7 @@ ext { , netty : '4.1.35.Final' , guava : '27.1-jre' , jmh : '1.2.1' - , kafka : '2.2.0' + , kafka : '2.3.1' , spring : '5.1.5.RELEASE' , aspectj : '1.9.0' , hadoop : '2.7.2' diff --git a/kafka/src/test/resources/logback.xml b/kafka/src/test/resources/logback.xml new file mode 100644 index 00000000..8523e298 --- /dev/null +++ b/kafka/src/test/resources/logback.xml @@ -0,0 +1,22 @@ + + + + + + + + + %d{HH:mm:ss.SSS} %highlight(%-5level) [%thread] %logger{30} %highlight(%M):%yellow(%-3L) %msg%n + + + DEBUG + + + + + + + + + + \ No newline at end of file From e3f68ec7f2444f85c45c5eb2fc1a99f5d36409e6 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Sat, 16 Nov 2019 20:20:02 +0800 Subject: [PATCH 118/476] +) murmurhash --- algorithms/build.gradle | 1 + .../java/com/github/kuangcp/bloom/filter/BloomFilter.java | 1 + .../java/com/github/kuangcp/bloom/filter/HashFunctions.java | 5 +++++ .../main/java/com/github/kuangcp/time/wheel/LinkedList.java | 1 + dependency.gradle | 1 + 5 files changed, 9 insertions(+) diff --git a/algorithms/build.gradle b/algorithms/build.gradle index ca03cdc0..6652e1a3 100644 --- a/algorithms/build.gradle +++ b/algorithms/build.gradle @@ -1,3 +1,4 @@ dependencies { implementation(group: 'com.github.jsonzou', name: 'jmockdata', version: '4.1.2') + implementation(libs['common-codec']) } \ No newline at end of file diff --git a/algorithms/src/main/java/com/github/kuangcp/bloom/filter/BloomFilter.java b/algorithms/src/main/java/com/github/kuangcp/bloom/filter/BloomFilter.java index 74306e9f..a2eac920 100644 --- a/algorithms/src/main/java/com/github/kuangcp/bloom/filter/BloomFilter.java +++ b/algorithms/src/main/java/com/github/kuangcp/bloom/filter/BloomFilter.java @@ -22,6 +22,7 @@ public class BloomFilter { static { functions.add(HashFunctions.hashByObjects); functions.add(HashFunctions.hashWithString); + functions.add(HashFunctions.murmurHash2); } public void add(String str) { diff --git a/algorithms/src/main/java/com/github/kuangcp/bloom/filter/HashFunctions.java b/algorithms/src/main/java/com/github/kuangcp/bloom/filter/HashFunctions.java index a104bf4c..3191cbcb 100644 --- a/algorithms/src/main/java/com/github/kuangcp/bloom/filter/HashFunctions.java +++ b/algorithms/src/main/java/com/github/kuangcp/bloom/filter/HashFunctions.java @@ -2,6 +2,7 @@ import java.util.Objects; import java.util.function.Function; +import org.apache.commons.codec.digest.MurmurHash2; /** * https://stackoverflow.com/questions/34595/what-is-a-good-hash-function @@ -33,4 +34,8 @@ public class HashFunctions { } return hash > 0 ? hash : -hash; }; + + // Kafka 默认分区器 hash 算法 + static Function murmurHash2 = MurmurHash2::hash32; + } diff --git a/algorithms/src/main/java/com/github/kuangcp/time/wheel/LinkedList.java b/algorithms/src/main/java/com/github/kuangcp/time/wheel/LinkedList.java index 56832a31..0dadb502 100644 --- a/algorithms/src/main/java/com/github/kuangcp/time/wheel/LinkedList.java +++ b/algorithms/src/main/java/com/github/kuangcp/time/wheel/LinkedList.java @@ -33,6 +33,7 @@ void clear() { this.tail = null; } + @SuppressWarnings("unchecked") List toList() { List result = new ArrayList<>(len); T pointer = this.head; diff --git a/dependency.gradle b/dependency.gradle index a01a1469..abba913f 100644 --- a/dependency.gradle +++ b/dependency.gradle @@ -30,6 +30,7 @@ ext { , "groovy" : "org.codehaus.groovy:groovy-all:$ver.groovy" , "cglib" : "cglib:cglib:3.2.9" , "common-math" : "org.apache.commons:commons-math3:3.6.1" + , "common-codec" : "commons-codec:commons-codec:1.13" , "guava" : "com.google.guava:guava:$ver.guava" // test, junit has removed hamcrest since 4.11 From 7f173e7910226a2c03e2cc0a8a9e79f616650dd7 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Sun, 17 Nov 2019 14:28:07 +0800 Subject: [PATCH 119/476] *) optimized config --- .../wrapper/config/KafkaConfigManager.java | 43 ++++++++++--------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/kafka/src/main/java/com/github/kuangcp/wrapper/config/KafkaConfigManager.java b/kafka/src/main/java/com/github/kuangcp/wrapper/config/KafkaConfigManager.java index 1abd1c3e..2678613c 100644 --- a/kafka/src/main/java/com/github/kuangcp/wrapper/config/KafkaConfigManager.java +++ b/kafka/src/main/java/com/github/kuangcp/wrapper/config/KafkaConfigManager.java @@ -4,7 +4,10 @@ import java.util.Properties; import java.util.UUID; +import org.apache.kafka.clients.consumer.ConsumerConfig; +import org.apache.kafka.clients.producer.ProducerConfig; import org.apache.kafka.common.serialization.StringDeserializer; +import org.apache.kafka.common.serialization.StringSerializer; /** * @author https://github.com/kuangcp on 2019-11-13 09:37 @@ -12,29 +15,29 @@ public class KafkaConfigManager { public static Properties getConsumerConfig() { - Properties properties = new Properties(); - properties.put("bootstrap.servers", KAFKA_SERVER); - properties.put("group.id", "TestGroup-" + UUID.randomUUID().toString()); - properties.put("enable.auto.commit", "true"); - properties.put("auto.commit.interval.ms", "1000"); - properties.put("auto.offset.reset", "earliest"); - properties.put("session.timeout.ms", "30000"); - properties.put("key.deserializer", StringDeserializer.class.getName()); - properties.put("value.deserializer", StringDeserializer.class.getName()); - return properties; + Properties config = new Properties(); + config.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, KAFKA_SERVER); + config.put(ConsumerConfig.GROUP_ID_CONFIG, "TestGroup-" + UUID.randomUUID().toString()); + config.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "true"); + config.put(ConsumerConfig.AUTO_COMMIT_INTERVAL_MS_CONFIG, "1000"); + config.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest"); + config.put(ConsumerConfig.SESSION_TIMEOUT_MS_CONFIG, "30000"); + config.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName()); + config.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName()); + return config; } public static Properties getProducerConfig() { - Properties properties = new Properties(); - properties.put("bootstrap.servers", KAFKA_SERVER); - properties.put("acks", "all"); - properties.put("retries", 0); - properties.put("batch.size", 16384); - properties.put("linger.ms", 1); - properties.put("buffer.memory", 33554432); + Properties config = new Properties(); + config.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, KAFKA_SERVER); + config.put(ProducerConfig.ACKS_CONFIG, "all"); + config.put(ProducerConfig.RETRIES_CONFIG, 0); + config.put(ProducerConfig.BATCH_SIZE_CONFIG, 16384); + config.put(ProducerConfig.LINGER_MS_CONFIG, 1); + config.put(ProducerConfig.BUFFER_MEMORY_CONFIG, 33554432); - properties.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer"); - properties.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer"); - return properties; + config.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName()); + config.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName()); + return config; } } From 3c6cbd3c008a266f2835c991585195916a6dff12 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Sun, 17 Nov 2019 15:17:43 +0800 Subject: [PATCH 120/476] *) complete config --- .../wrapper/config/KafkaConfigManager.java | 44 +++++++++++++++---- .../consumer/KafkaConsumerWrapper.java | 5 ++- kafka/src/main/resources/kafka.properties | 1 + .../config/KafkaConfigManagerTest.java | 18 ++++++++ .../consumer/KafkaConsumerWrapperTest.java | 16 +++++++ 5 files changed, 74 insertions(+), 10 deletions(-) create mode 100644 kafka/src/main/resources/kafka.properties create mode 100644 kafka/src/test/java/com/github/kuangcp/wrapper/config/KafkaConfigManagerTest.java create mode 100644 kafka/src/test/java/com/github/kuangcp/wrapper/consumer/KafkaConsumerWrapperTest.java diff --git a/kafka/src/main/java/com/github/kuangcp/wrapper/config/KafkaConfigManager.java b/kafka/src/main/java/com/github/kuangcp/wrapper/config/KafkaConfigManager.java index 2678613c..8d169e6a 100644 --- a/kafka/src/main/java/com/github/kuangcp/wrapper/config/KafkaConfigManager.java +++ b/kafka/src/main/java/com/github/kuangcp/wrapper/config/KafkaConfigManager.java @@ -1,9 +1,11 @@ package com.github.kuangcp.wrapper.config; -import static com.github.kuangcp.hi.Constants.KAFKA_SERVER; - +import java.io.IOException; +import java.io.InputStream; +import java.util.Optional; import java.util.Properties; import java.util.UUID; +import lombok.extern.slf4j.Slf4j; import org.apache.kafka.clients.consumer.ConsumerConfig; import org.apache.kafka.clients.producer.ProducerConfig; import org.apache.kafka.common.serialization.StringDeserializer; @@ -12,11 +14,31 @@ /** * @author https://github.com/kuangcp on 2019-11-13 09:37 */ +@Slf4j public class KafkaConfigManager { - public static Properties getConsumerConfig() { + private static Properties load; + + static { + InputStream is = KafkaConfigManager.class.getResourceAsStream("/kafka.properties"); + try { + load = new Properties(); + load.load(is); + } catch (IOException e) { + log.error("", e); + } + } + + public static Optional getConsumerConfig() { + Optional serverOpt = Optional.ofNullable(load) + .map(v -> v.getProperty(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG)); + if (!serverOpt.isPresent()) { + return Optional.empty(); + } + + load.getProperty(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG); Properties config = new Properties(); - config.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, KAFKA_SERVER); + config.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, serverOpt.get()); config.put(ConsumerConfig.GROUP_ID_CONFIG, "TestGroup-" + UUID.randomUUID().toString()); config.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "true"); config.put(ConsumerConfig.AUTO_COMMIT_INTERVAL_MS_CONFIG, "1000"); @@ -24,12 +46,18 @@ public static Properties getConsumerConfig() { config.put(ConsumerConfig.SESSION_TIMEOUT_MS_CONFIG, "30000"); config.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName()); config.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName()); - return config; + return Optional.of(config); } - public static Properties getProducerConfig() { + public static Optional getProducerConfig() { + Optional serverOpt = Optional.ofNullable(load) + .map(v -> v.getProperty(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG)); + if (!serverOpt.isPresent()) { + return Optional.empty(); + } + Properties config = new Properties(); - config.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, KAFKA_SERVER); + config.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, serverOpt.get()); config.put(ProducerConfig.ACKS_CONFIG, "all"); config.put(ProducerConfig.RETRIES_CONFIG, 0); config.put(ProducerConfig.BATCH_SIZE_CONFIG, 16384); @@ -38,6 +66,6 @@ public static Properties getProducerConfig() { config.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName()); config.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName()); - return config; + return Optional.of(config); } } diff --git a/kafka/src/main/java/com/github/kuangcp/wrapper/consumer/KafkaConsumerWrapper.java b/kafka/src/main/java/com/github/kuangcp/wrapper/consumer/KafkaConsumerWrapper.java index d13e42da..5155a677 100644 --- a/kafka/src/main/java/com/github/kuangcp/wrapper/consumer/KafkaConsumerWrapper.java +++ b/kafka/src/main/java/com/github/kuangcp/wrapper/consumer/KafkaConsumerWrapper.java @@ -3,7 +3,6 @@ import com.github.kuangcp.wrapper.config.KafkaConfigManager; import java.time.Duration; import java.util.Collection; -import java.util.Collections; import java.util.Properties; import lombok.extern.slf4j.Slf4j; import org.apache.kafka.clients.consumer.ConsumerRecord; @@ -16,7 +15,8 @@ @Slf4j public class KafkaConsumerWrapper { - private static Properties properties = KafkaConfigManager.getConsumerConfig(); + private static Properties properties = KafkaConfigManager.getConsumerConfig() + .orElseThrow(() -> new RuntimeException("")); static void consumer(Duration duration, Collection topics) { KafkaConsumer kafkaConsumer = new KafkaConsumer<>(properties); @@ -25,6 +25,7 @@ static void consumer(Duration duration, Collection topics) { ConsumerRecords records = kafkaConsumer.poll(duration); for (ConsumerRecord record : records) { log.info("offset = {}, value = {}", record.offset(), record.value()); + } } } diff --git a/kafka/src/main/resources/kafka.properties b/kafka/src/main/resources/kafka.properties new file mode 100644 index 00000000..d32e57c5 --- /dev/null +++ b/kafka/src/main/resources/kafka.properties @@ -0,0 +1 @@ +bootstrap.servers=127.0.0.1:9092 \ No newline at end of file diff --git a/kafka/src/test/java/com/github/kuangcp/wrapper/config/KafkaConfigManagerTest.java b/kafka/src/test/java/com/github/kuangcp/wrapper/config/KafkaConfigManagerTest.java new file mode 100644 index 00000000..5bebbd06 --- /dev/null +++ b/kafka/src/test/java/com/github/kuangcp/wrapper/config/KafkaConfigManagerTest.java @@ -0,0 +1,18 @@ +package com.github.kuangcp.wrapper.config; + +import java.util.Optional; +import java.util.Properties; +import org.junit.Test; + +/** + * @author https://github.com/kuangcp on 2019-11-17 14:45 + */ +public class KafkaConfigManagerTest { + + @Test + public void testConsumer(){ + Optional propOpt = KafkaConfigManager.getConsumerConfig(); + + propOpt.ifPresent(System.out::println); + } +} diff --git a/kafka/src/test/java/com/github/kuangcp/wrapper/consumer/KafkaConsumerWrapperTest.java b/kafka/src/test/java/com/github/kuangcp/wrapper/consumer/KafkaConsumerWrapperTest.java new file mode 100644 index 00000000..23c2e1fa --- /dev/null +++ b/kafka/src/test/java/com/github/kuangcp/wrapper/consumer/KafkaConsumerWrapperTest.java @@ -0,0 +1,16 @@ +package com.github.kuangcp.wrapper.consumer; + +import java.time.Duration; +import java.util.Collections; +import org.junit.Test; + +/** + * @author https://github.com/kuangcp on 2019-11-17 14:54 + */ +public class KafkaConsumerWrapperTest { + + @Test + public void testConsumer(){ + KafkaConsumerWrapper.consumer(Duration.ofMillis(1000), Collections.singleton("Hi")); + } +} From e0ec869e584476ebfe2349ca183a687b84d79f3b Mon Sep 17 00:00:00 2001 From: kcp Date: Tue, 19 Nov 2019 21:44:52 +0800 Subject: [PATCH 121/476] x) optimized --- .../stackapp/StackReplaceRecurrence.java | 38 +++++++------------ .../stackapp/StackReplaceRecurrenceTest.java | 31 +++++++++++++++ 2 files changed, 44 insertions(+), 25 deletions(-) create mode 100644 algorithms/src/test/java/com/github/kuangcp/strcuture/stackapp/StackReplaceRecurrenceTest.java diff --git a/algorithms/src/main/java/com/github/kuangcp/strcuture/stackapp/StackReplaceRecurrence.java b/algorithms/src/main/java/com/github/kuangcp/strcuture/stackapp/StackReplaceRecurrence.java index 36f0b427..2a601d69 100644 --- a/algorithms/src/main/java/com/github/kuangcp/strcuture/stackapp/StackReplaceRecurrence.java +++ b/algorithms/src/main/java/com/github/kuangcp/strcuture/stackapp/StackReplaceRecurrence.java @@ -3,47 +3,35 @@ import com.github.kuangcp.strcuture.stack.MythLinkedStack; -import java.util.Scanner; - /** - * Created by https://github.com/kuangcp on 17-8-22 下午3:01 - * 用栈来消除递归,就是模拟递归中系统给他的栈 - * 递归的每一次退出函数, 就是出栈 + * Created by https://github.com/kuangcp on 17-8-22 下午3:01 用栈来消除递归,就是模拟递归中系统给他的栈 递归的每一次退出函数, 就是出栈 * 每一次进入函数 ,就是进栈 */ public class StackReplaceRecurrence { - public static void main(String[] args) { - MythLinkedStack Q = new MythLinkedStack<>(); - Q.push(1); - Scanner sc = new Scanner(System.in); - System.out.println("请输入函数自变量的值"); - int m = sc.nextInt(); - int n = m; + public static double calculateByStack(int num) { + MythLinkedStack stack = new MythLinkedStack<>(); + stack.push(1); + int temp = num; double sum = 1; - //先把实例化语句写出来,再选中用Ctrl+shift+o 快速导入包,注意不能跨项目 - while (Q.peek() != null) { - if (n == 0) { - sum = sum * Q.pop(); + while (stack.peek() != null) { + if (temp == 0) { + sum = sum * stack.pop(); } else { - Q.push(n); - n /= 2; + stack.push(temp); + temp /= 2; } } - System.out.println("函数的值是:" + sum); - - System.out.println("函数的值是:" + f(m));//终于知道错误了,我擦,上面的代码已经将n改变成了0了 - sc.close(); - + return sum; } - private static double f(int n) { + public static double calculateByRecursive(int n) { double m; if (n == 0) { return 1; } else { - m = n * f(n / 2); + m = n * calculateByRecursive(n / 2); } return m; } diff --git a/algorithms/src/test/java/com/github/kuangcp/strcuture/stackapp/StackReplaceRecurrenceTest.java b/algorithms/src/test/java/com/github/kuangcp/strcuture/stackapp/StackReplaceRecurrenceTest.java new file mode 100644 index 00000000..2a8a81ae --- /dev/null +++ b/algorithms/src/test/java/com/github/kuangcp/strcuture/stackapp/StackReplaceRecurrenceTest.java @@ -0,0 +1,31 @@ +package com.github.kuangcp.strcuture.stackapp; + +import java.util.HashMap; +import java.util.Map.Entry; +import org.junit.Test; + +import static org.hamcrest.Matchers.equalTo; +import static org.junit.Assert.assertThat; + +/** + * @author https://github.com/kuangcp on 2019-11-19 21:38 + */ + + +public class StackReplaceRecurrenceTest { + + @Test + public void testCalculate() throws Exception { + HashMap argAndResult = new HashMap<>(); + argAndResult.put(4, 8.0); + argAndResult.put(30, 9450.0); + + for (Entry entry : argAndResult.entrySet()) { + double result = StackReplaceRecurrence.calculateByStack(entry.getKey()); + assertThat(result, equalTo(entry.getValue())); + + double recursive = StackReplaceRecurrence.calculateByRecursive(entry.getKey()); + assertThat(recursive, equalTo(entry.getValue())); + } + } +} From 3567a8b5cbb0066fee5e3d145eaf331fd9dc5ee7 Mon Sep 17 00:00:00 2001 From: kcp Date: Fri, 22 Nov 2019 21:25:03 +0800 Subject: [PATCH 122/476] +) complete consumer --- .../consumer/KafkaConsumerWrapper.java | 29 +++++++++++++++++-- .../wrapper/consumer/MessageExecutor.java | 6 ++-- .../consumer/SimpleTopicMessageExecutor.java | 8 +++++ .../kuangcp/wrapper/consumer/SingleTopic.java | 9 ++++++ .../consumer/SingleTopicMessageExecutor.java | 5 ++-- .../kuangcp/wrapper/consumer/HiExecutor.java | 17 +++++++++++ .../consumer/KafkaConsumerWrapperTest.java | 6 ++-- 7 files changed, 69 insertions(+), 11 deletions(-) create mode 100644 kafka/src/main/java/com/github/kuangcp/wrapper/consumer/SimpleTopicMessageExecutor.java create mode 100644 kafka/src/main/java/com/github/kuangcp/wrapper/consumer/SingleTopic.java create mode 100644 kafka/src/test/java/com/github/kuangcp/wrapper/consumer/HiExecutor.java diff --git a/kafka/src/main/java/com/github/kuangcp/wrapper/consumer/KafkaConsumerWrapper.java b/kafka/src/main/java/com/github/kuangcp/wrapper/consumer/KafkaConsumerWrapper.java index 5155a677..d42a2f0b 100644 --- a/kafka/src/main/java/com/github/kuangcp/wrapper/consumer/KafkaConsumerWrapper.java +++ b/kafka/src/main/java/com/github/kuangcp/wrapper/consumer/KafkaConsumerWrapper.java @@ -3,11 +3,19 @@ import com.github.kuangcp.wrapper.config.KafkaConfigManager; import java.time.Duration; import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Objects; import java.util.Properties; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.function.Function; +import java.util.stream.Collectors; import lombok.extern.slf4j.Slf4j; import org.apache.kafka.clients.consumer.ConsumerRecord; import org.apache.kafka.clients.consumer.ConsumerRecords; import org.apache.kafka.clients.consumer.KafkaConsumer; +import org.apache.kafka.common.utils.CollectionUtils; /** * @author https://github.com/kuangcp on 2019-11-13 09:35 @@ -15,17 +23,32 @@ @Slf4j public class KafkaConsumerWrapper { + private static ExecutorService pool = Executors + .newFixedThreadPool(Runtime.getRuntime().availableProcessors()); + private static Properties properties = KafkaConfigManager.getConsumerConfig() .orElseThrow(() -> new RuntimeException("")); - static void consumer(Duration duration, Collection topics) { + static void consumer(Duration duration, List executors) { + if (Objects.isNull(duration) || Objects.isNull(executors) || executors.isEmpty()) { + log.warn("consumer param invalid"); + return; + } + + Map executorMap = executors.stream() + .collect(Collectors.toMap(SimpleTopicMessageExecutor::getTopic, + Function.identity(), (front, current) -> current)); + KafkaConsumer kafkaConsumer = new KafkaConsumer<>(properties); - kafkaConsumer.subscribe(topics); + kafkaConsumer.subscribe(executorMap.keySet()); while (true) { ConsumerRecords records = kafkaConsumer.poll(duration); for (ConsumerRecord record : records) { - log.info("offset = {}, value = {}", record.offset(), record.value()); + SimpleTopicMessageExecutor executor = executorMap.get(record.topic()); + if (Objects.nonNull(executor)) { + pool.submit(() -> executor.execute(record.value())); + } } } } diff --git a/kafka/src/main/java/com/github/kuangcp/wrapper/consumer/MessageExecutor.java b/kafka/src/main/java/com/github/kuangcp/wrapper/consumer/MessageExecutor.java index 146f9f17..9b4076c1 100644 --- a/kafka/src/main/java/com/github/kuangcp/wrapper/consumer/MessageExecutor.java +++ b/kafka/src/main/java/com/github/kuangcp/wrapper/consumer/MessageExecutor.java @@ -1,14 +1,12 @@ package com.github.kuangcp.wrapper.consumer; -import com.github.kuangcp.wrapper.Message; - /** * @author https://github.com/kuangcp on 2019-11-13 09:47 */ -public interface MessageExecutor { +public interface MessageExecutor { /** * 处理消息 */ - void execute(Message message); + void execute(T message); } diff --git a/kafka/src/main/java/com/github/kuangcp/wrapper/consumer/SimpleTopicMessageExecutor.java b/kafka/src/main/java/com/github/kuangcp/wrapper/consumer/SimpleTopicMessageExecutor.java new file mode 100644 index 00000000..c5e1c804 --- /dev/null +++ b/kafka/src/main/java/com/github/kuangcp/wrapper/consumer/SimpleTopicMessageExecutor.java @@ -0,0 +1,8 @@ +package com.github.kuangcp.wrapper.consumer; + +/** + * @author https://github.com/kuangcp on 2019-11-22 21:06 + */ +public interface SimpleTopicMessageExecutor extends MessageExecutor, SingleTopic { + +} diff --git a/kafka/src/main/java/com/github/kuangcp/wrapper/consumer/SingleTopic.java b/kafka/src/main/java/com/github/kuangcp/wrapper/consumer/SingleTopic.java new file mode 100644 index 00000000..d4630733 --- /dev/null +++ b/kafka/src/main/java/com/github/kuangcp/wrapper/consumer/SingleTopic.java @@ -0,0 +1,9 @@ +package com.github.kuangcp.wrapper.consumer; + +/** + * @author https://github.com/kuangcp on 2019-11-22 21:07 + */ +public interface SingleTopic { + + String getTopic(); +} diff --git a/kafka/src/main/java/com/github/kuangcp/wrapper/consumer/SingleTopicMessageExecutor.java b/kafka/src/main/java/com/github/kuangcp/wrapper/consumer/SingleTopicMessageExecutor.java index fde3ac24..f09d6309 100644 --- a/kafka/src/main/java/com/github/kuangcp/wrapper/consumer/SingleTopicMessageExecutor.java +++ b/kafka/src/main/java/com/github/kuangcp/wrapper/consumer/SingleTopicMessageExecutor.java @@ -1,9 +1,10 @@ package com.github.kuangcp.wrapper.consumer; +import com.github.kuangcp.wrapper.Message; + /** * @author https://github.com/kuangcp on 2019-11-13 09:49 */ -public interface SingleTopicMessageExecutor extends MessageExecutor { +public interface SingleTopicMessageExecutor extends MessageExecutor>, SingleTopic { - String getTopic(); } diff --git a/kafka/src/test/java/com/github/kuangcp/wrapper/consumer/HiExecutor.java b/kafka/src/test/java/com/github/kuangcp/wrapper/consumer/HiExecutor.java new file mode 100644 index 00000000..fdf37b9e --- /dev/null +++ b/kafka/src/test/java/com/github/kuangcp/wrapper/consumer/HiExecutor.java @@ -0,0 +1,17 @@ +package com.github.kuangcp.wrapper.consumer; + +/** + * @author https://github.com/kuangcp on 2019-11-22 21:16 + */ +public class HiExecutor implements SimpleTopicMessageExecutor { + + @Override + public void execute(String message) { + System.out.println(message); + } + + @Override + public String getTopic() { + return "Hi"; + } +} diff --git a/kafka/src/test/java/com/github/kuangcp/wrapper/consumer/KafkaConsumerWrapperTest.java b/kafka/src/test/java/com/github/kuangcp/wrapper/consumer/KafkaConsumerWrapperTest.java index 23c2e1fa..cc3d9576 100644 --- a/kafka/src/test/java/com/github/kuangcp/wrapper/consumer/KafkaConsumerWrapperTest.java +++ b/kafka/src/test/java/com/github/kuangcp/wrapper/consumer/KafkaConsumerWrapperTest.java @@ -10,7 +10,9 @@ public class KafkaConsumerWrapperTest { @Test - public void testConsumer(){ - KafkaConsumerWrapper.consumer(Duration.ofMillis(1000), Collections.singleton("Hi")); + public void testConsumerWithSimpleExecutor() { + HiExecutor executor = new HiExecutor(); + + KafkaConsumerWrapper.consumer(Duration.ofMillis(1000), Collections.singletonList(executor)); } } From 2790b6fb951b074d94e4ef9cdb97a8f09f6e0bdd Mon Sep 17 00:00:00 2001 From: kcp Date: Sat, 23 Nov 2019 10:22:25 +0800 Subject: [PATCH 123/476] +) cache --- .../kuangcp/wrapper/KafkaProducerWrapper.java | 28 +++++++++++++++++++ .../consumer/KafkaConsumerWrapper.java | 24 ++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/kafka/src/main/java/com/github/kuangcp/wrapper/KafkaProducerWrapper.java b/kafka/src/main/java/com/github/kuangcp/wrapper/KafkaProducerWrapper.java index f7550b6c..13ad92c6 100644 --- a/kafka/src/main/java/com/github/kuangcp/wrapper/KafkaProducerWrapper.java +++ b/kafka/src/main/java/com/github/kuangcp/wrapper/KafkaProducerWrapper.java @@ -1,8 +1,36 @@ package com.github.kuangcp.wrapper; +import com.github.kuangcp.wrapper.config.KafkaConfigManager; +import java.util.Objects; +import java.util.Properties; +import org.apache.kafka.clients.producer.KafkaProducer; +import org.apache.kafka.clients.producer.Producer; +import org.apache.kafka.clients.producer.ProducerRecord; + /** * @author https://github.com/kuangcp on 2019-11-13 09:36 */ public class KafkaProducerWrapper { + private static Properties properties = KafkaConfigManager.getProducerConfig() + .orElseThrow(() -> new RuntimeException("")); + + public static void send(String topic, Message message) { + if (Objects.isNull(topic) || topic.isEmpty() || Objects.isNull(message)) { + return; + } + + Producer> producer = new KafkaProducer<>(properties); + producer.send(new ProducerRecord<>(topic, message)); + } + + public static void send(String topic, T content) { + Producer producer = new KafkaProducer<>(properties); + producer.send(new ProducerRecord<>(topic, content)); + } + + public static void sendPlainText(String topic, String message) { + Producer producer = new KafkaProducer<>(properties); + producer.send(new ProducerRecord<>(topic, message)); + } } diff --git a/kafka/src/main/java/com/github/kuangcp/wrapper/consumer/KafkaConsumerWrapper.java b/kafka/src/main/java/com/github/kuangcp/wrapper/consumer/KafkaConsumerWrapper.java index d42a2f0b..e20294cb 100644 --- a/kafka/src/main/java/com/github/kuangcp/wrapper/consumer/KafkaConsumerWrapper.java +++ b/kafka/src/main/java/com/github/kuangcp/wrapper/consumer/KafkaConsumerWrapper.java @@ -52,4 +52,28 @@ static void consumer(Duration duration, List exe } } } + + static void consumers(Duration duration, List> executors) { + if (Objects.isNull(duration) || Objects.isNull(executors) || executors.isEmpty()) { + log.warn("consumer param invalid"); + return; + } + + Map> executorMap = executors.stream() + .collect(Collectors.toMap(SingleTopicMessageExecutor::getTopic, + Function.identity(), (front, current) -> current)); + + KafkaConsumer kafkaConsumer = new KafkaConsumer<>(properties); + kafkaConsumer.subscribe(executorMap.keySet()); + while (true) { + ConsumerRecords records = kafkaConsumer.poll(duration); + for (ConsumerRecord record : records) { + SingleTopicMessageExecutor executor = executorMap.get(record.topic()); + + if (Objects.nonNull(executor)) { + pool.submit(() -> executor.execute(record.value())); + } + } + } + } } From bbd13f378ac6d2a68181bd8854954c9e3d10aae1 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Sat, 23 Nov 2019 23:55:21 +0800 Subject: [PATCH 124/476] +) complete basic test --- .../kuangcp/wrapper/KafkaProducerWrapper.java | 65 +++++++++- .../com/github/kuangcp/wrapper/Message.java | 5 +- .../wrapper/config/KafkaConfigManager.java | 9 +- ...cutor.java => GeneralMessageExecutor.java} | 3 +- .../consumer/KafkaConsumerWrapper.java | 117 ++++++++++++++---- .../{SingleTopic.java => MessageTopic.java} | 2 +- ...ecutor.java => SimpleMessageExecutor.java} | 2 +- kafka/src/main/resources/kafka.properties | 3 +- .../wrapper/KafkaProducerWrapperTest.java | 25 ++++ .../kuangcp/wrapper/consumer/HiExecutor.java | 6 +- .../consumer/KafkaConsumerWrapperTest.java | 7 ++ .../consumer/LoginMessageExecutor.java | 28 +++++ .../github/kuangcp/wrapper/domain/Topics.java | 10 ++ .../github/kuangcp/wrapper/domain/User.java | 27 ++++ 14 files changed, 272 insertions(+), 37 deletions(-) rename kafka/src/main/java/com/github/kuangcp/wrapper/consumer/{SingleTopicMessageExecutor.java => GeneralMessageExecutor.java} (55%) rename kafka/src/main/java/com/github/kuangcp/wrapper/consumer/{SingleTopic.java => MessageTopic.java} (80%) rename kafka/src/main/java/com/github/kuangcp/wrapper/consumer/{SimpleTopicMessageExecutor.java => SimpleMessageExecutor.java} (55%) create mode 100644 kafka/src/test/java/com/github/kuangcp/wrapper/KafkaProducerWrapperTest.java create mode 100644 kafka/src/test/java/com/github/kuangcp/wrapper/consumer/LoginMessageExecutor.java create mode 100644 kafka/src/test/java/com/github/kuangcp/wrapper/domain/Topics.java create mode 100644 kafka/src/test/java/com/github/kuangcp/wrapper/domain/User.java diff --git a/kafka/src/main/java/com/github/kuangcp/wrapper/KafkaProducerWrapper.java b/kafka/src/main/java/com/github/kuangcp/wrapper/KafkaProducerWrapper.java index 13ad92c6..0a3d634c 100644 --- a/kafka/src/main/java/com/github/kuangcp/wrapper/KafkaProducerWrapper.java +++ b/kafka/src/main/java/com/github/kuangcp/wrapper/KafkaProducerWrapper.java @@ -1,8 +1,11 @@ package com.github.kuangcp.wrapper; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; import com.github.kuangcp.wrapper.config.KafkaConfigManager; import java.util.Objects; import java.util.Properties; +import lombok.extern.slf4j.Slf4j; import org.apache.kafka.clients.producer.KafkaProducer; import org.apache.kafka.clients.producer.Producer; import org.apache.kafka.clients.producer.ProducerRecord; @@ -10,27 +13,77 @@ /** * @author https://github.com/kuangcp on 2019-11-13 09:36 */ +@Slf4j public class KafkaProducerWrapper { + private static ObjectMapper objectMapper = new ObjectMapper(); + private static Properties properties = KafkaConfigManager.getProducerConfig() - .orElseThrow(() -> new RuntimeException("")); + .orElseThrow(() -> new RuntimeException("加载Kafka生产者配置出错")); + + private static Producer producer = new KafkaProducer<>(properties); + static { + Runtime.getRuntime().addShutdownHook(new Thread(KafkaProducerWrapper::shutdown)); + } + + /** + * 发送消息 + * + * @param topic 主题 + * @param message 消息 + * @param 内容类型 + * @see Message + */ public static void send(String topic, Message message) { if (Objects.isNull(topic) || topic.isEmpty() || Objects.isNull(message)) { return; } - Producer> producer = new KafkaProducer<>(properties); - producer.send(new ProducerRecord<>(topic, message)); + try { + String messageStr = objectMapper.writeValueAsString(message); + producer.send(new ProducerRecord<>(topic, messageStr)); + } catch (JsonProcessingException e) { + log.error("", e); + } } + /** + * 发送消息 + * + * @param topic 主题 + * @param content 消息内容 + * @param 内容类型 + * @see Message + */ public static void send(String topic, T content) { - Producer producer = new KafkaProducer<>(properties); - producer.send(new ProducerRecord<>(topic, content)); + Message message = new Message<>(content); + try { + String messageStr = objectMapper.writeValueAsString(message); + producer.send(new ProducerRecord<>(topic, messageStr)); + } catch (JsonProcessingException e) { + log.error("", e); + } } + /** + * 发送字符串消息 + * + * @param topic 主题 + * @param message 消息 + */ public static void sendPlainText(String topic, String message) { - Producer producer = new KafkaProducer<>(properties); producer.send(new ProducerRecord<>(topic, message)); } + + /** + * 生产者资源回收 未发送的消息进行发送 + */ + private static void shutdown() { + try { + producer.close(); + } catch (Exception e) { + log.error("shutdown messageProducer error"); + } + } } diff --git a/kafka/src/main/java/com/github/kuangcp/wrapper/Message.java b/kafka/src/main/java/com/github/kuangcp/wrapper/Message.java index 92d80233..f1e06a31 100644 --- a/kafka/src/main/java/com/github/kuangcp/wrapper/Message.java +++ b/kafka/src/main/java/com/github/kuangcp/wrapper/Message.java @@ -1,5 +1,6 @@ package com.github.kuangcp.wrapper; +import java.io.Serializable; import java.util.Date; import java.util.UUID; import lombok.Data; @@ -10,7 +11,9 @@ */ @Data @NoArgsConstructor -public class Message { +public class Message implements Serializable { + + private static final long serialVersionUID = 0L; /** * 消息Id diff --git a/kafka/src/main/java/com/github/kuangcp/wrapper/config/KafkaConfigManager.java b/kafka/src/main/java/com/github/kuangcp/wrapper/config/KafkaConfigManager.java index 8d169e6a..466a6fd9 100644 --- a/kafka/src/main/java/com/github/kuangcp/wrapper/config/KafkaConfigManager.java +++ b/kafka/src/main/java/com/github/kuangcp/wrapper/config/KafkaConfigManager.java @@ -36,10 +36,15 @@ public static Optional getConsumerConfig() { return Optional.empty(); } - load.getProperty(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG); + String groupId; + Optional groupOpt = Optional.ofNullable(load) + .map(v -> v.getProperty(ConsumerConfig.GROUP_ID_CONFIG)); + groupId = groupOpt.orElseGet(() -> "Group-" + UUID.randomUUID().toString()); + Properties config = new Properties(); config.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, serverOpt.get()); - config.put(ConsumerConfig.GROUP_ID_CONFIG, "TestGroup-" + UUID.randomUUID().toString()); + config.put(ConsumerConfig.GROUP_ID_CONFIG, groupId); + config.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "true"); config.put(ConsumerConfig.AUTO_COMMIT_INTERVAL_MS_CONFIG, "1000"); config.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest"); diff --git a/kafka/src/main/java/com/github/kuangcp/wrapper/consumer/SingleTopicMessageExecutor.java b/kafka/src/main/java/com/github/kuangcp/wrapper/consumer/GeneralMessageExecutor.java similarity index 55% rename from kafka/src/main/java/com/github/kuangcp/wrapper/consumer/SingleTopicMessageExecutor.java rename to kafka/src/main/java/com/github/kuangcp/wrapper/consumer/GeneralMessageExecutor.java index f09d6309..d35cd08a 100644 --- a/kafka/src/main/java/com/github/kuangcp/wrapper/consumer/SingleTopicMessageExecutor.java +++ b/kafka/src/main/java/com/github/kuangcp/wrapper/consumer/GeneralMessageExecutor.java @@ -5,6 +5,7 @@ /** * @author https://github.com/kuangcp on 2019-11-13 09:49 */ -public interface SingleTopicMessageExecutor extends MessageExecutor>, SingleTopic { +public interface GeneralMessageExecutor extends MessageTopic, MessageExecutor> { + Class getContentClass(); } diff --git a/kafka/src/main/java/com/github/kuangcp/wrapper/consumer/KafkaConsumerWrapper.java b/kafka/src/main/java/com/github/kuangcp/wrapper/consumer/KafkaConsumerWrapper.java index e20294cb..292ee6fd 100644 --- a/kafka/src/main/java/com/github/kuangcp/wrapper/consumer/KafkaConsumerWrapper.java +++ b/kafka/src/main/java/com/github/kuangcp/wrapper/consumer/KafkaConsumerWrapper.java @@ -1,8 +1,10 @@ package com.github.kuangcp.wrapper.consumer; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.module.SimpleModule; +import com.github.kuangcp.wrapper.Message; import com.github.kuangcp.wrapper.config.KafkaConfigManager; import java.time.Duration; -import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Objects; @@ -15,65 +17,136 @@ import org.apache.kafka.clients.consumer.ConsumerRecord; import org.apache.kafka.clients.consumer.ConsumerRecords; import org.apache.kafka.clients.consumer.KafkaConsumer; -import org.apache.kafka.common.utils.CollectionUtils; /** * @author https://github.com/kuangcp on 2019-11-13 09:35 */ +@SuppressWarnings("unchecked") @Slf4j public class KafkaConsumerWrapper { + private static ObjectMapper objectMapper = new ObjectMapper(); + + private static volatile boolean stop = false; + private static ExecutorService pool = Executors .newFixedThreadPool(Runtime.getRuntime().availableProcessors()); private static Properties properties = KafkaConfigManager.getConsumerConfig() - .orElseThrow(() -> new RuntimeException("")); + .orElseThrow(() -> new RuntimeException("加载Kafka消费者配置出错")); + + private static KafkaConsumer consumer; + + static { + SimpleModule module = new SimpleModule(); + + Runtime.getRuntime().addShutdownHook(new Thread(KafkaConsumerWrapper::shutdown)); + } - static void consumer(Duration duration, List executors) { + // TODO 两个线程分摊 Topics? + + + /** + * 消费Topic + * + * @param duration 拉取间隔 + * @param executors 执行器 + * @param executor + */ + static & MessageTopic> void consumerPlainText( + Duration duration, List executors) { if (Objects.isNull(duration) || Objects.isNull(executors) || executors.isEmpty()) { log.warn("consumer param invalid"); return; } - Map executorMap = executors.stream() - .collect(Collectors.toMap(SimpleTopicMessageExecutor::getTopic, - Function.identity(), (front, current) -> current)); + if (Objects.nonNull(consumer)) { + log.warn("consumer has started"); + return; + } + + Map executorMap = executors.stream() + .collect(Collectors.toMap(E::getTopic, Function.identity(), (front, current) -> current)); - KafkaConsumer kafkaConsumer = new KafkaConsumer<>(properties); - kafkaConsumer.subscribe(executorMap.keySet()); - while (true) { - ConsumerRecords records = kafkaConsumer.poll(duration); + consumer = new KafkaConsumer<>(properties); + consumer.subscribe(executorMap.keySet()); + while (!stop) { + ConsumerRecords records = consumer.poll(duration); for (ConsumerRecord record : records) { - SimpleTopicMessageExecutor executor = executorMap.get(record.topic()); + E executor = executorMap.get(record.topic()); if (Objects.nonNull(executor)) { + pool.submit(() -> executor.execute(record.value())); } } } } - static void consumers(Duration duration, List> executors) { + /** + * 消费Topic + * + * @param duration 拉取间隔 + * @param executors 执行器 + * @param Message content 类型 + */ + static void consumer(Duration duration, List> executors) { if (Objects.isNull(duration) || Objects.isNull(executors) || executors.isEmpty()) { log.warn("consumer param invalid"); return; } - Map> executorMap = executors.stream() - .collect(Collectors.toMap(SingleTopicMessageExecutor::getTopic, - Function.identity(), (front, current) -> current)); + if (Objects.nonNull(consumer)) { + log.warn("consumer has started"); + return; + } + + Map> executorMap = executors.stream() + .collect(Collectors.toMap(GeneralMessageExecutor::getTopic, Function.identity(), + (front, current) -> current)); - KafkaConsumer kafkaConsumer = new KafkaConsumer<>(properties); - kafkaConsumer.subscribe(executorMap.keySet()); - while (true) { - ConsumerRecords records = kafkaConsumer.poll(duration); + consumer = new KafkaConsumer<>(properties); + consumer.subscribe(executorMap.keySet()); + while (!stop) { + ConsumerRecords records = consumer.poll(duration); for (ConsumerRecord record : records) { - SingleTopicMessageExecutor executor = executorMap.get(record.topic()); + GeneralMessageExecutor executor = executorMap.get(record.topic()); if (Objects.nonNull(executor)) { - pool.submit(() -> executor.execute(record.value())); + pool.submit(() -> { + try { + Message message = objectMapper.readValue(record.value(), Message.class); + + // 因为以上反序列化无法获取泛型的类型,所以无法正确的设置 content 对象,根本原因是Java的泛型擦除 + // 解决方案是先将content再序列化 最后指定类型反序列化 + Class contentClass = executor.getContentClass(); + String value = objectMapper.writeValueAsString(message.getContent()); + T content = objectMapper.readValue(value, contentClass); + message.setContent(content); + + executor.execute(message); + } catch (Exception e) { + log.error("", e); + } + }); + } } } } + + /** + * 消费者资源回收 线程池关闭 + */ + private static void shutdown() { + stop = true; + try { + if (Objects.nonNull(consumer)) { + consumer.close(); + } + pool.shutdown(); + } catch (Exception e) { + log.error("shutdown messageProducer error"); + } + } } diff --git a/kafka/src/main/java/com/github/kuangcp/wrapper/consumer/SingleTopic.java b/kafka/src/main/java/com/github/kuangcp/wrapper/consumer/MessageTopic.java similarity index 80% rename from kafka/src/main/java/com/github/kuangcp/wrapper/consumer/SingleTopic.java rename to kafka/src/main/java/com/github/kuangcp/wrapper/consumer/MessageTopic.java index d4630733..63d0f3ee 100644 --- a/kafka/src/main/java/com/github/kuangcp/wrapper/consumer/SingleTopic.java +++ b/kafka/src/main/java/com/github/kuangcp/wrapper/consumer/MessageTopic.java @@ -3,7 +3,7 @@ /** * @author https://github.com/kuangcp on 2019-11-22 21:07 */ -public interface SingleTopic { +public interface MessageTopic { String getTopic(); } diff --git a/kafka/src/main/java/com/github/kuangcp/wrapper/consumer/SimpleTopicMessageExecutor.java b/kafka/src/main/java/com/github/kuangcp/wrapper/consumer/SimpleMessageExecutor.java similarity index 55% rename from kafka/src/main/java/com/github/kuangcp/wrapper/consumer/SimpleTopicMessageExecutor.java rename to kafka/src/main/java/com/github/kuangcp/wrapper/consumer/SimpleMessageExecutor.java index c5e1c804..8d9c3cc5 100644 --- a/kafka/src/main/java/com/github/kuangcp/wrapper/consumer/SimpleTopicMessageExecutor.java +++ b/kafka/src/main/java/com/github/kuangcp/wrapper/consumer/SimpleMessageExecutor.java @@ -3,6 +3,6 @@ /** * @author https://github.com/kuangcp on 2019-11-22 21:06 */ -public interface SimpleTopicMessageExecutor extends MessageExecutor, SingleTopic { +public interface SimpleMessageExecutor extends MessageTopic, MessageExecutor { } diff --git a/kafka/src/main/resources/kafka.properties b/kafka/src/main/resources/kafka.properties index d32e57c5..800230f9 100644 --- a/kafka/src/main/resources/kafka.properties +++ b/kafka/src/main/resources/kafka.properties @@ -1 +1,2 @@ -bootstrap.servers=127.0.0.1:9092 \ No newline at end of file +bootstrap.servers=127.0.0.1:9092 +group.id=test-1 \ No newline at end of file diff --git a/kafka/src/test/java/com/github/kuangcp/wrapper/KafkaProducerWrapperTest.java b/kafka/src/test/java/com/github/kuangcp/wrapper/KafkaProducerWrapperTest.java new file mode 100644 index 00000000..03612ee9 --- /dev/null +++ b/kafka/src/test/java/com/github/kuangcp/wrapper/KafkaProducerWrapperTest.java @@ -0,0 +1,25 @@ +package com.github.kuangcp.wrapper; + +import com.github.kuangcp.wrapper.domain.Topics; +import com.github.kuangcp.wrapper.domain.User; +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; + +/** + * @author https://github.com/kuangcp on 2019-11-23 21:58 + */ +@Slf4j +public class KafkaProducerWrapperTest { + + + @Test + public void testSendPlainText() { + KafkaProducerWrapper.sendPlainText(Topics.HI, "test send message"); + } + + @Test + public void testSendMessage(){ + User user = User.builder().name("one").nickName("two").build(); + KafkaProducerWrapper.send(Topics.USER_LOGIN, user); + } +} diff --git a/kafka/src/test/java/com/github/kuangcp/wrapper/consumer/HiExecutor.java b/kafka/src/test/java/com/github/kuangcp/wrapper/consumer/HiExecutor.java index fdf37b9e..799b1612 100644 --- a/kafka/src/test/java/com/github/kuangcp/wrapper/consumer/HiExecutor.java +++ b/kafka/src/test/java/com/github/kuangcp/wrapper/consumer/HiExecutor.java @@ -1,9 +1,11 @@ package com.github.kuangcp.wrapper.consumer; +import com.github.kuangcp.wrapper.domain.Topics; + /** * @author https://github.com/kuangcp on 2019-11-22 21:16 */ -public class HiExecutor implements SimpleTopicMessageExecutor { +public class HiExecutor implements SimpleMessageExecutor { @Override public void execute(String message) { @@ -12,6 +14,6 @@ public void execute(String message) { @Override public String getTopic() { - return "Hi"; + return Topics.USER_LOGIN; } } diff --git a/kafka/src/test/java/com/github/kuangcp/wrapper/consumer/KafkaConsumerWrapperTest.java b/kafka/src/test/java/com/github/kuangcp/wrapper/consumer/KafkaConsumerWrapperTest.java index cc3d9576..8f0ba466 100644 --- a/kafka/src/test/java/com/github/kuangcp/wrapper/consumer/KafkaConsumerWrapperTest.java +++ b/kafka/src/test/java/com/github/kuangcp/wrapper/consumer/KafkaConsumerWrapperTest.java @@ -13,6 +13,13 @@ public class KafkaConsumerWrapperTest { public void testConsumerWithSimpleExecutor() { HiExecutor executor = new HiExecutor(); + KafkaConsumerWrapper.consumerPlainText(Duration.ofMillis(1000), + Collections.singletonList(executor)); + } + + @Test + public void testConsumerMessage(){ + LoginMessageExecutor executor = new LoginMessageExecutor(); KafkaConsumerWrapper.consumer(Duration.ofMillis(1000), Collections.singletonList(executor)); } } diff --git a/kafka/src/test/java/com/github/kuangcp/wrapper/consumer/LoginMessageExecutor.java b/kafka/src/test/java/com/github/kuangcp/wrapper/consumer/LoginMessageExecutor.java new file mode 100644 index 00000000..8a54d08e --- /dev/null +++ b/kafka/src/test/java/com/github/kuangcp/wrapper/consumer/LoginMessageExecutor.java @@ -0,0 +1,28 @@ +package com.github.kuangcp.wrapper.consumer; + +import com.github.kuangcp.wrapper.Message; +import com.github.kuangcp.wrapper.domain.Topics; +import com.github.kuangcp.wrapper.domain.User; +import lombok.extern.slf4j.Slf4j; + +/** + * @author https://github.com/kuangcp on 2019-11-23 21:51 + */ +@Slf4j +public class LoginMessageExecutor implements GeneralMessageExecutor { + + @Override + public void execute(Message message) { + log.warn("user={} msg={}", message.getContent(), message); + } + + @Override + public String getTopic() { + return Topics.USER_LOGIN; + } + + @Override + public Class getContentClass() { + return User.class; + } +} diff --git a/kafka/src/test/java/com/github/kuangcp/wrapper/domain/Topics.java b/kafka/src/test/java/com/github/kuangcp/wrapper/domain/Topics.java new file mode 100644 index 00000000..1d811ba2 --- /dev/null +++ b/kafka/src/test/java/com/github/kuangcp/wrapper/domain/Topics.java @@ -0,0 +1,10 @@ +package com.github.kuangcp.wrapper.domain; + +/** + * @author https://github.com/kuangcp on 2019-11-23 21:52 + */ +public interface Topics { + + String HI = "hi"; + String USER_LOGIN = "user_login"; +} diff --git a/kafka/src/test/java/com/github/kuangcp/wrapper/domain/User.java b/kafka/src/test/java/com/github/kuangcp/wrapper/domain/User.java new file mode 100644 index 00000000..8de3f543 --- /dev/null +++ b/kafka/src/test/java/com/github/kuangcp/wrapper/domain/User.java @@ -0,0 +1,27 @@ +package com.github.kuangcp.wrapper.domain; + +import java.io.Serializable; +import java.util.List; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author https://github.com/kuangcp on 2019-11-23 21:52 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class User implements Serializable { + + private static final long serialVersionUID = 4902781473338262495L; + + private String name; + + private String nickName; + + private List phones; + +} From f5d7144d8efa59128cc165eea64280be27b77131 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Sun, 24 Nov 2019 00:04:40 +0800 Subject: [PATCH 125/476] +) complete --- .../kuangcp/wrapper/KafkaProducerWrapper.java | 1 + .../wrapper/KafkaProducerWrapperTest.java | 11 +++++++-- .../kuangcp/wrapper/consumer/HiExecutor.java | 6 +++-- .../github/kuangcp/wrapper/domain/Book.java | 23 +++++++++++++++++++ .../github/kuangcp/wrapper/domain/User.java | 2 ++ 5 files changed, 39 insertions(+), 4 deletions(-) create mode 100644 kafka/src/test/java/com/github/kuangcp/wrapper/domain/Book.java diff --git a/kafka/src/main/java/com/github/kuangcp/wrapper/KafkaProducerWrapper.java b/kafka/src/main/java/com/github/kuangcp/wrapper/KafkaProducerWrapper.java index 0a3d634c..4b0a14af 100644 --- a/kafka/src/main/java/com/github/kuangcp/wrapper/KafkaProducerWrapper.java +++ b/kafka/src/main/java/com/github/kuangcp/wrapper/KafkaProducerWrapper.java @@ -61,6 +61,7 @@ public static void send(String topic, T content) { try { String messageStr = objectMapper.writeValueAsString(message); producer.send(new ProducerRecord<>(topic, messageStr)); + log.info("send message succeed: messageStr={}", messageStr); } catch (JsonProcessingException e) { log.error("", e); } diff --git a/kafka/src/test/java/com/github/kuangcp/wrapper/KafkaProducerWrapperTest.java b/kafka/src/test/java/com/github/kuangcp/wrapper/KafkaProducerWrapperTest.java index 03612ee9..0b2759e2 100644 --- a/kafka/src/test/java/com/github/kuangcp/wrapper/KafkaProducerWrapperTest.java +++ b/kafka/src/test/java/com/github/kuangcp/wrapper/KafkaProducerWrapperTest.java @@ -1,7 +1,10 @@ package com.github.kuangcp.wrapper; +import com.github.kuangcp.wrapper.domain.Book; import com.github.kuangcp.wrapper.domain.Topics; import com.github.kuangcp.wrapper.domain.User; +import java.util.Arrays; +import java.util.Collections; import lombok.extern.slf4j.Slf4j; import org.junit.Test; @@ -18,8 +21,12 @@ public void testSendPlainText() { } @Test - public void testSendMessage(){ - User user = User.builder().name("one").nickName("two").build(); + public void testSendMessage() { + Book book = Book.builder().name("test").type("math").build(); + User user = User.builder().name("one").nickName("two") + .phones(Arrays.asList("11111", "22222")) + .books(Collections.singletonList(book)) + .build(); KafkaProducerWrapper.send(Topics.USER_LOGIN, user); } } diff --git a/kafka/src/test/java/com/github/kuangcp/wrapper/consumer/HiExecutor.java b/kafka/src/test/java/com/github/kuangcp/wrapper/consumer/HiExecutor.java index 799b1612..374d5169 100644 --- a/kafka/src/test/java/com/github/kuangcp/wrapper/consumer/HiExecutor.java +++ b/kafka/src/test/java/com/github/kuangcp/wrapper/consumer/HiExecutor.java @@ -1,19 +1,21 @@ package com.github.kuangcp.wrapper.consumer; import com.github.kuangcp.wrapper.domain.Topics; +import lombok.extern.slf4j.Slf4j; /** * @author https://github.com/kuangcp on 2019-11-22 21:16 */ +@Slf4j public class HiExecutor implements SimpleMessageExecutor { @Override public void execute(String message) { - System.out.println(message); + log.info(": message={}", message); } @Override public String getTopic() { - return Topics.USER_LOGIN; + return Topics.HI; } } diff --git a/kafka/src/test/java/com/github/kuangcp/wrapper/domain/Book.java b/kafka/src/test/java/com/github/kuangcp/wrapper/domain/Book.java new file mode 100644 index 00000000..b24f556f --- /dev/null +++ b/kafka/src/test/java/com/github/kuangcp/wrapper/domain/Book.java @@ -0,0 +1,23 @@ +package com.github.kuangcp.wrapper.domain; + +import java.io.Serializable; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author https://github.com/kuangcp on 2019-11-23 23:58 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class Book implements Serializable { + + private static final long serialVersionUID = -5993442476257577012L; + + private String name; + + private String type; +} diff --git a/kafka/src/test/java/com/github/kuangcp/wrapper/domain/User.java b/kafka/src/test/java/com/github/kuangcp/wrapper/domain/User.java index 8de3f543..13681719 100644 --- a/kafka/src/test/java/com/github/kuangcp/wrapper/domain/User.java +++ b/kafka/src/test/java/com/github/kuangcp/wrapper/domain/User.java @@ -24,4 +24,6 @@ public class User implements Serializable { private List phones; + private List books; + } From db5acaaf3a2ea817106e5eab40cc7db59da7c4d1 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Sun, 24 Nov 2019 00:26:19 +0800 Subject: [PATCH 126/476] -) extract as util --- .../kuangcp/wrapper/KafkaProducerWrapper.java | 90 ----------- .../com/github/kuangcp/wrapper/Message.java | 36 ----- .../wrapper/config/KafkaConfigManager.java | 76 --------- .../wrapper/config/KafkaConsumerConfig.java | 63 -------- .../wrapper/config/KafkaProducerConfig.java | 76 --------- .../consumer/GeneralMessageExecutor.java | 11 -- .../consumer/KafkaConsumerWrapper.java | 152 ------------------ .../wrapper/consumer/MessageExecutor.java | 12 -- .../wrapper/consumer/MessageTopic.java | 9 -- .../consumer/SimpleMessageExecutor.java | 8 - .../wrapper/KafkaProducerWrapperTest.java | 32 ---- .../config/KafkaConfigManagerTest.java | 18 --- .../kuangcp/wrapper/consumer/HiExecutor.java | 21 --- .../consumer/KafkaConsumerWrapperTest.java | 25 --- .../consumer/LoginMessageExecutor.java | 28 ---- .../github/kuangcp/wrapper/domain/Book.java | 23 --- .../github/kuangcp/wrapper/domain/Topics.java | 10 -- .../github/kuangcp/wrapper/domain/User.java | 29 ---- 18 files changed, 719 deletions(-) delete mode 100644 kafka/src/main/java/com/github/kuangcp/wrapper/KafkaProducerWrapper.java delete mode 100644 kafka/src/main/java/com/github/kuangcp/wrapper/Message.java delete mode 100644 kafka/src/main/java/com/github/kuangcp/wrapper/config/KafkaConfigManager.java delete mode 100644 kafka/src/main/java/com/github/kuangcp/wrapper/config/KafkaConsumerConfig.java delete mode 100644 kafka/src/main/java/com/github/kuangcp/wrapper/config/KafkaProducerConfig.java delete mode 100644 kafka/src/main/java/com/github/kuangcp/wrapper/consumer/GeneralMessageExecutor.java delete mode 100644 kafka/src/main/java/com/github/kuangcp/wrapper/consumer/KafkaConsumerWrapper.java delete mode 100644 kafka/src/main/java/com/github/kuangcp/wrapper/consumer/MessageExecutor.java delete mode 100644 kafka/src/main/java/com/github/kuangcp/wrapper/consumer/MessageTopic.java delete mode 100644 kafka/src/main/java/com/github/kuangcp/wrapper/consumer/SimpleMessageExecutor.java delete mode 100644 kafka/src/test/java/com/github/kuangcp/wrapper/KafkaProducerWrapperTest.java delete mode 100644 kafka/src/test/java/com/github/kuangcp/wrapper/config/KafkaConfigManagerTest.java delete mode 100644 kafka/src/test/java/com/github/kuangcp/wrapper/consumer/HiExecutor.java delete mode 100644 kafka/src/test/java/com/github/kuangcp/wrapper/consumer/KafkaConsumerWrapperTest.java delete mode 100644 kafka/src/test/java/com/github/kuangcp/wrapper/consumer/LoginMessageExecutor.java delete mode 100644 kafka/src/test/java/com/github/kuangcp/wrapper/domain/Book.java delete mode 100644 kafka/src/test/java/com/github/kuangcp/wrapper/domain/Topics.java delete mode 100644 kafka/src/test/java/com/github/kuangcp/wrapper/domain/User.java diff --git a/kafka/src/main/java/com/github/kuangcp/wrapper/KafkaProducerWrapper.java b/kafka/src/main/java/com/github/kuangcp/wrapper/KafkaProducerWrapper.java deleted file mode 100644 index 4b0a14af..00000000 --- a/kafka/src/main/java/com/github/kuangcp/wrapper/KafkaProducerWrapper.java +++ /dev/null @@ -1,90 +0,0 @@ -package com.github.kuangcp.wrapper; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.github.kuangcp.wrapper.config.KafkaConfigManager; -import java.util.Objects; -import java.util.Properties; -import lombok.extern.slf4j.Slf4j; -import org.apache.kafka.clients.producer.KafkaProducer; -import org.apache.kafka.clients.producer.Producer; -import org.apache.kafka.clients.producer.ProducerRecord; - -/** - * @author https://github.com/kuangcp on 2019-11-13 09:36 - */ -@Slf4j -public class KafkaProducerWrapper { - - private static ObjectMapper objectMapper = new ObjectMapper(); - - private static Properties properties = KafkaConfigManager.getProducerConfig() - .orElseThrow(() -> new RuntimeException("加载Kafka生产者配置出错")); - - private static Producer producer = new KafkaProducer<>(properties); - - static { - Runtime.getRuntime().addShutdownHook(new Thread(KafkaProducerWrapper::shutdown)); - } - - /** - * 发送消息 - * - * @param topic 主题 - * @param message 消息 - * @param 内容类型 - * @see Message - */ - public static void send(String topic, Message message) { - if (Objects.isNull(topic) || topic.isEmpty() || Objects.isNull(message)) { - return; - } - - try { - String messageStr = objectMapper.writeValueAsString(message); - producer.send(new ProducerRecord<>(topic, messageStr)); - } catch (JsonProcessingException e) { - log.error("", e); - } - } - - /** - * 发送消息 - * - * @param topic 主题 - * @param content 消息内容 - * @param 内容类型 - * @see Message - */ - public static void send(String topic, T content) { - Message message = new Message<>(content); - try { - String messageStr = objectMapper.writeValueAsString(message); - producer.send(new ProducerRecord<>(topic, messageStr)); - log.info("send message succeed: messageStr={}", messageStr); - } catch (JsonProcessingException e) { - log.error("", e); - } - } - - /** - * 发送字符串消息 - * - * @param topic 主题 - * @param message 消息 - */ - public static void sendPlainText(String topic, String message) { - producer.send(new ProducerRecord<>(topic, message)); - } - - /** - * 生产者资源回收 未发送的消息进行发送 - */ - private static void shutdown() { - try { - producer.close(); - } catch (Exception e) { - log.error("shutdown messageProducer error"); - } - } -} diff --git a/kafka/src/main/java/com/github/kuangcp/wrapper/Message.java b/kafka/src/main/java/com/github/kuangcp/wrapper/Message.java deleted file mode 100644 index f1e06a31..00000000 --- a/kafka/src/main/java/com/github/kuangcp/wrapper/Message.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.github.kuangcp.wrapper; - -import java.io.Serializable; -import java.util.Date; -import java.util.UUID; -import lombok.Data; -import lombok.NoArgsConstructor; - -/** - * @author https://github.com/kuangcp on 2019-11-13 09:48 - */ -@Data -@NoArgsConstructor -public class Message implements Serializable { - - private static final long serialVersionUID = 0L; - - /** - * 消息Id - */ - private String id = UUID.randomUUID().toString(); - - /** - * 消息创建时间 - */ - private Date createTime = new Date(); - - /** - * 消息内容 - */ - private T content; - - public Message(T content) { - this.content = content; - } -} \ No newline at end of file diff --git a/kafka/src/main/java/com/github/kuangcp/wrapper/config/KafkaConfigManager.java b/kafka/src/main/java/com/github/kuangcp/wrapper/config/KafkaConfigManager.java deleted file mode 100644 index 466a6fd9..00000000 --- a/kafka/src/main/java/com/github/kuangcp/wrapper/config/KafkaConfigManager.java +++ /dev/null @@ -1,76 +0,0 @@ -package com.github.kuangcp.wrapper.config; - -import java.io.IOException; -import java.io.InputStream; -import java.util.Optional; -import java.util.Properties; -import java.util.UUID; -import lombok.extern.slf4j.Slf4j; -import org.apache.kafka.clients.consumer.ConsumerConfig; -import org.apache.kafka.clients.producer.ProducerConfig; -import org.apache.kafka.common.serialization.StringDeserializer; -import org.apache.kafka.common.serialization.StringSerializer; - -/** - * @author https://github.com/kuangcp on 2019-11-13 09:37 - */ -@Slf4j -public class KafkaConfigManager { - - private static Properties load; - - static { - InputStream is = KafkaConfigManager.class.getResourceAsStream("/kafka.properties"); - try { - load = new Properties(); - load.load(is); - } catch (IOException e) { - log.error("", e); - } - } - - public static Optional getConsumerConfig() { - Optional serverOpt = Optional.ofNullable(load) - .map(v -> v.getProperty(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG)); - if (!serverOpt.isPresent()) { - return Optional.empty(); - } - - String groupId; - Optional groupOpt = Optional.ofNullable(load) - .map(v -> v.getProperty(ConsumerConfig.GROUP_ID_CONFIG)); - groupId = groupOpt.orElseGet(() -> "Group-" + UUID.randomUUID().toString()); - - Properties config = new Properties(); - config.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, serverOpt.get()); - config.put(ConsumerConfig.GROUP_ID_CONFIG, groupId); - - config.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "true"); - config.put(ConsumerConfig.AUTO_COMMIT_INTERVAL_MS_CONFIG, "1000"); - config.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest"); - config.put(ConsumerConfig.SESSION_TIMEOUT_MS_CONFIG, "30000"); - config.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName()); - config.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName()); - return Optional.of(config); - } - - public static Optional getProducerConfig() { - Optional serverOpt = Optional.ofNullable(load) - .map(v -> v.getProperty(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG)); - if (!serverOpt.isPresent()) { - return Optional.empty(); - } - - Properties config = new Properties(); - config.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, serverOpt.get()); - config.put(ProducerConfig.ACKS_CONFIG, "all"); - config.put(ProducerConfig.RETRIES_CONFIG, 0); - config.put(ProducerConfig.BATCH_SIZE_CONFIG, 16384); - config.put(ProducerConfig.LINGER_MS_CONFIG, 1); - config.put(ProducerConfig.BUFFER_MEMORY_CONFIG, 33554432); - - config.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName()); - config.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName()); - return Optional.of(config); - } -} diff --git a/kafka/src/main/java/com/github/kuangcp/wrapper/config/KafkaConsumerConfig.java b/kafka/src/main/java/com/github/kuangcp/wrapper/config/KafkaConsumerConfig.java deleted file mode 100644 index edbe1695..00000000 --- a/kafka/src/main/java/com/github/kuangcp/wrapper/config/KafkaConsumerConfig.java +++ /dev/null @@ -1,63 +0,0 @@ -package com.github.kuangcp.wrapper.config; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -/** - * @author https://github.com/kuangcp on 2019-11-13 09:43 - */ -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class KafkaConsumerConfig { - - //group.id - private String groupId; - - //zookeeper.connect - //zookeeper server list - private String zookeeperServerIpAndPortList; - - //zookeeper.session.timeout.ms - // default 6000 - private String zookeeperSessionTimeout; - //zookeeper.connection.timeout.ms - //timeout in ms for connecting to zookeeper - // default =zookeeperSessionTimeout =6000 - private String zookeeperConnectionTimeout; - //zookeeper.sync.time.ms - // default 2000 - private String zookeeperSyncTime; - - //consumer.timeout.ms - // default -1 - private String consumerTimeout; - - //auto.commit.enable - //如果设为true,consumer会定时向ZooKeeper发送已经获取到的消息的offset - // 当consumer进程挂掉时,已经提交的offset可以继续使用,让新的consumer继续工作 - // 如果为false,会重复读取挂掉的consumer已经读取过的消息 - //default true - private String autoCommitEnable; - - //auto.commit.interval.ms - //consumer向ZooKeeper发送offset的时间间隔 - //default 60000 - private String autoCommitInterval; - - //rebalance.backoff.ms - //该设置过短就会导致old consumer还没有来得及释放资源,new consumer重试失败多次到达阀值就退出了。 - // 抛出 ConsumerRebalanceFailedException - //所以要确保rebalanceMaxRetries * rebalanceMaxRetries > zookeeperSessionTimeout - // default =zookeeperSyncTime =2000 - private String reBalanceBackOff; - //rebalance.max.retries - // default 4 - private String rebalanceMaxRetries; - - //---还有更多配置,详见ConsumerConfig, - -} diff --git a/kafka/src/main/java/com/github/kuangcp/wrapper/config/KafkaProducerConfig.java b/kafka/src/main/java/com/github/kuangcp/wrapper/config/KafkaProducerConfig.java deleted file mode 100644 index 07410aec..00000000 --- a/kafka/src/main/java/com/github/kuangcp/wrapper/config/KafkaProducerConfig.java +++ /dev/null @@ -1,76 +0,0 @@ -package com.github.kuangcp.wrapper.config; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -/** - * @author https://github.com/kuangcp on 2019-11-13 09:44 - */ -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class KafkaProducerConfig { - - //metadata.broker.list - //broker列表可以为kafka server的子集,因为producer需要从broker中获取metadata - //尽管每个broker都可以提供metadata,此处还是建议,将所有broker都列举出来 - private String kafkaBrokerIpAndPortList; - - //producer.type - //消息发送的模式,同步或异步,异步的时候消息将会在本地buffer,并适时批量发送 - //default sync - private String producerType; - - //batch.num.messages - //消息在producer端buffer的条数,仅在producer.type=async下有效 - //default 200 - private String batchNumMessages; - - //request.required.acks - //producer接收消息ack的时机,默认为0 - // 0:produce人不会等待broker发送ack - // 1:当leader接收到消息后发送ack - // 2:当所有的follower都同步消息成功后发送ack - private String requestRequiredAcks; - - //serializer.class - //消息序列化类 - //向kafka发送数据,默认支持String和byte[]2种类型,分别支持String和二进制 - //包括kafka.serializer.StringEncoder和kafka.serializer.DefaultEncoder 2个类 - //default kafka.serializer.DefaultEncoder - private String serializerClass; - //key.serializer.class - //default =serializer.class =kafka.serializer.StringEncoder - private String keySerializerClass; - - //compression.codec - //消息压缩算法,none,gzip,snappy - //default none - private String compressionCodec; - //compressed.topics - //default null - private String compressedTopics; - - //partitioner.class - //partitions路由类,消息在发送时将根据此实例的方法获得partition索引号 - //default kafka.producer.DefaultPartitioner - private String partitionerClass; - - - //message.send.max.retries - //default 3 - private String messageSendMaxRetries; - - //retry.backoff.ms - //default 100 - private String retryBackoff; - - //topic.metadata.refresh.interval.ms - //default 600000 - private String topicMetadataRefreshInterval; - - //---还有更多配置,详见ProducerConfig -} diff --git a/kafka/src/main/java/com/github/kuangcp/wrapper/consumer/GeneralMessageExecutor.java b/kafka/src/main/java/com/github/kuangcp/wrapper/consumer/GeneralMessageExecutor.java deleted file mode 100644 index d35cd08a..00000000 --- a/kafka/src/main/java/com/github/kuangcp/wrapper/consumer/GeneralMessageExecutor.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.github.kuangcp.wrapper.consumer; - -import com.github.kuangcp.wrapper.Message; - -/** - * @author https://github.com/kuangcp on 2019-11-13 09:49 - */ -public interface GeneralMessageExecutor extends MessageTopic, MessageExecutor> { - - Class getContentClass(); -} diff --git a/kafka/src/main/java/com/github/kuangcp/wrapper/consumer/KafkaConsumerWrapper.java b/kafka/src/main/java/com/github/kuangcp/wrapper/consumer/KafkaConsumerWrapper.java deleted file mode 100644 index 292ee6fd..00000000 --- a/kafka/src/main/java/com/github/kuangcp/wrapper/consumer/KafkaConsumerWrapper.java +++ /dev/null @@ -1,152 +0,0 @@ -package com.github.kuangcp.wrapper.consumer; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.module.SimpleModule; -import com.github.kuangcp.wrapper.Message; -import com.github.kuangcp.wrapper.config.KafkaConfigManager; -import java.time.Duration; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Properties; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.function.Function; -import java.util.stream.Collectors; -import lombok.extern.slf4j.Slf4j; -import org.apache.kafka.clients.consumer.ConsumerRecord; -import org.apache.kafka.clients.consumer.ConsumerRecords; -import org.apache.kafka.clients.consumer.KafkaConsumer; - -/** - * @author https://github.com/kuangcp on 2019-11-13 09:35 - */ -@SuppressWarnings("unchecked") -@Slf4j -public class KafkaConsumerWrapper { - - private static ObjectMapper objectMapper = new ObjectMapper(); - - private static volatile boolean stop = false; - - private static ExecutorService pool = Executors - .newFixedThreadPool(Runtime.getRuntime().availableProcessors()); - - private static Properties properties = KafkaConfigManager.getConsumerConfig() - .orElseThrow(() -> new RuntimeException("加载Kafka消费者配置出错")); - - private static KafkaConsumer consumer; - - static { - SimpleModule module = new SimpleModule(); - - Runtime.getRuntime().addShutdownHook(new Thread(KafkaConsumerWrapper::shutdown)); - } - - // TODO 两个线程分摊 Topics? - - - /** - * 消费Topic - * - * @param duration 拉取间隔 - * @param executors 执行器 - * @param executor - */ - static & MessageTopic> void consumerPlainText( - Duration duration, List executors) { - if (Objects.isNull(duration) || Objects.isNull(executors) || executors.isEmpty()) { - log.warn("consumer param invalid"); - return; - } - - if (Objects.nonNull(consumer)) { - log.warn("consumer has started"); - return; - } - - Map executorMap = executors.stream() - .collect(Collectors.toMap(E::getTopic, Function.identity(), (front, current) -> current)); - - consumer = new KafkaConsumer<>(properties); - consumer.subscribe(executorMap.keySet()); - while (!stop) { - ConsumerRecords records = consumer.poll(duration); - for (ConsumerRecord record : records) { - E executor = executorMap.get(record.topic()); - - if (Objects.nonNull(executor)) { - - pool.submit(() -> executor.execute(record.value())); - } - } - } - } - - /** - * 消费Topic - * - * @param duration 拉取间隔 - * @param executors 执行器 - * @param Message content 类型 - */ - static void consumer(Duration duration, List> executors) { - if (Objects.isNull(duration) || Objects.isNull(executors) || executors.isEmpty()) { - log.warn("consumer param invalid"); - return; - } - - if (Objects.nonNull(consumer)) { - log.warn("consumer has started"); - return; - } - - Map> executorMap = executors.stream() - .collect(Collectors.toMap(GeneralMessageExecutor::getTopic, Function.identity(), - (front, current) -> current)); - - consumer = new KafkaConsumer<>(properties); - consumer.subscribe(executorMap.keySet()); - while (!stop) { - ConsumerRecords records = consumer.poll(duration); - for (ConsumerRecord record : records) { - GeneralMessageExecutor executor = executorMap.get(record.topic()); - - if (Objects.nonNull(executor)) { - pool.submit(() -> { - try { - Message message = objectMapper.readValue(record.value(), Message.class); - - // 因为以上反序列化无法获取泛型的类型,所以无法正确的设置 content 对象,根本原因是Java的泛型擦除 - // 解决方案是先将content再序列化 最后指定类型反序列化 - Class contentClass = executor.getContentClass(); - String value = objectMapper.writeValueAsString(message.getContent()); - T content = objectMapper.readValue(value, contentClass); - message.setContent(content); - - executor.execute(message); - } catch (Exception e) { - log.error("", e); - } - }); - - } - } - } - } - - /** - * 消费者资源回收 线程池关闭 - */ - private static void shutdown() { - stop = true; - try { - if (Objects.nonNull(consumer)) { - consumer.close(); - } - pool.shutdown(); - } catch (Exception e) { - log.error("shutdown messageProducer error"); - } - } -} diff --git a/kafka/src/main/java/com/github/kuangcp/wrapper/consumer/MessageExecutor.java b/kafka/src/main/java/com/github/kuangcp/wrapper/consumer/MessageExecutor.java deleted file mode 100644 index 9b4076c1..00000000 --- a/kafka/src/main/java/com/github/kuangcp/wrapper/consumer/MessageExecutor.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.github.kuangcp.wrapper.consumer; - -/** - * @author https://github.com/kuangcp on 2019-11-13 09:47 - */ -public interface MessageExecutor { - - /** - * 处理消息 - */ - void execute(T message); -} diff --git a/kafka/src/main/java/com/github/kuangcp/wrapper/consumer/MessageTopic.java b/kafka/src/main/java/com/github/kuangcp/wrapper/consumer/MessageTopic.java deleted file mode 100644 index 63d0f3ee..00000000 --- a/kafka/src/main/java/com/github/kuangcp/wrapper/consumer/MessageTopic.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.github.kuangcp.wrapper.consumer; - -/** - * @author https://github.com/kuangcp on 2019-11-22 21:07 - */ -public interface MessageTopic { - - String getTopic(); -} diff --git a/kafka/src/main/java/com/github/kuangcp/wrapper/consumer/SimpleMessageExecutor.java b/kafka/src/main/java/com/github/kuangcp/wrapper/consumer/SimpleMessageExecutor.java deleted file mode 100644 index 8d9c3cc5..00000000 --- a/kafka/src/main/java/com/github/kuangcp/wrapper/consumer/SimpleMessageExecutor.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.github.kuangcp.wrapper.consumer; - -/** - * @author https://github.com/kuangcp on 2019-11-22 21:06 - */ -public interface SimpleMessageExecutor extends MessageTopic, MessageExecutor { - -} diff --git a/kafka/src/test/java/com/github/kuangcp/wrapper/KafkaProducerWrapperTest.java b/kafka/src/test/java/com/github/kuangcp/wrapper/KafkaProducerWrapperTest.java deleted file mode 100644 index 0b2759e2..00000000 --- a/kafka/src/test/java/com/github/kuangcp/wrapper/KafkaProducerWrapperTest.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.github.kuangcp.wrapper; - -import com.github.kuangcp.wrapper.domain.Book; -import com.github.kuangcp.wrapper.domain.Topics; -import com.github.kuangcp.wrapper.domain.User; -import java.util.Arrays; -import java.util.Collections; -import lombok.extern.slf4j.Slf4j; -import org.junit.Test; - -/** - * @author https://github.com/kuangcp on 2019-11-23 21:58 - */ -@Slf4j -public class KafkaProducerWrapperTest { - - - @Test - public void testSendPlainText() { - KafkaProducerWrapper.sendPlainText(Topics.HI, "test send message"); - } - - @Test - public void testSendMessage() { - Book book = Book.builder().name("test").type("math").build(); - User user = User.builder().name("one").nickName("two") - .phones(Arrays.asList("11111", "22222")) - .books(Collections.singletonList(book)) - .build(); - KafkaProducerWrapper.send(Topics.USER_LOGIN, user); - } -} diff --git a/kafka/src/test/java/com/github/kuangcp/wrapper/config/KafkaConfigManagerTest.java b/kafka/src/test/java/com/github/kuangcp/wrapper/config/KafkaConfigManagerTest.java deleted file mode 100644 index 5bebbd06..00000000 --- a/kafka/src/test/java/com/github/kuangcp/wrapper/config/KafkaConfigManagerTest.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.github.kuangcp.wrapper.config; - -import java.util.Optional; -import java.util.Properties; -import org.junit.Test; - -/** - * @author https://github.com/kuangcp on 2019-11-17 14:45 - */ -public class KafkaConfigManagerTest { - - @Test - public void testConsumer(){ - Optional propOpt = KafkaConfigManager.getConsumerConfig(); - - propOpt.ifPresent(System.out::println); - } -} diff --git a/kafka/src/test/java/com/github/kuangcp/wrapper/consumer/HiExecutor.java b/kafka/src/test/java/com/github/kuangcp/wrapper/consumer/HiExecutor.java deleted file mode 100644 index 374d5169..00000000 --- a/kafka/src/test/java/com/github/kuangcp/wrapper/consumer/HiExecutor.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.github.kuangcp.wrapper.consumer; - -import com.github.kuangcp.wrapper.domain.Topics; -import lombok.extern.slf4j.Slf4j; - -/** - * @author https://github.com/kuangcp on 2019-11-22 21:16 - */ -@Slf4j -public class HiExecutor implements SimpleMessageExecutor { - - @Override - public void execute(String message) { - log.info(": message={}", message); - } - - @Override - public String getTopic() { - return Topics.HI; - } -} diff --git a/kafka/src/test/java/com/github/kuangcp/wrapper/consumer/KafkaConsumerWrapperTest.java b/kafka/src/test/java/com/github/kuangcp/wrapper/consumer/KafkaConsumerWrapperTest.java deleted file mode 100644 index 8f0ba466..00000000 --- a/kafka/src/test/java/com/github/kuangcp/wrapper/consumer/KafkaConsumerWrapperTest.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.github.kuangcp.wrapper.consumer; - -import java.time.Duration; -import java.util.Collections; -import org.junit.Test; - -/** - * @author https://github.com/kuangcp on 2019-11-17 14:54 - */ -public class KafkaConsumerWrapperTest { - - @Test - public void testConsumerWithSimpleExecutor() { - HiExecutor executor = new HiExecutor(); - - KafkaConsumerWrapper.consumerPlainText(Duration.ofMillis(1000), - Collections.singletonList(executor)); - } - - @Test - public void testConsumerMessage(){ - LoginMessageExecutor executor = new LoginMessageExecutor(); - KafkaConsumerWrapper.consumer(Duration.ofMillis(1000), Collections.singletonList(executor)); - } -} diff --git a/kafka/src/test/java/com/github/kuangcp/wrapper/consumer/LoginMessageExecutor.java b/kafka/src/test/java/com/github/kuangcp/wrapper/consumer/LoginMessageExecutor.java deleted file mode 100644 index 8a54d08e..00000000 --- a/kafka/src/test/java/com/github/kuangcp/wrapper/consumer/LoginMessageExecutor.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.github.kuangcp.wrapper.consumer; - -import com.github.kuangcp.wrapper.Message; -import com.github.kuangcp.wrapper.domain.Topics; -import com.github.kuangcp.wrapper.domain.User; -import lombok.extern.slf4j.Slf4j; - -/** - * @author https://github.com/kuangcp on 2019-11-23 21:51 - */ -@Slf4j -public class LoginMessageExecutor implements GeneralMessageExecutor { - - @Override - public void execute(Message message) { - log.warn("user={} msg={}", message.getContent(), message); - } - - @Override - public String getTopic() { - return Topics.USER_LOGIN; - } - - @Override - public Class getContentClass() { - return User.class; - } -} diff --git a/kafka/src/test/java/com/github/kuangcp/wrapper/domain/Book.java b/kafka/src/test/java/com/github/kuangcp/wrapper/domain/Book.java deleted file mode 100644 index b24f556f..00000000 --- a/kafka/src/test/java/com/github/kuangcp/wrapper/domain/Book.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.github.kuangcp.wrapper.domain; - -import java.io.Serializable; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -/** - * @author https://github.com/kuangcp on 2019-11-23 23:58 - */ -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class Book implements Serializable { - - private static final long serialVersionUID = -5993442476257577012L; - - private String name; - - private String type; -} diff --git a/kafka/src/test/java/com/github/kuangcp/wrapper/domain/Topics.java b/kafka/src/test/java/com/github/kuangcp/wrapper/domain/Topics.java deleted file mode 100644 index 1d811ba2..00000000 --- a/kafka/src/test/java/com/github/kuangcp/wrapper/domain/Topics.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.github.kuangcp.wrapper.domain; - -/** - * @author https://github.com/kuangcp on 2019-11-23 21:52 - */ -public interface Topics { - - String HI = "hi"; - String USER_LOGIN = "user_login"; -} diff --git a/kafka/src/test/java/com/github/kuangcp/wrapper/domain/User.java b/kafka/src/test/java/com/github/kuangcp/wrapper/domain/User.java deleted file mode 100644 index 13681719..00000000 --- a/kafka/src/test/java/com/github/kuangcp/wrapper/domain/User.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.github.kuangcp.wrapper.domain; - -import java.io.Serializable; -import java.util.List; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -/** - * @author https://github.com/kuangcp on 2019-11-23 21:52 - */ -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class User implements Serializable { - - private static final long serialVersionUID = 4902781473338262495L; - - private String name; - - private String nickName; - - private List phones; - - private List books; - -} From 15b1b6bdedab3faaff8547c22b3026d52e8a8dad Mon Sep 17 00:00:00 2001 From: kcp Date: Thu, 28 Nov 2019 19:54:26 +0800 Subject: [PATCH 127/476] +) Java8 bug --- .../lambda/bug/MultipleExtendsTest.java | 73 +++++++++++++++++++ .../{lambda => stream}/filter/FilterTest.java | 2 +- .../github/kuangcp/stream/map/MapTest.java | 52 +++++++++++++ 3 files changed, 126 insertions(+), 1 deletion(-) create mode 100644 java8/src/test/java/com/github/kuangcp/lambda/bug/MultipleExtendsTest.java rename java8/src/test/java/com/github/kuangcp/{lambda => stream}/filter/FilterTest.java (93%) create mode 100644 java8/src/test/java/com/github/kuangcp/stream/map/MapTest.java diff --git a/java8/src/test/java/com/github/kuangcp/lambda/bug/MultipleExtendsTest.java b/java8/src/test/java/com/github/kuangcp/lambda/bug/MultipleExtendsTest.java new file mode 100644 index 00000000..2a60a331 --- /dev/null +++ b/java8/src/test/java/com/github/kuangcp/lambda/bug/MultipleExtendsTest.java @@ -0,0 +1,73 @@ +package com.github.kuangcp.lambda.bug; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; +import org.junit.Test; + +/** + * @author https://github.com/kuangcp on 2019-11-28 19:43 + */ +public class MultipleExtendsTest { + + @Test + public void testMap() { + List list = Arrays.asList(new CImpl(), new CImpl()); + List names = list.stream().map(A::getName).collect(Collectors.toList()); + System.out.println(names); + } + + private static void getNames(List list) { + list.stream().map(E::getName).count(); + list.stream().map(E::getNum).count(); + } + + // https://bugs.java.com/bugdatabase/view_bug.do?bug_id=8142476 + @Test + public void testMapWithGeneric() { + List list = Arrays.asList(new CImpl(), new CImpl()); + getNames(list); + getNames(Arrays.asList(new D(), new D())); + } +} + + +interface A { + + String getName(); +} + +interface B { + + Integer getNum(); +} + +interface C extends A, B { + +} + +class CImpl implements C { + + @Override + public String getName() { + return null; + } + + @Override + public Integer getNum() { + return null; + } +} + +class D implements A, B { + + @Override + public String getName() { + return null; + } + + @Override + public Integer getNum() { + return null; + } +} \ No newline at end of file diff --git a/java8/src/test/java/com/github/kuangcp/lambda/filter/FilterTest.java b/java8/src/test/java/com/github/kuangcp/stream/filter/FilterTest.java similarity index 93% rename from java8/src/test/java/com/github/kuangcp/lambda/filter/FilterTest.java rename to java8/src/test/java/com/github/kuangcp/stream/filter/FilterTest.java index 61a76657..e8c116f5 100644 --- a/java8/src/test/java/com/github/kuangcp/lambda/filter/FilterTest.java +++ b/java8/src/test/java/com/github/kuangcp/stream/filter/FilterTest.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.lambda.filter; +package com.github.kuangcp.stream.filter; import java.util.stream.IntStream; import lombok.extern.slf4j.Slf4j; diff --git a/java8/src/test/java/com/github/kuangcp/stream/map/MapTest.java b/java8/src/test/java/com/github/kuangcp/stream/map/MapTest.java new file mode 100644 index 00000000..e731395f --- /dev/null +++ b/java8/src/test/java/com/github/kuangcp/stream/map/MapTest.java @@ -0,0 +1,52 @@ +package com.github.kuangcp.stream.map; + + +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.stream.Collectors; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; + +/** + * @author https://github.com/kuangcp on 2019-11-25 19:22 + */ +@Slf4j +public class MapTest { + + + @Test + public void testConvertMap() throws IllegalAccessException, InstantiationException { + Map input = new HashMap<>(); + input.put(1L, new A()); + Map result = convert(input, B.class); + log.info("result={}", result); + } + + R convert(I input, Class target) { + try { + return target.newInstance(); + } catch (InstantiationException | IllegalAccessException e) { + e.printStackTrace(); + } + return null; + } + + Map convert(Map inputMap, Class target) { + return inputMap.entrySet().stream().collect(Collectors + .toMap(Entry::getKey, v -> convert(v.getKey(), target), (front, current) -> current)); + } +} + +@Data +class A { + + private String name; +} + +@Data +class B { + + private String name; +} \ No newline at end of file From 3f9b5728f338daa322f963803c364c8848c55d07 Mon Sep 17 00:00:00 2001 From: kcp Date: Thu, 28 Nov 2019 20:05:51 +0800 Subject: [PATCH 128/476] -) skip test --- .../test/java/com/github/kuangcp/time/wheel/TimeWheelTest.java | 2 ++ java8/src/test/java/com/github/kuangcp/stream/map/MapTest.java | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/algorithms/src/test/java/com/github/kuangcp/time/wheel/TimeWheelTest.java b/algorithms/src/test/java/com/github/kuangcp/time/wheel/TimeWheelTest.java index 3b8cee46..3bfd4c57 100644 --- a/algorithms/src/test/java/com/github/kuangcp/time/wheel/TimeWheelTest.java +++ b/algorithms/src/test/java/com/github/kuangcp/time/wheel/TimeWheelTest.java @@ -14,11 +14,13 @@ import java.util.stream.IntStream; import lombok.extern.slf4j.Slf4j; import org.junit.Assert; +import org.junit.Ignore; import org.junit.Test; /** * @author https://github.com/kuangcp on 2019-10-27 12:15 */ +@Ignore @Slf4j public class TimeWheelTest { diff --git a/java8/src/test/java/com/github/kuangcp/stream/map/MapTest.java b/java8/src/test/java/com/github/kuangcp/stream/map/MapTest.java index e731395f..6fb06c52 100644 --- a/java8/src/test/java/com/github/kuangcp/stream/map/MapTest.java +++ b/java8/src/test/java/com/github/kuangcp/stream/map/MapTest.java @@ -17,7 +17,7 @@ public class MapTest { @Test - public void testConvertMap() throws IllegalAccessException, InstantiationException { + public void testConvertMap() { Map input = new HashMap<>(); input.put(1L, new A()); Map result = convert(input, B.class); From e6b51464ca79dd4f21097c1915d7b38a334246b4 Mon Sep 17 00:00:00 2001 From: kcp Date: Fri, 29 Nov 2019 14:19:40 +0800 Subject: [PATCH 129/476] +) filter and && --- .../kuangcp/stream/filter/FilterTest.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/java8/src/test/java/com/github/kuangcp/stream/filter/FilterTest.java b/java8/src/test/java/com/github/kuangcp/stream/filter/FilterTest.java index e8c116f5..8f95bc42 100644 --- a/java8/src/test/java/com/github/kuangcp/stream/filter/FilterTest.java +++ b/java8/src/test/java/com/github/kuangcp/stream/filter/FilterTest.java @@ -1,9 +1,15 @@ package com.github.kuangcp.stream.filter; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; import java.util.stream.IntStream; import lombok.extern.slf4j.Slf4j; import org.junit.Test; +import static org.hamcrest.Matchers.equalTo; +import static org.junit.Assert.assertThat; + /** * @author kuangcp on 3/6/19-5:08 PM */ @@ -26,4 +32,15 @@ public void testFilterWithNull() { long count = IntStream.rangeClosed(1, 10).filter(this::isTrue).count(); System.out.println(count); } + + // 多个 filter 具有 && 一样的短路效应 + @Test + public void testAnd() { + List data = Arrays.asList("", null, "2", "12"); + long count = data.stream() + .filter(Objects::nonNull) + .filter(v -> v.length() > 1) + .count(); + assertThat(count, equalTo(1L)); + } } From fc1787191fa6121f18c06e26e3d0e5f3571a1386 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Sun, 1 Dec 2019 12:58:53 +0800 Subject: [PATCH 130/476] +) use kafka util --- dependency.gradle | 2 +- kafka/build.gradle | 1 + .../com/github/kuangcp/hi/ConsumerDemo.java | 26 ++++++++++++------- kafka/src/main/resources/kafka.properties | 2 +- .../{logback.xml => logback-test.xml} | 5 ++-- 5 files changed, 22 insertions(+), 14 deletions(-) rename kafka/src/test/resources/{logback.xml => logback-test.xml} (79%) diff --git a/dependency.gradle b/dependency.gradle index abba913f..bfa20b1d 100644 --- a/dependency.gradle +++ b/dependency.gradle @@ -10,7 +10,7 @@ ext { , mail : '1.4.7' , jackson : '2.9.5' , hamcrest: '1.3' - , kcp_tool: '1.0.5' + , kcp_tool: '1.0.7' , netty : '4.1.35.Final' , guava : '27.1-jre' , jmh : '1.2.1' diff --git a/kafka/build.gradle b/kafka/build.gradle index b431af23..94317943 100644 --- a/kafka/build.gradle +++ b/kafka/build.gradle @@ -1,4 +1,5 @@ dependencies { + implementation libs['kcp-tool'] implementation libs['kafka-clients'] implementation libs['jackson_core'] implementation libs['jackson_databind'] diff --git a/kafka/src/main/java/com/github/kuangcp/hi/ConsumerDemo.java b/kafka/src/main/java/com/github/kuangcp/hi/ConsumerDemo.java index 13e40928..bbc736d0 100644 --- a/kafka/src/main/java/com/github/kuangcp/hi/ConsumerDemo.java +++ b/kafka/src/main/java/com/github/kuangcp/hi/ConsumerDemo.java @@ -5,7 +5,8 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.github.kuangcp.hi.domain.ProductStatisticJobCommand; -import com.github.kuangcp.hi.domain.StartCommand; +import com.github.kuangcp.kafka.KafkaConsumerUtil; +import com.github.kuangcp.kafka.common.SimpleMessageExecutor; import java.io.IOException; import java.time.Duration; import java.util.Collections; @@ -28,14 +29,20 @@ public class ConsumerDemo { private static ObjectMapper mapper = new ObjectMapper(); static void receiveHi() { - KafkaConsumer kafkaConsumer = new KafkaConsumer<>(properties); - kafkaConsumer.subscribe(Collections.singletonList(HI_TOPIC)); - while (true) { - ConsumerRecords records = kafkaConsumer.poll(Duration.ofMillis(100)); - for (ConsumerRecord record : records) { - log.info("offset = {}, value = {}", record.offset(), record.value()); + SimpleMessageExecutor executor = new SimpleMessageExecutor() { + @Override + public void execute(String message) { + log.info("message={}", message); } - } + + @Override + public String getTopic() { + return HI_TOPIC; + } + }; + + KafkaConsumerUtil.consumerPlainText(Duration.ofMillis(1000), + Collections.singletonList(executor)); } static void receiveCommand() throws IOException { @@ -45,7 +52,8 @@ static void receiveCommand() throws IOException { ConsumerRecords records = kafkaConsumer.poll(Duration.ofMillis(100)); for (ConsumerRecord record : records) { log.info("offset = {}, value = {}", record.offset(), record.value()); - ProductStatisticJobCommand command = mapper.readValue(record.value(), ProductStatisticJobCommand.class); + ProductStatisticJobCommand command = mapper + .readValue(record.value(), ProductStatisticJobCommand.class); System.out.println(command); } } diff --git a/kafka/src/main/resources/kafka.properties b/kafka/src/main/resources/kafka.properties index 800230f9..3c0a310c 100644 --- a/kafka/src/main/resources/kafka.properties +++ b/kafka/src/main/resources/kafka.properties @@ -1,2 +1,2 @@ bootstrap.servers=127.0.0.1:9092 -group.id=test-1 \ No newline at end of file +group.id=test-demo \ No newline at end of file diff --git a/kafka/src/test/resources/logback.xml b/kafka/src/test/resources/logback-test.xml similarity index 79% rename from kafka/src/test/resources/logback.xml rename to kafka/src/test/resources/logback-test.xml index 8523e298..bf2a1d8b 100644 --- a/kafka/src/test/resources/logback.xml +++ b/kafka/src/test/resources/logback-test.xml @@ -13,10 +13,9 @@ - + - - + \ No newline at end of file From 9d71b826e4aa8df24ef0a5646c1ddab27237c614 Mon Sep 17 00:00:00 2001 From: kcp Date: Tue, 3 Dec 2019 10:59:51 +0800 Subject: [PATCH 131/476] +) jvm will omit NPE stack when frequently --- .../test/java/log/OmitExceptionStackTest.java | 39 +++++++++++++++++++ class/src/test/resources/logback.xml | 37 ++++++++++++++++++ common-config/src/main/resources/logback.xml | 30 +++++++------- 3 files changed, 89 insertions(+), 17 deletions(-) create mode 100644 class/src/test/java/log/OmitExceptionStackTest.java create mode 100644 class/src/test/resources/logback.xml diff --git a/class/src/test/java/log/OmitExceptionStackTest.java b/class/src/test/java/log/OmitExceptionStackTest.java new file mode 100644 index 00000000..11ee21d8 --- /dev/null +++ b/class/src/test/java/log/OmitExceptionStackTest.java @@ -0,0 +1,39 @@ +package log; + +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; + +/** + * https://www.oracle.com/technetwork/java/javase/relnotes-139183.html + * https://blog.csdn.net/zheng0518/article/details/52450537 + * + * @author https://github.com/kuangcp on 2019-12-03 09:41 + */ +@Slf4j +public class OmitExceptionStackTest { + + private void handleA() { + String n = null; + n.length(); + } + + private void handleB() { + try { + handleA(); + } catch (Exception e) { + log.error("", e); + throw new RuntimeException(e.getMessage()); + } + } + + @Test + public void testNPE() { + for (int i = 0; i < 50000; i++) { + try { + handleB(); + } catch (Exception e) { + log.error("", e); + } + } + } +} diff --git a/class/src/test/resources/logback.xml b/class/src/test/resources/logback.xml new file mode 100644 index 00000000..7f94edc7 --- /dev/null +++ b/class/src/test/resources/logback.xml @@ -0,0 +1,37 @@ + + + + + + + + + %d{HH:mm:ss.SSS} %highlight(%-5level) [%thread] %logger{30} + %highlight(%M):%yellow(%-3L) %msg%n + + + + DEBUG + + + + + + ${log.base}debug.log + true + + ${log.base}debug.%d{yyyy-MM-dd}.log + + + %d{MM-dd HH:mm:ss.SSS} |-%-5level |%thread| %logger{36}:%L - %msg%n + + + DEBUG + + + + + + + + \ No newline at end of file diff --git a/common-config/src/main/resources/logback.xml b/common-config/src/main/resources/logback.xml index 787da32b..9df33a48 100644 --- a/common-config/src/main/resources/logback.xml +++ b/common-config/src/main/resources/logback.xml @@ -13,22 +13,20 @@ - - - ${log.base}debug.log - true - - ${log.base}debug.%d{yyyy-MM-dd}.log - - - %d{MM-dd HH:mm:ss.SSS} |-%-5level |%thread| %logger{36}:%L - %msg%n - - - DEBUG - - + + + + + + + + + + + + + - @@ -43,7 +41,6 @@ - @@ -58,7 +55,6 @@ - From 45f83639590b187409e7b970487a818978230244 Mon Sep 17 00:00:00 2001 From: kcp Date: Wed, 4 Dec 2019 18:24:47 +0800 Subject: [PATCH 132/476] *) json --- .../com/github/kuangcp/serialize/Person.java | 2 ++ .../serialize/json/speed/FastJsonTool.java | 8 +++---- .../serialize/json/speed/GsonTool.java | 8 +++---- .../serialize/json/speed/JacksonTool.java | 8 +++---- .../serialize/json/speed/JsonTool.java | 4 ++-- ...{SpeedTest.java => JsonSerializeTest.java} | 24 +++++++++++++++---- 6 files changed, 36 insertions(+), 18 deletions(-) rename class/src/test/java/com/github/kuangcp/serialize/json/{SpeedTest.java => JsonSerializeTest.java} (59%) diff --git a/class/src/main/java/com/github/kuangcp/serialize/Person.java b/class/src/main/java/com/github/kuangcp/serialize/Person.java index 61e5b588..4ef3165d 100644 --- a/class/src/main/java/com/github/kuangcp/serialize/Person.java +++ b/class/src/main/java/com/github/kuangcp/serialize/Person.java @@ -15,6 +15,8 @@ public class Person implements Serializable { private String address; private String phone; + public Person(){} + public Person(String name) { this.name = name; } diff --git a/class/src/main/java/com/github/kuangcp/serialize/json/speed/FastJsonTool.java b/class/src/main/java/com/github/kuangcp/serialize/json/speed/FastJsonTool.java index b0ff7f32..16440b07 100644 --- a/class/src/main/java/com/github/kuangcp/serialize/json/speed/FastJsonTool.java +++ b/class/src/main/java/com/github/kuangcp/serialize/json/speed/FastJsonTool.java @@ -12,13 +12,13 @@ public class FastJsonTool implements JsonTool { @Override - public void fromJSON(String json, Class target) { - JSON.parseObject(json, target); + public Person fromJSON(String json, Class target) { + return JSON.parseObject(json, target); } @Override - public void toJSON(List dataList) { - JSON.toJSON(dataList); + public String toJSON(List dataList) { + return JSON.toJSONString(dataList); } @Override diff --git a/class/src/main/java/com/github/kuangcp/serialize/json/speed/GsonTool.java b/class/src/main/java/com/github/kuangcp/serialize/json/speed/GsonTool.java index f26c92e5..0a9752b6 100644 --- a/class/src/main/java/com/github/kuangcp/serialize/json/speed/GsonTool.java +++ b/class/src/main/java/com/github/kuangcp/serialize/json/speed/GsonTool.java @@ -16,13 +16,13 @@ public class GsonTool implements JsonTool { private Gson gson = new Gson(); @Override - public void fromJSON(String json, Class target) { - gson.fromJson(json, target); + public Person fromJSON(String json, Class target) { + return gson.fromJson(json, target); } @Override - public void toJSON(List dataList) { - gson.toJson(dataList); + public String toJSON(List dataList) { + return gson.toJson(dataList); } @Override diff --git a/class/src/main/java/com/github/kuangcp/serialize/json/speed/JacksonTool.java b/class/src/main/java/com/github/kuangcp/serialize/json/speed/JacksonTool.java index 53a380a0..218595ff 100644 --- a/class/src/main/java/com/github/kuangcp/serialize/json/speed/JacksonTool.java +++ b/class/src/main/java/com/github/kuangcp/serialize/json/speed/JacksonTool.java @@ -16,13 +16,13 @@ public class JacksonTool implements JsonTool { private ObjectMapper mapper = new ObjectMapper(); @Override - public void fromJSON(String json, Class target) throws IOException { - mapper.readValue(json, target); + public Person fromJSON(String json, Class target) throws IOException { + return mapper.readValue(json, target); } @Override - public void toJSON(List dataList) throws JsonProcessingException { - mapper.writeValueAsString(dataList); + public String toJSON(List dataList) throws JsonProcessingException { + return mapper.writeValueAsString(dataList); } @Override diff --git a/class/src/main/java/com/github/kuangcp/serialize/json/speed/JsonTool.java b/class/src/main/java/com/github/kuangcp/serialize/json/speed/JsonTool.java index c4bcf4f0..2e6e1b48 100644 --- a/class/src/main/java/com/github/kuangcp/serialize/json/speed/JsonTool.java +++ b/class/src/main/java/com/github/kuangcp/serialize/json/speed/JsonTool.java @@ -11,9 +11,9 @@ */ public interface JsonTool { - void fromJSON(String json, Class target) throws IOException; + T fromJSON(String json, Class target) throws IOException; - void toJSON(List dataList) throws JsonProcessingException; + String toJSON(List dataList) throws JsonProcessingException; String getName(); } diff --git a/class/src/test/java/com/github/kuangcp/serialize/json/SpeedTest.java b/class/src/test/java/com/github/kuangcp/serialize/json/JsonSerializeTest.java similarity index 59% rename from class/src/test/java/com/github/kuangcp/serialize/json/SpeedTest.java rename to class/src/test/java/com/github/kuangcp/serialize/json/JsonSerializeTest.java index 60cf248d..a0f8318a 100644 --- a/class/src/test/java/com/github/kuangcp/serialize/json/SpeedTest.java +++ b/class/src/test/java/com/github/kuangcp/serialize/json/JsonSerializeTest.java @@ -1,12 +1,15 @@ package com.github.kuangcp.serialize.json; import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; import com.github.kuangcp.serialize.Person; import com.github.kuangcp.serialize.json.speed.FastJsonTool; import com.github.kuangcp.serialize.json.speed.GsonTool; import com.github.kuangcp.serialize.json.speed.JacksonTool; import com.github.kuangcp.serialize.json.speed.JsonTool; import com.github.kuangcp.time.GetRunTime; +import com.google.gson.Gson; +import java.io.IOException; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; @@ -18,9 +21,9 @@ * * @author kuangcp */ -public class SpeedTest { +public class JsonSerializeTest { - private static final int DATA_SIZE = 3000; + private static final int DATA_SIZE = 30; private List> list = Arrays .asList(new GsonTool(), new JacksonTool(), new FastJsonTool()); @@ -29,8 +32,20 @@ public class SpeedTest { .mapToObj(i -> new Person("name" + i)).collect(Collectors.toList()); @Test - public void compareRead() { + public void compareRead() throws IOException { + ObjectMapper mapper = new ObjectMapper(); + Person one = new Person("one"); + String json = mapper.writeValueAsString(one); +// Gson gson = new Gson(); +// String json = gson.toJson(one); + GetRunTime getRunTime = new GetRunTime(); + for (JsonTool tool : list) { + getRunTime.startCount(); + Person person = tool.fromJSON(json, Person.class); +// System.out.println(person); + getRunTime.endCountOneLine(tool.getName()); + } } @Test @@ -38,7 +53,8 @@ public void compareWrite() throws JsonProcessingException { GetRunTime getRunTime = new GetRunTime(); for (JsonTool tool : list) { getRunTime.startCount(); - tool.toJSON(personList); + String json = tool.toJSON(personList); + System.out.println(json); getRunTime.endCountOneLine(tool.getName()); } } From a3b5619e05b14fb3abc0acd66b98f26f4a13882f Mon Sep 17 00:00:00 2001 From: kcp Date: Fri, 13 Dec 2019 20:44:26 +0800 Subject: [PATCH 133/476] +) date --- .../stream/collector/CollectorTest.java | 26 +++++++++++++++++++ .../kuangcp/time/DateTimeFormatterTest.java | 19 ++++++++++++++ .../com/github/kuangcp/time/DurationTest.java | 18 ++++++++++--- 3 files changed, 60 insertions(+), 3 deletions(-) create mode 100644 java8/src/test/java/com/github/kuangcp/stream/collector/CollectorTest.java create mode 100644 java8/src/test/java/com/github/kuangcp/time/DateTimeFormatterTest.java diff --git a/java8/src/test/java/com/github/kuangcp/stream/collector/CollectorTest.java b/java8/src/test/java/com/github/kuangcp/stream/collector/CollectorTest.java new file mode 100644 index 00000000..871c9b1f --- /dev/null +++ b/java8/src/test/java/com/github/kuangcp/stream/collector/CollectorTest.java @@ -0,0 +1,26 @@ +package com.github.kuangcp.stream.collector; + +import static org.hamcrest.Matchers.equalTo; +import static org.junit.Assert.assertThat; + +import java.util.Comparator; +import java.util.Optional; +import java.util.stream.Stream; +import org.junit.Test; + +/** + * @author https://github.com/kuangcp on 2019-12-12 16:48 + */ +public class CollectorTest { + + @Test + public void testMergeSegment() { + Optional result = Stream.of(2, 4, 6, 3) + .sorted(Comparator.comparing(Integer::intValue).reversed()) + .reduce((a, b) -> { + System.out.println(a + " " + b); + return a - b; + }); + assertThat(result.get(), equalTo(-3)); + } +} diff --git a/java8/src/test/java/com/github/kuangcp/time/DateTimeFormatterTest.java b/java8/src/test/java/com/github/kuangcp/time/DateTimeFormatterTest.java new file mode 100644 index 00000000..e25e72a1 --- /dev/null +++ b/java8/src/test/java/com/github/kuangcp/time/DateTimeFormatterTest.java @@ -0,0 +1,19 @@ +package com.github.kuangcp.time; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import org.junit.Test; + +/** + * @author https://github.com/kuangcp on 2019-12-11 20:41 + */ +public class DateTimeFormatterTest { + + @Test + public void testFormat(){ + LocalDateTime now = LocalDateTime.now(); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + String dateStr = formatter.format(now); + System.out.println(dateStr); + } +} diff --git a/java8/src/test/java/com/github/kuangcp/time/DurationTest.java b/java8/src/test/java/com/github/kuangcp/time/DurationTest.java index 1459d2be..b1d25e90 100644 --- a/java8/src/test/java/com/github/kuangcp/time/DurationTest.java +++ b/java8/src/test/java/com/github/kuangcp/time/DurationTest.java @@ -12,15 +12,27 @@ public class DurationTest { @Test - public void testConvert(){ + public void testConvert() { Duration duration = Duration.ofHours(23); duration = duration.plus(59, ChronoUnit.MINUTES); log.info("duration={}", duration.toMinutes()); } - + @Test - public void testNegative(){ + public void testNegative() { Duration duration = Duration.ofMillis(-1000); System.out.println(duration.getSeconds()); } + + private String format(Duration duration) { + return duration.toHours() + ":" + duration.toMinutes() + ":" + duration.getSeconds() % 60; + } + + @Test + public void testFormat() { + for (int i = 0; i < 600; i++) { + Duration duration = Duration.ofMillis(i * 300); + log.info("{}", format(duration)); + } + } } From c752b63affcf45bdecc1eecb20ff0b70834c02b3 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Mon, 16 Dec 2019 07:55:32 +0800 Subject: [PATCH 134/476] +) lombok hashcode --- .../github/kuangcp/reflects/LombokTest.java | 60 +++++++++++++++++++ .../reflects/ReflectPerformanceTest.java | 1 + 2 files changed, 61 insertions(+) create mode 100644 class/src/test/java/com/github/kuangcp/reflects/LombokTest.java diff --git a/class/src/test/java/com/github/kuangcp/reflects/LombokTest.java b/class/src/test/java/com/github/kuangcp/reflects/LombokTest.java new file mode 100644 index 00000000..0035349c --- /dev/null +++ b/class/src/test/java/com/github/kuangcp/reflects/LombokTest.java @@ -0,0 +1,60 @@ +package com.github.kuangcp.reflects; + +import lombok.Data; +import lombok.ToString; +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; + +/** + * @author https://github.com/kuangcp on 2019-12-15 22:42 + */ +public class LombokTest { + + @Test + public void testHashCode() throws Exception { + App app = new App(); + System.out.println(app); + System.out.println(app.hashCode()); + + // hash use all field + // https://projectlombok.org/features/EqualsAndHashCode + Table table = new Table(); + System.out.println(table); + System.out.println(table.hashCode()); + } +} + + +@ToString +@Slf4j +class App { + + private String name; + + public String getName() { + log.info("getName"); + return name; + } + + public void setName(String name) { + log.info("setName"); + this.name = name; + } +} + +@Data +@Slf4j +class Table { + + private String name; + + public String getName() { + log.info("getName"); + return name; + } + + public void setName(String name) { + log.info("setName"); + this.name = name; + } +} \ No newline at end of file diff --git a/class/src/test/java/com/github/kuangcp/reflects/ReflectPerformanceTest.java b/class/src/test/java/com/github/kuangcp/reflects/ReflectPerformanceTest.java index b1f2f553..e8293882 100644 --- a/class/src/test/java/com/github/kuangcp/reflects/ReflectPerformanceTest.java +++ b/class/src/test/java/com/github/kuangcp/reflects/ReflectPerformanceTest.java @@ -11,6 +11,7 @@ /** * 反射的性能问题 http://www.cnblogs.com/zhishan/p/3195771.html * cglib(已缓存) 耗时 50%-70% 于缓存, 10% 于 原始方式 + * TODO 操作字节码方式取代反射 * * @author kuangcp */ From 9208389fb57cbb50d02d2f786a353c69f43384eb Mon Sep 17 00:00:00 2001 From: kcp Date: Tue, 17 Dec 2019 19:36:52 +0800 Subject: [PATCH 135/476] +) Decimal Format --- .../java/syntax/string/StringFormatTest.java | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 class/src/test/java/syntax/string/StringFormatTest.java diff --git a/class/src/test/java/syntax/string/StringFormatTest.java b/class/src/test/java/syntax/string/StringFormatTest.java new file mode 100644 index 00000000..6eea8c90 --- /dev/null +++ b/class/src/test/java/syntax/string/StringFormatTest.java @@ -0,0 +1,30 @@ +package syntax.string; + +import java.text.DecimalFormat; +import java.time.Duration; +import org.junit.Test; + +/** + * @author https://github.com/kuangcp on 2019-12-17 11:53 + */ +public class StringFormatTest { + + @Test + public void testDecimalFormat() { + DecimalFormat decimalFormat = new DecimalFormat("##"); + System.out.println(Float.parseFloat("0.512") * 100); + String format = decimalFormat.format(Float.parseFloat("0.14") * 100); + System.out.println(format); + } + + @Test + public void testPlaceholderFormat() { + long mills = 799239284; + Duration duration = Duration.ofMillis(mills); + + System.out.println(Math.ceil(2435 / 1000.0) % 60); + String result = String.format("%02d:%02d:%02d", duration.toHours(), duration.toMinutes() % 60, + (int)Math.ceil(mills / 1000.0) % 60); + System.out.println(result); + } +} From 23fa8a2eb21387efe90b63a99cb5e31c66f163f8 Mon Sep 17 00:00:00 2001 From: kcp Date: Thu, 19 Dec 2019 12:53:12 +0800 Subject: [PATCH 136/476] +) init static field --- .../instantiation/StaticFieldInit.java | 6 ++-- .../instantiation/StaticFieldInitTest.java | 34 ++++++++++++++++--- .../java/syntax/string/StringFormatTest.java | 7 +++- 3 files changed, 38 insertions(+), 9 deletions(-) diff --git a/class/src/main/java/com/github/kuangcp/instantiation/StaticFieldInit.java b/class/src/main/java/com/github/kuangcp/instantiation/StaticFieldInit.java index be313353..d7cecdff 100644 --- a/class/src/main/java/com/github/kuangcp/instantiation/StaticFieldInit.java +++ b/class/src/main/java/com/github/kuangcp/instantiation/StaticFieldInit.java @@ -1,10 +1,8 @@ package com.github.kuangcp.instantiation; /** - * 看起来 count 似乎是先使用再声明的 - * 静态变量是类加载时期就分配到了数据区, 在内存中只有一个, 不会分配多次, 其后所有的操作都是值改变, 地址不会变 ?? - * 类初始化的时候, 先去查找类中的所有静态声明, 然后分配空间, 这时候只有空间, 还没有赋值, 然后依据代码的顺序执行, 进行赋值 - * IDEA 里面避免这个问题是比较容易的, 会有明显的警告 + * 看起来 count 似乎是先使用再声明的 静态变量是类加载时期就分配到了数据区, 在内存中只有一个, 不会分配多次, 其后所有的操作都是值改变, 地址不会变 ?? 类初始化的时候, + * 先去查找类中的所有静态声明, 然后分配空间, 这时候只有空间, 还没有赋值, 然后依据代码的顺序执行, 进行赋值 IDEA 里面避免这个问题是比较容易的, 会有明显的警告 * * @author https://github.com/kuangcp * @date 2019-05-15 09:03 diff --git a/class/src/test/java/com/github/kuangcp/instantiation/StaticFieldInitTest.java b/class/src/test/java/com/github/kuangcp/instantiation/StaticFieldInitTest.java index ebfb52fd..bbe9ce1c 100644 --- a/class/src/test/java/com/github/kuangcp/instantiation/StaticFieldInitTest.java +++ b/class/src/test/java/com/github/kuangcp/instantiation/StaticFieldInitTest.java @@ -3,18 +3,44 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.IsEqual.equalTo; +import lombok.extern.slf4j.Slf4j; +import org.junit.Assert; import org.junit.Test; /** * @author https://github.com/kuangcp * @date 2019-05-15 09:11 */ - +@Slf4j public class StaticFieldInitTest { @Test - public void test(){ - assertThat(StaticFieldInit.num , equalTo(2)); - assertThat(StaticFieldInit.count , equalTo(1)); + public void testInitStaticFieldSort() { + assertThat(StaticFieldInit.num, equalTo(2)); + assertThat(StaticFieldInit.count, equalTo(1)); + } + + @Test + public void testInitStaticField() { + System.out.println("init class?"); + System.out.println(InitError.class); + + try { + System.out.println("init class"); + System.out.println(InitError.num); + Assert.fail(); + } catch (Error e) { + log.error("", e); + } } } + +class InitError { + + static int num = 0; + static int version = initVersion(); + + static int initVersion() { + return 3 / 0; + } +} \ No newline at end of file diff --git a/class/src/test/java/syntax/string/StringFormatTest.java b/class/src/test/java/syntax/string/StringFormatTest.java index 6eea8c90..6e6d4d5b 100644 --- a/class/src/test/java/syntax/string/StringFormatTest.java +++ b/class/src/test/java/syntax/string/StringFormatTest.java @@ -24,7 +24,12 @@ public void testPlaceholderFormat() { System.out.println(Math.ceil(2435 / 1000.0) % 60); String result = String.format("%02d:%02d:%02d", duration.toHours(), duration.toMinutes() % 60, - (int)Math.ceil(mills / 1000.0) % 60); + (int) Math.ceil(mills / 1000.0) % 60); System.out.println(result); } + + @Test + public void testCaculate() { + System.out.println((20 * 10000) / 30); + } } From b9f2426fab28eecff1fc17788fddb87e9f739f4a Mon Sep 17 00:00:00 2001 From: kcp Date: Wed, 25 Dec 2019 21:11:52 +0800 Subject: [PATCH 137/476] +) thread resources run out --- class/src/main/java/jvm/oom/ThreadOOM.java | 33 ++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 class/src/main/java/jvm/oom/ThreadOOM.java diff --git a/class/src/main/java/jvm/oom/ThreadOOM.java b/class/src/main/java/jvm/oom/ThreadOOM.java new file mode 100644 index 00000000..f02315fd --- /dev/null +++ b/class/src/main/java/jvm/oom/ThreadOOM.java @@ -0,0 +1,33 @@ +package jvm.oom; + +import java.util.concurrent.LinkedBlockingDeque; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +/** + * set max thread: echo 1000 > /proc/sys/kernel/threads-max + * + * OutOfMemoryError: unable to create new native thread + * + * @author https://github.com/kuangcp on 2019-12-25 20:05 + */ +public class ThreadOOM { + + public static void main(String[] args) { + + while (true) { + ThreadPoolExecutor pool = new ThreadPoolExecutor(16, 20, 2, TimeUnit.MINUTES, + new LinkedBlockingDeque<>()); + + pool.submit(() -> { + int a = 1; + }); + + try { + Thread.sleep(10); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } +} From 357c928e298ab927daff8b7d3ccbd28fecb470f5 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Sun, 29 Dec 2019 23:58:08 +0800 Subject: [PATCH 138/476] +) min --- .../kuangcp/strcuture/stackapp/GetMin.java | 63 +++++++++++++++++++ .../strcuture/stackapp/GetMinTest.java | 37 +++++++++++ 2 files changed, 100 insertions(+) create mode 100644 algorithms/src/main/java/com/github/kuangcp/strcuture/stackapp/GetMin.java create mode 100644 algorithms/src/test/java/com/github/kuangcp/strcuture/stackapp/GetMinTest.java diff --git a/algorithms/src/main/java/com/github/kuangcp/strcuture/stackapp/GetMin.java b/algorithms/src/main/java/com/github/kuangcp/strcuture/stackapp/GetMin.java new file mode 100644 index 00000000..a19b737a --- /dev/null +++ b/algorithms/src/main/java/com/github/kuangcp/strcuture/stackapp/GetMin.java @@ -0,0 +1,63 @@ +package com.github.kuangcp.strcuture.stackapp; + +import java.util.Objects; +import java.util.Stack; +import lombok.Data; + +/** + * @author https://github.com/kuangcp on 2019-12-29 16:45 + */ +@Data +public class GetMin { + + private Stack data = new Stack<>(); + private Stack min = new Stack<>(); + + void pushA(int value) { + data.push(value); + if (min.isEmpty()) { + min.push(value); + } else if (min.peek() >= value) { + min.push(value); + } + } + + int popA() { + if (data.isEmpty()) { + return -1; + } + + Integer value = data.pop(); + if (!min.isEmpty() && Objects.equals(value, min.peek())) { + min.pop(); + } + return value; + } + + int getMin() { + return min.peek(); + } + + void pushB(int value) { + data.push(value); + if (min.isEmpty()) { + min.push(value); + } else if (min.peek() >= value) { + min.push(value); + } else { + min.push(min.peek()); + } + } + + int popB() { + if (!min.isEmpty()) { + min.pop(); + } + + if (!data.isEmpty()) { + return data.pop(); + } + + return -1; + } +} diff --git a/algorithms/src/test/java/com/github/kuangcp/strcuture/stackapp/GetMinTest.java b/algorithms/src/test/java/com/github/kuangcp/strcuture/stackapp/GetMinTest.java new file mode 100644 index 00000000..846e85a6 --- /dev/null +++ b/algorithms/src/test/java/com/github/kuangcp/strcuture/stackapp/GetMinTest.java @@ -0,0 +1,37 @@ +package com.github.kuangcp.strcuture.stackapp; + +import static org.hamcrest.Matchers.equalTo; +import static org.junit.Assert.assertThat; + +import org.junit.Test; + +/** + * @author https://github.com/kuangcp on 2019-12-29 23:45 + */ +public class GetMinTest { + + + @Test + public void testA() throws Exception { + GetMin app = new GetMin(); + app.pushA(2); + app.pushA(0); + app.pushA(6); + app.pushA(10); + app.pushA(1); + app.pushA(0); + app.popA(); + + assertThat(app.getMin(), equalTo(0)); + } + + @Test + public void testB() throws Exception { + GetMin app = new GetMin(); + app.pushB(3); + app.pushB(2); + app.pushB(9); + app.popB(); + assertThat(app.getMin(), equalTo(2)); + } +} From bfbb69ff1509f64aff2cd8f603c8f34cee5c338c Mon Sep 17 00:00:00 2001 From: kcp Date: Wed, 8 Jan 2020 16:49:05 +0800 Subject: [PATCH 139/476] +) stream.of with null --- .../kuangcp/stream/CreateStreamTest.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 java8/src/test/java/com/github/kuangcp/stream/CreateStreamTest.java diff --git a/java8/src/test/java/com/github/kuangcp/stream/CreateStreamTest.java b/java8/src/test/java/com/github/kuangcp/stream/CreateStreamTest.java new file mode 100644 index 00000000..b3dac917 --- /dev/null +++ b/java8/src/test/java/com/github/kuangcp/stream/CreateStreamTest.java @@ -0,0 +1,20 @@ +package com.github.kuangcp.stream; + +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.junit.Test; + +/** + * @author https://github.com/kuangcp on 2020-01-08 16:41 + */ +public class CreateStreamTest { + + @Test + public void testOfWithNull(){ + List list = Stream.of(null, "test", "name").filter(Objects::nonNull) + .collect(Collectors.toList()); + System.out.println(list); + } +} From 88814d375fbaa2e0cc216e3ebd151a97a3e27958 Mon Sep 17 00:00:00 2001 From: kcp Date: Thu, 16 Jan 2020 13:34:42 +0800 Subject: [PATCH 140/476] +) multicast --- .../com/github/kuangcp/multicast/One.java | 32 +++++++++++++++++ .../com/github/kuangcp/multicast/Two.java | 35 +++++++++++++++++++ 2 files changed, 67 insertions(+) create mode 100644 network/src/main/java/com/github/kuangcp/multicast/One.java create mode 100644 network/src/main/java/com/github/kuangcp/multicast/Two.java diff --git a/network/src/main/java/com/github/kuangcp/multicast/One.java b/network/src/main/java/com/github/kuangcp/multicast/One.java new file mode 100644 index 00000000..f2bed0cc --- /dev/null +++ b/network/src/main/java/com/github/kuangcp/multicast/One.java @@ -0,0 +1,32 @@ +package com.github.kuangcp.multicast; + +import java.net.DatagramPacket; +import java.net.InetAddress; +import java.net.MulticastSocket; +import lombok.extern.slf4j.Slf4j; + +/** + * @author https://github.com/kuangcp on 2020-01-15 20:30 + */ +@Slf4j +public class One { + + private static int port = 8000; + private static String address = "228.0.0.4"; + + public static void main(String[] args) { + try { + InetAddress group = InetAddress.getByName(address); + MulticastSocket multicastSocket = new MulticastSocket(port); + multicastSocket.joinGroup(group); + while (true) { + byte[] buffer = "One".getBytes(); + DatagramPacket packet = new DatagramPacket(buffer, buffer.length, group, port); + multicastSocket.send(packet); + Thread.sleep(1000); + } + } catch (Exception e) { + log.error("", e); + } + } +} diff --git a/network/src/main/java/com/github/kuangcp/multicast/Two.java b/network/src/main/java/com/github/kuangcp/multicast/Two.java new file mode 100644 index 00000000..fdda6d98 --- /dev/null +++ b/network/src/main/java/com/github/kuangcp/multicast/Two.java @@ -0,0 +1,35 @@ +package com.github.kuangcp.multicast; + +import java.net.DatagramPacket; +import java.net.InetAddress; +import java.net.MulticastSocket; +import lombok.extern.slf4j.Slf4j; + +/** + * @author https://github.com/kuangcp on 2020-01-15 20:34 + */ +@Slf4j +public class Two { + + private static int port = 8000; + private static String address = "228.0.0.4"; + + public static void main(String[] args) { + try { + InetAddress group = InetAddress.getByName(address); + MulticastSocket multicastSocket = new MulticastSocket(port); + multicastSocket.joinGroup(group); + byte[] buffer = new byte[1024]; + + while (true) { + DatagramPacket packet = new DatagramPacket(buffer, buffer.length); + multicastSocket.receive(packet); + + String str = new String(packet.getData(), 0, packet.getLength()); + log.info("str={}", str); + } + } catch (Exception e) { + log.error("", e); + } + } +} From 7127b29b0449a62410c80967f491c414344bdfcb Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Sun, 2 Feb 2020 22:40:19 +0800 Subject: [PATCH 141/476] +) add unsafe --- .../InstantiationAndConstructor.java | 4 +-- .../main/java/jmh/StringBuilderBenchmark.java | 10 ++----- .../InstantiationAndConstructorTest.java | 28 +++++++++++++++++-- .../java/jmh/StringBuilderBenchmarkTest.java | 2 +- .../syntax/base/method/ParamTransferTest.java | 6 ++-- 5 files changed, 34 insertions(+), 16 deletions(-) diff --git a/class/src/main/java/com/github/kuangcp/instantiation/InstantiationAndConstructor.java b/class/src/main/java/com/github/kuangcp/instantiation/InstantiationAndConstructor.java index a00136b8..5b9ba32d 100644 --- a/class/src/main/java/com/github/kuangcp/instantiation/InstantiationAndConstructor.java +++ b/class/src/main/java/com/github/kuangcp/instantiation/InstantiationAndConstructor.java @@ -24,12 +24,12 @@ class InstantiationAndConstructor implements Serializable, Cloneable { } public InstantiationAndConstructor() { - log.info("invoke empty constructor"); + log.warn("invoke empty constructor"); } public InstantiationAndConstructor(String name) { this.name = name; - log.info("invoke constructor(name): name={}", name); + log.warn("invoke constructor(name): name={}", name); } @Override diff --git a/class/src/main/java/jmh/StringBuilderBenchmark.java b/class/src/main/java/jmh/StringBuilderBenchmark.java index f7823a63..3b822bd6 100644 --- a/class/src/main/java/jmh/StringBuilderBenchmark.java +++ b/class/src/main/java/jmh/StringBuilderBenchmark.java @@ -15,8 +15,8 @@ */ @BenchmarkMode(Mode.Throughput) @Warmup(iterations = 3) -@Measurement(iterations = 10, time = 5) -@Threads(8) +@Measurement(iterations = 10, time = 1) +@Threads(4) @Fork(2) @OutputTimeUnit(TimeUnit.MILLISECONDS) public class StringBuilderBenchmark { @@ -27,7 +27,6 @@ public void testStringAdd() { for (int i = 0; i < 10; i++) { a += i; } - print(a); } @Benchmark @@ -36,9 +35,6 @@ public void testStringBuilderAdd() { for (int i = 0; i < 10; i++) { sb.append(i); } - print(sb.toString()); - } - - private void print(String a) { + sb.toString(); } } \ No newline at end of file diff --git a/class/src/test/java/com/github/kuangcp/instantiation/InstantiationAndConstructorTest.java b/class/src/test/java/com/github/kuangcp/instantiation/InstantiationAndConstructorTest.java index c3cf1882..00c2554e 100644 --- a/class/src/test/java/com/github/kuangcp/instantiation/InstantiationAndConstructorTest.java +++ b/class/src/test/java/com/github/kuangcp/instantiation/InstantiationAndConstructorTest.java @@ -9,20 +9,28 @@ import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.Constructor; +import java.lang.reflect.Field; import java.lang.reflect.Method; import lombok.extern.slf4j.Slf4j; import org.junit.Test; +import sun.misc.Unsafe; /** * @author kuangcp on 3/9/19-5:48 PM * - * 实例化和构造器的关系 + * 实例化 对象的方式 + * 1. new + * 1. 反射获取构造器调用 + * 1. clone + * 1. 反序列化 + * 1. Unsafe.allocateInstance * * clone 以及 serialize 创建对象时 不会调用构造器 */ @Slf4j public class InstantiationAndConstructorTest { + // 调用构造器 @Test public void testInitByNew() { InstantiationAndConstructor instance = new InstantiationAndConstructor("name"); @@ -30,11 +38,13 @@ public void testInitByNew() { } // 反射实例化对象方式 实际上调用的空构造器 + // 如果空构造器不存在或者 私有了 就会抛出IllegalAccessException异常 @Test - public void testInitByReflect1() throws IllegalAccessException, InstantiationException { + public void testInitByReflectEmpty() throws IllegalAccessException, InstantiationException { InstantiationAndConstructor.class.newInstance(); } + // 反射实例化对象方式 获取指定构造器 @Test public void testInitByReflect() throws ReflectiveOperationException { Constructor constructor = @@ -50,6 +60,7 @@ public void testInitByReflect() throws ReflectiveOperationException { assertThat(domain.getName(), equalTo(name)); } + // clone 实例化对象 不会调用构造器 @Test public void testInitByClone() throws CloneNotSupportedException { String name = "clone"; @@ -63,6 +74,7 @@ public void testInitByClone() throws CloneNotSupportedException { assertThat(clone.getName(), equalTo("clone")); } + // 反序列化不会调用构造器 @Test public void testInitByDeserialize() throws IOException, ClassNotFoundException { InstantiationAndConstructor origin = new InstantiationAndConstructor("name"); @@ -80,6 +92,18 @@ public void testInitByDeserialize() throws IOException, ClassNotFoundException { InstantiationAndConstructor deserialize = (InstantiationAndConstructor) input.readObject(); assertThat(deserialize.getName(), equalTo("name")); + } + // Unsafe allocateInstance 方法 + @Test + public void testInitByUnsafe() throws Exception { + Field field = Unsafe.class.getDeclaredField("theUnsafe"); + field.setAccessible(true); + Unsafe unsafe = (Unsafe) field.get(null); + InstantiationAndConstructor instance = (InstantiationAndConstructor) unsafe + .allocateInstance(InstantiationAndConstructor.class); + assertThat(instance.getName(), equalTo(null)); + instance.setName("name"); + assertThat(instance.getName(), equalTo("name")); } } diff --git a/class/src/test/java/jmh/StringBuilderBenchmarkTest.java b/class/src/test/java/jmh/StringBuilderBenchmarkTest.java index ec57f585..1614fe66 100644 --- a/class/src/test/java/jmh/StringBuilderBenchmarkTest.java +++ b/class/src/test/java/jmh/StringBuilderBenchmarkTest.java @@ -15,7 +15,7 @@ public class StringBuilderBenchmarkTest { public void test() throws RunnerException { Options options = new OptionsBuilder() .include(StringBuilderBenchmark.class.getSimpleName()) - .output("/home/kcp/test/jmh/Benchmark.log").build(); + .output("/tmp/Benchmark.log").build(); new Runner(options).run(); } } diff --git a/class/src/test/java/syntax/base/method/ParamTransferTest.java b/class/src/test/java/syntax/base/method/ParamTransferTest.java index 985e057f..c2692872 100644 --- a/class/src/test/java/syntax/base/method/ParamTransferTest.java +++ b/class/src/test/java/syntax/base/method/ParamTransferTest.java @@ -12,17 +12,17 @@ */ public class ParamTransferTest { - class A { + static class A { private String name; } - // 值传递 private void modifyName(A a) { a.name = "2"; assertThat(a.name, equalTo("2")); + // 只更改到形参不能更改实参引用指向 a = new A(); a.name = "3"; assertThat(a.name, equalTo("3")); @@ -36,6 +36,4 @@ public void testMain() { modifyName(a); assertThat(a.name, equalTo("2")); } - - } From d63d8c2951aee71971f35e060ce7294286ae54ae Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Wed, 5 Feb 2020 21:13:29 +0800 Subject: [PATCH 142/476] +) virus --- .../kuangcp/virusbroadcast/Hospital.java | 73 ++++++++ .../kuangcp/virusbroadcast/PersonPool.java | 38 ++++ .../github/kuangcp/virusbroadcast/Readme.md | 1 + .../virusbroadcast/VirusBroadcast.java | 35 ++++ .../virusbroadcast/constant/Constants.java | 12 ++ .../virusbroadcast/constant/PersonState.java | 23 +++ .../kuangcp/virusbroadcast/domain/Bed.java | 13 ++ .../kuangcp/virusbroadcast/domain/City.java | 14 ++ .../virusbroadcast/domain/MoveTarget.java | 19 ++ .../kuangcp/virusbroadcast/domain/Person.java | 170 ++++++++++++++++++ .../kuangcp/virusbroadcast/domain/Point.java | 17 ++ .../kuangcp/virusbroadcast/domain/Virus.java | 8 + .../virusbroadcast/gui/DisplayPanel.java | 83 +++++++++ 13 files changed, 506 insertions(+) create mode 100644 gui/src/main/java/com/github/kuangcp/virusbroadcast/Hospital.java create mode 100644 gui/src/main/java/com/github/kuangcp/virusbroadcast/PersonPool.java create mode 100644 gui/src/main/java/com/github/kuangcp/virusbroadcast/Readme.md create mode 100644 gui/src/main/java/com/github/kuangcp/virusbroadcast/VirusBroadcast.java create mode 100644 gui/src/main/java/com/github/kuangcp/virusbroadcast/constant/Constants.java create mode 100644 gui/src/main/java/com/github/kuangcp/virusbroadcast/constant/PersonState.java create mode 100644 gui/src/main/java/com/github/kuangcp/virusbroadcast/domain/Bed.java create mode 100644 gui/src/main/java/com/github/kuangcp/virusbroadcast/domain/City.java create mode 100644 gui/src/main/java/com/github/kuangcp/virusbroadcast/domain/MoveTarget.java create mode 100644 gui/src/main/java/com/github/kuangcp/virusbroadcast/domain/Person.java create mode 100644 gui/src/main/java/com/github/kuangcp/virusbroadcast/domain/Point.java create mode 100644 gui/src/main/java/com/github/kuangcp/virusbroadcast/domain/Virus.java create mode 100644 gui/src/main/java/com/github/kuangcp/virusbroadcast/gui/DisplayPanel.java diff --git a/gui/src/main/java/com/github/kuangcp/virusbroadcast/Hospital.java b/gui/src/main/java/com/github/kuangcp/virusbroadcast/Hospital.java new file mode 100644 index 00000000..2892eb14 --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/virusbroadcast/Hospital.java @@ -0,0 +1,73 @@ +package com.github.kuangcp.virusbroadcast; + +import com.github.kuangcp.virusbroadcast.constant.Constants; +import com.github.kuangcp.virusbroadcast.domain.Bed; +import com.github.kuangcp.virusbroadcast.domain.Point; +import java.util.ArrayList; +import java.util.List; + +/** + * + */ +public class Hospital { + + private int x = 800; + private int y = 110; + + private int width; + private int height = 606; + + public int getWidth() { + return width; + } + + + public int getHeight() { + return height; + } + + public int getX() { + return x; + } + + public int getY() { + return y; + } + + private static Hospital hospital = new Hospital(); + + public static Hospital getInstance() { + return hospital; + } + + private Point point = new Point(800, 100); + private List beds = new ArrayList<>(); + + private Hospital() { + if (Constants.BED_COUNT == 0) { + width = 0; + height = 0; + } + int column = Constants.BED_COUNT / 100; + width = column * 6; + + for (int i = 0; i < column; i++) { + + for (int j = 10; j <= 610; j += 6) { + Bed bed = new Bed(point.getX() + i * 6, point.getY() + j); + beds.add(bed); + + } + + } + } + + public Bed pickBed() { + for (Bed bed : beds) { + if (bed.isEmpty()) { + return bed; + } + } + return null; + } +} diff --git a/gui/src/main/java/com/github/kuangcp/virusbroadcast/PersonPool.java b/gui/src/main/java/com/github/kuangcp/virusbroadcast/PersonPool.java new file mode 100644 index 00000000..7127a85c --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/virusbroadcast/PersonPool.java @@ -0,0 +1,38 @@ +package com.github.kuangcp.virusbroadcast; + +import com.github.kuangcp.virusbroadcast.domain.City; +import com.github.kuangcp.virusbroadcast.domain.Person; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ThreadLocalRandom; + +/** + * + */ +public class PersonPool { + + private static PersonPool personPool = new PersonPool(); + + public static PersonPool getInstance() { + return personPool; + } + + public List personList = new ArrayList<>(); + + public List getPersonList() { + return personList; + } + + private PersonPool() { + City city = new City(400, 400); + for (int i = 0; i < 500; i++) { + int x = (int) (100 * ThreadLocalRandom.current().nextGaussian() + city.getCenterX()); + int y = (int) (100 * ThreadLocalRandom.current().nextGaussian() + city.getCenterY()); + if (x > 700) { + x = 700; + } + Person person = new Person(city, x, y); + personList.add(person); + } + } +} diff --git a/gui/src/main/java/com/github/kuangcp/virusbroadcast/Readme.md b/gui/src/main/java/com/github/kuangcp/virusbroadcast/Readme.md new file mode 100644 index 00000000..6cf2213f --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/virusbroadcast/Readme.md @@ -0,0 +1 @@ +> [Github 源地址](https://github.com/KikiLetGo/VirusBroadcast) \ No newline at end of file diff --git a/gui/src/main/java/com/github/kuangcp/virusbroadcast/VirusBroadcast.java b/gui/src/main/java/com/github/kuangcp/virusbroadcast/VirusBroadcast.java new file mode 100644 index 00000000..56ad6646 --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/virusbroadcast/VirusBroadcast.java @@ -0,0 +1,35 @@ +package com.github.kuangcp.virusbroadcast; + +import com.github.kuangcp.virusbroadcast.constant.Constants; +import com.github.kuangcp.virusbroadcast.domain.Person; +import com.github.kuangcp.virusbroadcast.gui.DisplayPanel; +import java.util.List; +import java.util.Random; +import javax.swing.JFrame; + +public class VirusBroadcast { + + public static void main(String[] args) { + DisplayPanel panel = new DisplayPanel(); + Thread panelThread = new Thread(panel); + JFrame frame = new JFrame(); + frame.add(panel); + frame.setSize(1000, 800); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + panelThread.start(); + + List people = PersonPool.getInstance().getPersonList(); + for (int i = 0; i < Constants.ORIGINAL_COUNT; i++) { + int index = new Random().nextInt(people.size() - 1); + Person person = people.get(index); + + while (person.isInfected()) { + index = new Random().nextInt(people.size() - 1); + person = people.get(index); + } + person.beInfected(); + } + } +} diff --git a/gui/src/main/java/com/github/kuangcp/virusbroadcast/constant/Constants.java b/gui/src/main/java/com/github/kuangcp/virusbroadcast/constant/Constants.java new file mode 100644 index 00000000..b0d88039 --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/virusbroadcast/constant/Constants.java @@ -0,0 +1,12 @@ +package com.github.kuangcp.virusbroadcast.constant; + +public interface Constants { + + int ORIGINAL_COUNT = 50;//初始感染数量 + float BROAD_RATE = 0.82f;//传播率 + float SHADOW_TIME = 140;//潜伏时间 + int HOSPITAL_RECEIVE_TIME = 10;//医院收治响应时间 + int BED_COUNT = 1000;//医院床位 + float INTENTION = 0.99f;//流动意向平均值 + float SAFE_DISTANCE = 2f; // 安全距离 +} diff --git a/gui/src/main/java/com/github/kuangcp/virusbroadcast/constant/PersonState.java b/gui/src/main/java/com/github/kuangcp/virusbroadcast/constant/PersonState.java new file mode 100644 index 00000000..e7ad54b5 --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/virusbroadcast/constant/PersonState.java @@ -0,0 +1,23 @@ +package com.github.kuangcp.virusbroadcast.constant; + +/** + * @author https://github.com/kuangcp on 2020-02-04 16:53 + */ +public interface PersonState { + + int NORMAL = 0; + + // 疑似 + int SUSPECTED = NORMAL + 1; + + // 潜伏期 + int SHADOW = NORMAL + 2; + + int CONFIRMED = NORMAL + 3; + + // 隔离 + int FREEZE = NORMAL + 4; + + // 治愈 + int CURED = NORMAL + 5; +} diff --git a/gui/src/main/java/com/github/kuangcp/virusbroadcast/domain/Bed.java b/gui/src/main/java/com/github/kuangcp/virusbroadcast/domain/Bed.java new file mode 100644 index 00000000..6a99c2e8 --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/virusbroadcast/domain/Bed.java @@ -0,0 +1,13 @@ +package com.github.kuangcp.virusbroadcast.domain; + +import lombok.Data; + +@Data +public class Bed extends Point { + + public Bed(int x, int y) { + super(x, y); + } + + private boolean isEmpty = true; +} diff --git a/gui/src/main/java/com/github/kuangcp/virusbroadcast/domain/City.java b/gui/src/main/java/com/github/kuangcp/virusbroadcast/domain/City.java new file mode 100644 index 00000000..6a3006ca --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/virusbroadcast/domain/City.java @@ -0,0 +1,14 @@ +package com.github.kuangcp.virusbroadcast.domain; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class City { + + private int centerX; + private int centerY; +} diff --git a/gui/src/main/java/com/github/kuangcp/virusbroadcast/domain/MoveTarget.java b/gui/src/main/java/com/github/kuangcp/virusbroadcast/domain/MoveTarget.java new file mode 100644 index 00000000..10b33636 --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/virusbroadcast/domain/MoveTarget.java @@ -0,0 +1,19 @@ +package com.github.kuangcp.virusbroadcast.domain; + +import lombok.Data; + +/** + * + */ +@Data +public class MoveTarget { + + private int x; + private int y; + private boolean arrived = false; + + public MoveTarget(int x, int y) { + this.x = x; + this.y = y; + } +} diff --git a/gui/src/main/java/com/github/kuangcp/virusbroadcast/domain/Person.java b/gui/src/main/java/com/github/kuangcp/virusbroadcast/domain/Person.java new file mode 100644 index 00000000..b44c81a7 --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/virusbroadcast/domain/Person.java @@ -0,0 +1,170 @@ +package com.github.kuangcp.virusbroadcast.domain; + +import static com.github.kuangcp.virusbroadcast.constant.Constants.SAFE_DISTANCE; + +import com.github.kuangcp.virusbroadcast.Hospital; +import com.github.kuangcp.virusbroadcast.PersonPool; +import com.github.kuangcp.virusbroadcast.constant.Constants; +import com.github.kuangcp.virusbroadcast.constant.PersonState; +import com.github.kuangcp.virusbroadcast.gui.DisplayPanel; +import java.util.List; +import java.util.concurrent.ThreadLocalRandom; +import lombok.Data; + +/** + * + */ +@Data +public class Person { + + private City city; + private int x; + private int y; + private MoveTarget moveTarget; + private int state = PersonState.NORMAL; + + int sig = 1; + int infectedTime = 0; + int confirmedTime = 0; + + double targetXU; + double targetYU; + double targetSig = 50; + + public Person(City city, int x, int y) { + this.city = city; + this.x = x; + this.y = y; + targetXU = 100 * ThreadLocalRandom.current().nextGaussian() + x; + targetYU = 100 * ThreadLocalRandom.current().nextGaussian() + y; + } + + public boolean wantMove() { + double value = sig * ThreadLocalRandom.current().nextGaussian() + Constants.INTENTION; + return value > 0; + } + + public boolean isInfected() { + return state >= PersonState.SHADOW; + } + + public void beInfected() { + state = PersonState.SHADOW; + infectedTime = DisplayPanel.worldTime; + } + + /** + * 是否感染 + */ + public boolean willInfected(Person person) { + if (person.getState() == PersonState.NORMAL) { + return false; + } + float random = ThreadLocalRandom.current().nextFloat(); + return random < Constants.BROAD_RATE && distance(person) < SAFE_DISTANCE; + } + + public double distance(Person person) { + return Math.sqrt(Math.pow(x - person.getX(), 2) + Math.pow(y - person.getY(), 2)); + } + + private void freezy() { + state = PersonState.FREEZE; + } + + private void moveTo(int x, int y) { + this.x += x; + this.y += y; + } + + // 随机移动 + private void action() { + if (state == PersonState.FREEZE) { + return; + } + + if (!wantMove()) { + return; + } + if (moveTarget == null || moveTarget.isArrived()) { + + double targetX = targetSig * ThreadLocalRandom.current().nextGaussian() + targetXU; + double targetY = targetSig * ThreadLocalRandom.current().nextGaussian() + targetYU; + moveTarget = new MoveTarget((int) targetX, (int) targetY); + + } + + int dX = moveTarget.getX() - x; + int dY = moveTarget.getY() - y; + double length = Math.sqrt(Math.pow(dX, 2) + Math.pow(dY, 2)); + + if (length < 1) { + moveTarget.setArrived(true); + return; + } + int udX = (int) (dX / length); + if (udX == 0 && dX != 0) { + if (dX > 0) { + udX = 1; + } else { + udX = -1; + } + } + int udY = (int) (dY / length); + if (udY == 0 && udY != 0) { + if (dY > 0) { + udY = 1; + } else { + udY = -1; + } + } + + if (x > 700) { + moveTarget = null; + if (udX > 0) { + udX = -udX; + } + } + moveTo(udX, udY); + } + + public void update() { + //TODO 找时间改为状态机 + if (state >= PersonState.FREEZE) { + return; + } + if (state == PersonState.CONFIRMED + && DisplayPanel.worldTime - confirmedTime >= Constants.HOSPITAL_RECEIVE_TIME) { + Bed bed = Hospital.getInstance().pickBed(); + if (bed == null) { +// System.out.println("隔离区没有空床位"); + } else { + state = PersonState.FREEZE; + x = bed.getX(); + y = bed.getY(); + bed.setEmpty(false); + } + } + if (DisplayPanel.worldTime - infectedTime > Constants.SHADOW_TIME + && state == PersonState.SHADOW) { + state = PersonState.CONFIRMED; + confirmedTime = DisplayPanel.worldTime; + } + + this.action(); + + List people = PersonPool.getInstance().personList; + if (state >= PersonState.SHADOW) { + return; + } + for (Person person : people) { + if (person.getState() == PersonState.NORMAL) { + continue; + } + + if (this.willInfected(person)) { + this.beInfected(); + } + } + } +} diff --git a/gui/src/main/java/com/github/kuangcp/virusbroadcast/domain/Point.java b/gui/src/main/java/com/github/kuangcp/virusbroadcast/domain/Point.java new file mode 100644 index 00000000..1c378b76 --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/virusbroadcast/domain/Point.java @@ -0,0 +1,17 @@ +package com.github.kuangcp.virusbroadcast.domain; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class Point { + + private int x; + private int y; +} diff --git a/gui/src/main/java/com/github/kuangcp/virusbroadcast/domain/Virus.java b/gui/src/main/java/com/github/kuangcp/virusbroadcast/domain/Virus.java new file mode 100644 index 00000000..ac9c59b5 --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/virusbroadcast/domain/Virus.java @@ -0,0 +1,8 @@ +package com.github.kuangcp.virusbroadcast.domain; + +/** + * + */ +public class Virus { + +} diff --git a/gui/src/main/java/com/github/kuangcp/virusbroadcast/gui/DisplayPanel.java b/gui/src/main/java/com/github/kuangcp/virusbroadcast/gui/DisplayPanel.java new file mode 100644 index 00000000..1f3100d0 --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/virusbroadcast/gui/DisplayPanel.java @@ -0,0 +1,83 @@ +package com.github.kuangcp.virusbroadcast.gui; + +import com.github.kuangcp.virusbroadcast.Hospital; +import com.github.kuangcp.virusbroadcast.domain.Person; +import com.github.kuangcp.virusbroadcast.PersonPool; +import com.github.kuangcp.virusbroadcast.constant.PersonState; +import java.awt.Color; +import java.awt.Graphics; +import java.util.List; +import javax.swing.JPanel; +import lombok.extern.slf4j.Slf4j; + +/** + * + */ +@Slf4j +public class DisplayPanel extends JPanel implements Runnable { + + /** + * 时间线 day + */ + public static int worldTime = 0; + + public DisplayPanel() { + this.setBackground(new Color(0x444444)); + } + + @Override + public void paint(Graphics arg0) { + super.paint(arg0); + //draw border + arg0.setColor(new Color(0x00ff00)); + arg0.drawRect(Hospital.getInstance().getX(), Hospital.getInstance().getY(), + Hospital.getInstance().getWidth(), Hospital.getInstance().getHeight()); + + List people = PersonPool.getInstance().getPersonList(); + if (people == null) { + return; + } + int pIndex = 0; + people.get(pIndex).update(); + for (Person person : people) { + + switch (person.getState()) { + case PersonState.NORMAL: { + arg0.setColor(new Color(0xdddddd)); + + } + break; + case PersonState.SHADOW: { + arg0.setColor(new Color(0xffee00)); + + } + break; + case PersonState.CONFIRMED: + case PersonState.FREEZE: { + arg0.setColor(new Color(0xff0000)); + + } + break; + } + person.update(); + arg0.fillOval(person.getX(), person.getY(), 3, 3); + } + pIndex++; + if (pIndex >= people.size()) { + pIndex = 0; + } + } + + @Override + public void run() { + while (true) { + this.repaint(); + try { + Thread.sleep(100); + worldTime++; + } catch (InterruptedException e) { + log.error("", e); + } + } + } +} From bd3be17a9708f60000cfc580d8db39e1d7017a06 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Fri, 14 Feb 2020 13:19:03 +0800 Subject: [PATCH 143/476] +) add cure logic, person count monitor --- .../kuangcp/virusbroadcast/Hospital.java | 70 ++++---- .../kuangcp/virusbroadcast/PersonPool.java | 38 ---- .../virusbroadcast/VirusBroadcast.java | 14 +- .../virusbroadcast/constant/Constants.java | 10 +- .../virusbroadcast/constant/PersonState.java | 26 +-- .../constant/PersonStateEnum.java | 40 +++++ .../kuangcp/virusbroadcast/domain/Bed.java | 3 + .../kuangcp/virusbroadcast/domain/City.java | 94 +++++++++- .../kuangcp/virusbroadcast/domain/Person.java | 167 +++++++++++++----- .../virusbroadcast/gui/DisplayPanel.java | 87 +++++---- 10 files changed, 374 insertions(+), 175 deletions(-) delete mode 100644 gui/src/main/java/com/github/kuangcp/virusbroadcast/PersonPool.java create mode 100644 gui/src/main/java/com/github/kuangcp/virusbroadcast/constant/PersonStateEnum.java diff --git a/gui/src/main/java/com/github/kuangcp/virusbroadcast/Hospital.java b/gui/src/main/java/com/github/kuangcp/virusbroadcast/Hospital.java index 2892eb14..6efb4b51 100644 --- a/gui/src/main/java/com/github/kuangcp/virusbroadcast/Hospital.java +++ b/gui/src/main/java/com/github/kuangcp/virusbroadcast/Hospital.java @@ -9,41 +9,17 @@ /** * */ -public class Hospital { +public enum Hospital { + + INSTANCE; private int x = 800; private int y = 110; - private int width; private int height = 606; - - public int getWidth() { - return width; - } - - - public int getHeight() { - return height; - } - - public int getX() { - return x; - } - - public int getY() { - return y; - } - - private static Hospital hospital = new Hospital(); - - public static Hospital getInstance() { - return hospital; - } - - private Point point = new Point(800, 100); private List beds = new ArrayList<>(); - private Hospital() { + Hospital() { if (Constants.BED_COUNT == 0) { width = 0; height = 0; @@ -51,14 +27,12 @@ private Hospital() { int column = Constants.BED_COUNT / 100; width = column * 6; + Point offset = new Point(800, 100); for (int i = 0; i < column; i++) { - for (int j = 10; j <= 610; j += 6) { - Bed bed = new Bed(point.getX() + i * 6, point.getY() + j); + Bed bed = new Bed(offset.getX() + i * 6, offset.getY() + j); beds.add(bed); - } - } } @@ -70,4 +44,36 @@ public Bed pickBed() { } return null; } + + public int getX() { + return x; + } + + public void setX(int x) { + this.x = x; + } + + public int getY() { + return y; + } + + public void setY(int y) { + this.y = y; + } + + public int getWidth() { + return width; + } + + public void setWidth(int width) { + this.width = width; + } + + public int getHeight() { + return height; + } + + public void setHeight(int height) { + this.height = height; + } } diff --git a/gui/src/main/java/com/github/kuangcp/virusbroadcast/PersonPool.java b/gui/src/main/java/com/github/kuangcp/virusbroadcast/PersonPool.java deleted file mode 100644 index 7127a85c..00000000 --- a/gui/src/main/java/com/github/kuangcp/virusbroadcast/PersonPool.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.github.kuangcp.virusbroadcast; - -import com.github.kuangcp.virusbroadcast.domain.City; -import com.github.kuangcp.virusbroadcast.domain.Person; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.ThreadLocalRandom; - -/** - * - */ -public class PersonPool { - - private static PersonPool personPool = new PersonPool(); - - public static PersonPool getInstance() { - return personPool; - } - - public List personList = new ArrayList<>(); - - public List getPersonList() { - return personList; - } - - private PersonPool() { - City city = new City(400, 400); - for (int i = 0; i < 500; i++) { - int x = (int) (100 * ThreadLocalRandom.current().nextGaussian() + city.getCenterX()); - int y = (int) (100 * ThreadLocalRandom.current().nextGaussian() + city.getCenterY()); - if (x > 700) { - x = 700; - } - Person person = new Person(city, x, y); - personList.add(person); - } - } -} diff --git a/gui/src/main/java/com/github/kuangcp/virusbroadcast/VirusBroadcast.java b/gui/src/main/java/com/github/kuangcp/virusbroadcast/VirusBroadcast.java index 56ad6646..04df61c5 100644 --- a/gui/src/main/java/com/github/kuangcp/virusbroadcast/VirusBroadcast.java +++ b/gui/src/main/java/com/github/kuangcp/virusbroadcast/VirusBroadcast.java @@ -1,26 +1,34 @@ package com.github.kuangcp.virusbroadcast; import com.github.kuangcp.virusbroadcast.constant.Constants; +import com.github.kuangcp.virusbroadcast.domain.City; import com.github.kuangcp.virusbroadcast.domain.Person; import com.github.kuangcp.virusbroadcast.gui.DisplayPanel; import java.util.List; import java.util.Random; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import javax.swing.JFrame; public class VirusBroadcast { + private static ExecutorService executors = Executors.newFixedThreadPool(2); + public static void main(String[] args) { + DisplayPanel panel = new DisplayPanel(); - Thread panelThread = new Thread(panel); JFrame frame = new JFrame(); frame.add(panel); frame.setSize(1000, 800); frame.setLocationRelativeTo(null); frame.setVisible(true); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - panelThread.start(); - List people = PersonPool.getInstance().getPersonList(); + executors.submit(panel); + executors.submit(City.getInstance()); + + // 初始感染 + List people = City.getInstance().getPersonList(); for (int i = 0; i < Constants.ORIGINAL_COUNT; i++) { int index = new Random().nextInt(people.size() - 1); Person person = people.get(index); diff --git a/gui/src/main/java/com/github/kuangcp/virusbroadcast/constant/Constants.java b/gui/src/main/java/com/github/kuangcp/virusbroadcast/constant/Constants.java index b0d88039..b1f7f3fa 100644 --- a/gui/src/main/java/com/github/kuangcp/virusbroadcast/constant/Constants.java +++ b/gui/src/main/java/com/github/kuangcp/virusbroadcast/constant/Constants.java @@ -2,11 +2,17 @@ public interface Constants { + int BASE_RATE = 10000; + int ORIGINAL_COUNT = 50;//初始感染数量 float BROAD_RATE = 0.82f;//传播率 float SHADOW_TIME = 140;//潜伏时间 int HOSPITAL_RECEIVE_TIME = 10;//医院收治响应时间 - int BED_COUNT = 1000;//医院床位 - float INTENTION = 0.99f;//流动意向平均值 + int BED_COUNT = 500;//医院床位 + float INTENTION = 0.49f;//流动意向平均值 float SAFE_DISTANCE = 2f; // 安全距离 + int CITY_PERSON_SCALE = 6000; + + int DEAD_RATE = 1000; // 每天可能死亡率 万分比 + int CURE_RATE = 100;// 每天可能治愈率 万分比 } diff --git a/gui/src/main/java/com/github/kuangcp/virusbroadcast/constant/PersonState.java b/gui/src/main/java/com/github/kuangcp/virusbroadcast/constant/PersonState.java index e7ad54b5..a1d0694e 100644 --- a/gui/src/main/java/com/github/kuangcp/virusbroadcast/constant/PersonState.java +++ b/gui/src/main/java/com/github/kuangcp/virusbroadcast/constant/PersonState.java @@ -7,17 +7,23 @@ public interface PersonState { int NORMAL = 0; - // 疑似 - int SUSPECTED = NORMAL + 1; + /** + * 潜伏期 + */ + int SHADOW = NORMAL + 1; - // 潜伏期 - int SHADOW = NORMAL + 2; + /** + * 确诊 + */ + int CONFIRMED = NORMAL + 2; - int CONFIRMED = NORMAL + 3; + /** + * 隔离 + */ + int FREEZE = NORMAL + 3; - // 隔离 - int FREEZE = NORMAL + 4; - - // 治愈 - int CURED = NORMAL + 5; + /** + * 死亡 + */ + int DEAD = NORMAL + 4; } diff --git a/gui/src/main/java/com/github/kuangcp/virusbroadcast/constant/PersonStateEnum.java b/gui/src/main/java/com/github/kuangcp/virusbroadcast/constant/PersonStateEnum.java new file mode 100644 index 00000000..b0652788 --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/virusbroadcast/constant/PersonStateEnum.java @@ -0,0 +1,40 @@ +package com.github.kuangcp.virusbroadcast.constant; + +import java.util.Optional; + +/** + * @author https://github.com/kuangcp on 2020-02-14 12:43 + */ +public enum PersonStateEnum { + + NORMAL(PersonState.NORMAL, "normal"), + SHADOW(PersonState.SHADOW, "shadow"), + CONFIRMED(PersonState.CONFIRMED, "confirmed"), + FREEZE(PersonState.FREEZE, "freeze"), + DEAD(PersonState.DEAD, "dead"); + + private int value; + private String desc; + + public int getValue() { + return value; + } + + public String getDesc() { + return desc; + } + + PersonStateEnum(int value, String desc) { + this.value = value; + this.desc = desc; + } + + public static Optional getByValue(int value) { + for (PersonStateEnum stateEnum : values()) { + if (stateEnum.value == value) { + return Optional.of(stateEnum); + } + } + return Optional.empty(); + } +} diff --git a/gui/src/main/java/com/github/kuangcp/virusbroadcast/domain/Bed.java b/gui/src/main/java/com/github/kuangcp/virusbroadcast/domain/Bed.java index 6a99c2e8..895ef24a 100644 --- a/gui/src/main/java/com/github/kuangcp/virusbroadcast/domain/Bed.java +++ b/gui/src/main/java/com/github/kuangcp/virusbroadcast/domain/Bed.java @@ -1,8 +1,11 @@ package com.github.kuangcp.virusbroadcast.domain; import lombok.Data; +import lombok.EqualsAndHashCode; + @Data +@EqualsAndHashCode(callSuper = true) public class Bed extends Point { public Bed(int x, int y) { diff --git a/gui/src/main/java/com/github/kuangcp/virusbroadcast/domain/City.java b/gui/src/main/java/com/github/kuangcp/virusbroadcast/domain/City.java index 6a3006ca..bf602165 100644 --- a/gui/src/main/java/com/github/kuangcp/virusbroadcast/domain/City.java +++ b/gui/src/main/java/com/github/kuangcp/virusbroadcast/domain/City.java @@ -1,14 +1,90 @@ package com.github.kuangcp.virusbroadcast.domain; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; +import static com.github.kuangcp.virusbroadcast.constant.Constants.CITY_PERSON_SCALE; +import static com.github.kuangcp.virusbroadcast.constant.Constants.ORIGINAL_COUNT; -@Data -@NoArgsConstructor -@AllArgsConstructor -public class City { +import com.github.kuangcp.virusbroadcast.constant.PersonState; +import com.github.kuangcp.virusbroadcast.constant.PersonStateEnum; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Optional; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import lombok.extern.slf4j.Slf4j; - private int centerX; - private int centerY; +@Slf4j +public class City implements Runnable { + + private int centerX = 400; + private int centerY = 400; + private int personCount = CITY_PERSON_SCALE; + + private static Map counts; + + static { + counts = Stream.of(PersonStateEnum.values()) + .collect(Collectors.toMap(PersonStateEnum::getValue, v -> new AtomicInteger(), + (front, current) -> current)); + counts.get(PersonState.NORMAL).set(CITY_PERSON_SCALE - ORIGINAL_COUNT); + } + + private static City city = new City(); + public List personList = new CopyOnWriteArrayList<>(); + + public static City getInstance() { + return city; + } + + public List getPersonList() { + return personList; + } + + public void dead(Person person) { + personList.remove(person); + } + + public static void trans(int from, int to) { + AtomicInteger fromCount = counts.get(from); + AtomicInteger toCount = counts.get(to); + toCount.incrementAndGet(); + fromCount.decrementAndGet(); + } + + private City() { + for (int i = 0; i < this.personCount; i++) { + int x = (int) (100 * ThreadLocalRandom.current().nextGaussian() + centerX); + int y = (int) (100 * ThreadLocalRandom.current().nextGaussian() + centerY); + if (x > 700) { + x = 700; + } + Person person = new Person(x, y); + personList.add(person); + } + } + + @Override + public void run() { + while (true) { + try { + Thread.sleep(1000); + showCityInfo(); + } catch (Exception e) { + log.error("", e); + } + } + } + + public void showCityInfo() { + StringBuilder temp = new StringBuilder(); + for (Entry entry : counts.entrySet()) { + Optional stateOpt = PersonStateEnum.getByValue(entry.getKey()); + stateOpt.ifPresent(personStateEnum -> temp + .append(String.format("%6s: %5d ", personStateEnum.getDesc(), entry.getValue().get()))); + } + log.info("{}", temp.toString()); + } } diff --git a/gui/src/main/java/com/github/kuangcp/virusbroadcast/domain/Person.java b/gui/src/main/java/com/github/kuangcp/virusbroadcast/domain/Person.java index b44c81a7..01782d38 100644 --- a/gui/src/main/java/com/github/kuangcp/virusbroadcast/domain/Person.java +++ b/gui/src/main/java/com/github/kuangcp/virusbroadcast/domain/Person.java @@ -3,36 +3,47 @@ import static com.github.kuangcp.virusbroadcast.constant.Constants.SAFE_DISTANCE; import com.github.kuangcp.virusbroadcast.Hospital; -import com.github.kuangcp.virusbroadcast.PersonPool; import com.github.kuangcp.virusbroadcast.constant.Constants; import com.github.kuangcp.virusbroadcast.constant.PersonState; import com.github.kuangcp.virusbroadcast.gui.DisplayPanel; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.Objects; import java.util.concurrent.ThreadLocalRandom; +import java.util.function.Consumer; import lombok.Data; +import lombok.extern.slf4j.Slf4j; /** * */ @Data +@Slf4j public class Person { - private City city; + private Bed bed; private int x; private int y; private MoveTarget moveTarget; private int state = PersonState.NORMAL; + private static Map> functions = new HashMap<>(); int sig = 1; + /** + * 被感染时间 + */ int infectedTime = 0; + /** + * 确诊时间 + */ int confirmedTime = 0; double targetXU; double targetYU; double targetSig = 50; - public Person(City city, int x, int y) { - this.city = city; + public Person(int x, int y) { this.x = x; this.y = y; targetXU = 100 * ThreadLocalRandom.current().nextGaussian() + x; @@ -45,18 +56,46 @@ public boolean wantMove() { } public boolean isInfected() { - return state >= PersonState.SHADOW; + return state >= PersonState.SHADOW && state != PersonState.DEAD; } + /** + * 被感染 + */ public void beInfected() { + City.trans(PersonState.NORMAL, PersonState.SHADOW); state = PersonState.SHADOW; infectedTime = DisplayPanel.worldTime; } + public void confirmed() { + City.trans(PersonState.SHADOW, PersonState.CONFIRMED); + state = PersonState.CONFIRMED; + confirmedTime = DisplayPanel.worldTime; + } + + public void willInfected() { + if (isInfected()) { + return; + } + + // 是否能被其他人感染 + List people = City.getInstance().personList; + for (Person person : people) { + if (person.getState() == PersonState.NORMAL) { + continue; + } + + if (this.isSafeWith(person)) { + this.beInfected(); + } + } + } + /** * 是否感染 */ - public boolean willInfected(Person person) { + public boolean isSafeWith(Person person) { if (person.getState() == PersonState.NORMAL) { return false; } @@ -68,15 +107,47 @@ public double distance(Person person) { return Math.sqrt(Math.pow(x - person.getX(), 2) + Math.pow(y - person.getY(), 2)); } - private void freezy() { - state = PersonState.FREEZE; - } - private void moveTo(int x, int y) { this.x += x; this.y += y; } + private boolean freeze(Bed bed) { + City.trans(PersonState.CONFIRMED, PersonState.FREEZE); + if (Objects.nonNull(this.bed) || Objects.isNull(bed)) { +// System.out.println("隔离区没有空床位"); + return false; + } + + this.bed = bed; + state = PersonState.FREEZE; + x = bed.getX(); + y = bed.getY(); + bed.setEmpty(false); + return true; + } + + private boolean cure() { + City.trans(PersonState.FREEZE, PersonState.NORMAL); + if (Objects.isNull(this.bed)) { + return false; + } + this.bed.setEmpty(true); + this.bed = null; + this.state = PersonState.NORMAL; + return true; + } + + private boolean dead() { + City.trans(this.state, PersonState.DEAD); + if (Objects.nonNull(this.bed)) { + this.bed.setEmpty(true); + this.bed = null; + } + this.state = PersonState.DEAD; + return true; + } + // 随机移动 private void action() { if (state == PersonState.FREEZE) { @@ -86,12 +157,11 @@ private void action() { if (!wantMove()) { return; } - if (moveTarget == null || moveTarget.isArrived()) { + if (moveTarget == null || moveTarget.isArrived()) { double targetX = targetSig * ThreadLocalRandom.current().nextGaussian() + targetXU; double targetY = targetSig * ThreadLocalRandom.current().nextGaussian() + targetYU; moveTarget = new MoveTarget((int) targetX, (int) targetY); - } int dX = moveTarget.getX() - x; @@ -125,46 +195,53 @@ private void action() { udX = -udX; } } - moveTo(udX, udY); + this.moveTo(udX, udY); } public void update() { - //TODO 找时间改为状态机 - if (state >= PersonState.FREEZE) { - return; - } - if (state == PersonState.CONFIRMED - && DisplayPanel.worldTime - confirmedTime >= Constants.HOSPITAL_RECEIVE_TIME) { - Bed bed = Hospital.getInstance().pickBed(); - if (bed == null) { -// System.out.println("隔离区没有空床位"); - } else { - state = PersonState.FREEZE; - x = bed.getX(); - y = bed.getY(); - bed.setEmpty(false); - } - } - if (DisplayPanel.worldTime - infectedTime > Constants.SHADOW_TIME - && state == PersonState.SHADOW) { - state = PersonState.CONFIRMED; - confirmedTime = DisplayPanel.worldTime; - } + functions.get(state).accept(this); + } - this.action(); + static { + functions.put(PersonState.NORMAL, p -> { + p.action(); + p.willInfected(); + }); - List people = PersonPool.getInstance().personList; - if (state >= PersonState.SHADOW) { - return; - } - for (Person person : people) { - if (person.getState() == PersonState.NORMAL) { - continue; + functions.put(PersonState.SHADOW, p -> { + if (DisplayPanel.worldTime - p.infectedTime > Constants.SHADOW_TIME) { + p.confirmed(); } + p.action(); + }); - if (this.willInfected(person)) { - this.beInfected(); + functions.put(PersonState.CONFIRMED, p -> { + if (DisplayPanel.worldTime - p.confirmedTime >= Constants.HOSPITAL_RECEIVE_TIME) { + Bed bed = Hospital.INSTANCE.pickBed(); + p.freeze(bed); + } else { + p.action(); } - } + + int rate = ThreadLocalRandom.current().nextInt(Constants.BASE_RATE); + if (rate <= Constants.DEAD_RATE) { + p.dead(); + } + }); + + functions.put(PersonState.FREEZE, p -> { + // 看作数轴上两个线段 + int rate = ThreadLocalRandom.current().nextInt(Constants.BASE_RATE); + if (rate <= Constants.CURE_RATE) { + p.cure(); + } else if (rate <= Constants.DEAD_RATE + Constants.CURE_RATE) { + p.dead(); + } + }); + + functions.put(PersonState.DEAD, p -> { +// log.info("dead: person={}", p); + City.getInstance().dead(p); + }); } } diff --git a/gui/src/main/java/com/github/kuangcp/virusbroadcast/gui/DisplayPanel.java b/gui/src/main/java/com/github/kuangcp/virusbroadcast/gui/DisplayPanel.java index 1f3100d0..e15cd201 100644 --- a/gui/src/main/java/com/github/kuangcp/virusbroadcast/gui/DisplayPanel.java +++ b/gui/src/main/java/com/github/kuangcp/virusbroadcast/gui/DisplayPanel.java @@ -1,12 +1,14 @@ package com.github.kuangcp.virusbroadcast.gui; import com.github.kuangcp.virusbroadcast.Hospital; -import com.github.kuangcp.virusbroadcast.domain.Person; -import com.github.kuangcp.virusbroadcast.PersonPool; +import com.github.kuangcp.virusbroadcast.domain.City; import com.github.kuangcp.virusbroadcast.constant.PersonState; +import com.github.kuangcp.virusbroadcast.domain.Person; import java.awt.Color; import java.awt.Graphics; +import java.util.HashMap; import java.util.List; +import java.util.Map; import javax.swing.JPanel; import lombok.extern.slf4j.Slf4j; @@ -21,63 +23,76 @@ public class DisplayPanel extends JPanel implements Runnable { */ public static int worldTime = 0; + private volatile boolean runnable = true; + private volatile int infected; + + public DisplayPanel() { this.setBackground(new Color(0x444444)); } + private static Map colorMap = new HashMap<>(); + + static { + colorMap.put(PersonState.NORMAL, Color.white); + colorMap.put(PersonState.SHADOW, Color.yellow); + colorMap.put(PersonState.CONFIRMED, Color.red); + colorMap.put(PersonState.FREEZE, Color.red); + } + @Override - public void paint(Graphics arg0) { - super.paint(arg0); + public void paint(Graphics graphics) { + super.paint(graphics); + //draw border - arg0.setColor(new Color(0x00ff00)); - arg0.drawRect(Hospital.getInstance().getX(), Hospital.getInstance().getY(), - Hospital.getInstance().getWidth(), Hospital.getInstance().getHeight()); + graphics.setColor(Color.green); + Hospital hospital = Hospital.INSTANCE; + graphics.drawRect(hospital.getX(), hospital.getY(), hospital.getWidth(), hospital.getHeight()); - List people = PersonPool.getInstance().getPersonList(); - if (people == null) { + List personList = City.getInstance().getPersonList(); + + if (personList.isEmpty()) { + this.stop(false); return; } - int pIndex = 0; - people.get(pIndex).update(); - for (Person person : people) { - - switch (person.getState()) { - case PersonState.NORMAL: { - arg0.setColor(new Color(0xdddddd)); - - } - break; - case PersonState.SHADOW: { - arg0.setColor(new Color(0xffee00)); - - } - break; - case PersonState.CONFIRMED: - case PersonState.FREEZE: { - arg0.setColor(new Color(0xff0000)); - - } - break; + + int sum = 0; + for (Person person : personList) { + if (person.isInfected()) { + sum++; } + Color color = colorMap.get(person.getState()); + graphics.setColor(color); + person.update(); - arg0.fillOval(person.getX(), person.getY(), 3, 3); + graphics.fillOval(person.getX(), person.getY(), 3, 3); } - pIndex++; - if (pIndex >= people.size()) { - pIndex = 0; + this.infected = sum; + if (sum == 0) { + this.stop(true); } } @Override public void run() { - while (true) { + while (runnable) { this.repaint(); + try { - Thread.sleep(100); + Thread.sleep(30); worldTime++; } catch (InterruptedException e) { log.error("", e); } } } + + public synchronized void stop(boolean survival) { + this.runnable = false; + if (survival) { + log.warn("End of epidemic situation!"); + } else { + log.warn("No one survived!"); + } + } } From 52636242c9fbab9253739af1cb90e3e9059468ad Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Fri, 14 Feb 2020 13:58:04 +0800 Subject: [PATCH 144/476] x) trans error --- .../virusbroadcast/VirusBroadcast.java | 18 ------------- .../kuangcp/virusbroadcast/domain/City.java | 25 +++++++++++++++---- .../kuangcp/virusbroadcast/domain/Person.java | 9 ++++--- 3 files changed, 26 insertions(+), 26 deletions(-) diff --git a/gui/src/main/java/com/github/kuangcp/virusbroadcast/VirusBroadcast.java b/gui/src/main/java/com/github/kuangcp/virusbroadcast/VirusBroadcast.java index 04df61c5..70f1e8c1 100644 --- a/gui/src/main/java/com/github/kuangcp/virusbroadcast/VirusBroadcast.java +++ b/gui/src/main/java/com/github/kuangcp/virusbroadcast/VirusBroadcast.java @@ -1,11 +1,7 @@ package com.github.kuangcp.virusbroadcast; -import com.github.kuangcp.virusbroadcast.constant.Constants; import com.github.kuangcp.virusbroadcast.domain.City; -import com.github.kuangcp.virusbroadcast.domain.Person; import com.github.kuangcp.virusbroadcast.gui.DisplayPanel; -import java.util.List; -import java.util.Random; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import javax.swing.JFrame; @@ -15,7 +11,6 @@ public class VirusBroadcast { private static ExecutorService executors = Executors.newFixedThreadPool(2); public static void main(String[] args) { - DisplayPanel panel = new DisplayPanel(); JFrame frame = new JFrame(); frame.add(panel); @@ -26,18 +21,5 @@ public static void main(String[] args) { executors.submit(panel); executors.submit(City.getInstance()); - - // 初始感染 - List people = City.getInstance().getPersonList(); - for (int i = 0; i < Constants.ORIGINAL_COUNT; i++) { - int index = new Random().nextInt(people.size() - 1); - Person person = people.get(index); - - while (person.isInfected()) { - index = new Random().nextInt(people.size() - 1); - person = people.get(index); - } - person.beInfected(); - } } } diff --git a/gui/src/main/java/com/github/kuangcp/virusbroadcast/domain/City.java b/gui/src/main/java/com/github/kuangcp/virusbroadcast/domain/City.java index bf602165..b0cfee12 100644 --- a/gui/src/main/java/com/github/kuangcp/virusbroadcast/domain/City.java +++ b/gui/src/main/java/com/github/kuangcp/virusbroadcast/domain/City.java @@ -1,14 +1,15 @@ package com.github.kuangcp.virusbroadcast.domain; import static com.github.kuangcp.virusbroadcast.constant.Constants.CITY_PERSON_SCALE; -import static com.github.kuangcp.virusbroadcast.constant.Constants.ORIGINAL_COUNT; +import com.github.kuangcp.virusbroadcast.constant.Constants; import com.github.kuangcp.virusbroadcast.constant.PersonState; import com.github.kuangcp.virusbroadcast.constant.PersonStateEnum; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Optional; +import java.util.Random; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.atomic.AtomicInteger; @@ -29,7 +30,7 @@ public class City implements Runnable { counts = Stream.of(PersonStateEnum.values()) .collect(Collectors.toMap(PersonStateEnum::getValue, v -> new AtomicInteger(), (front, current) -> current)); - counts.get(PersonState.NORMAL).set(CITY_PERSON_SCALE - ORIGINAL_COUNT); + counts.get(PersonState.NORMAL).set(CITY_PERSON_SCALE); } private static City city = new City(); @@ -64,13 +65,25 @@ private City() { Person person = new Person(x, y); personList.add(person); } + + // 初始感染 + for (int i = 0; i < Constants.ORIGINAL_COUNT; i++) { + int index = new Random().nextInt(personList.size() - 1); + Person person = personList.get(index); + + while (person.isInfected()) { + index = new Random().nextInt(personList.size() - 1); + person = personList.get(index); + } + person.beInfected(); + } } @Override public void run() { while (true) { try { - Thread.sleep(1000); + Thread.sleep(3000); showCityInfo(); } catch (Exception e) { log.error("", e); @@ -82,8 +95,10 @@ public void showCityInfo() { StringBuilder temp = new StringBuilder(); for (Entry entry : counts.entrySet()) { Optional stateOpt = PersonStateEnum.getByValue(entry.getKey()); - stateOpt.ifPresent(personStateEnum -> temp - .append(String.format("%6s: %5d ", personStateEnum.getDesc(), entry.getValue().get()))); + if (stateOpt.isPresent()) { + int value = entry.getValue().get(); + temp.append(String.format("%6s: %5d ▌", stateOpt.get().getDesc(), value)); + } } log.info("{}", temp.toString()); } diff --git a/gui/src/main/java/com/github/kuangcp/virusbroadcast/domain/Person.java b/gui/src/main/java/com/github/kuangcp/virusbroadcast/domain/Person.java index 01782d38..db277a32 100644 --- a/gui/src/main/java/com/github/kuangcp/virusbroadcast/domain/Person.java +++ b/gui/src/main/java/com/github/kuangcp/virusbroadcast/domain/Person.java @@ -63,6 +63,9 @@ public boolean isInfected() { * 被感染 */ public void beInfected() { + if (this.isInfected()) { + return; + } City.trans(PersonState.NORMAL, PersonState.SHADOW); state = PersonState.SHADOW; infectedTime = DisplayPanel.worldTime; @@ -75,7 +78,7 @@ public void confirmed() { } public void willInfected() { - if (isInfected()) { + if (this.isInfected()) { return; } @@ -86,7 +89,7 @@ public void willInfected() { continue; } - if (this.isSafeWith(person)) { + if (this.mayInfected(person)) { this.beInfected(); } } @@ -95,7 +98,7 @@ public void willInfected() { /** * 是否感染 */ - public boolean isSafeWith(Person person) { + public boolean mayInfected(Person person) { if (person.getState() == PersonState.NORMAL) { return false; } From 7464e0cf7df7b115380f541857a81b1776b07fca Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Fri, 14 Feb 2020 14:19:03 +0800 Subject: [PATCH 145/476] x) trans error --- .../kuangcp/virusbroadcast/domain/City.java | 57 +++++++++++-------- .../kuangcp/virusbroadcast/domain/Person.java | 2 +- 2 files changed, 34 insertions(+), 25 deletions(-) diff --git a/gui/src/main/java/com/github/kuangcp/virusbroadcast/domain/City.java b/gui/src/main/java/com/github/kuangcp/virusbroadcast/domain/City.java index b0cfee12..5264aa07 100644 --- a/gui/src/main/java/com/github/kuangcp/virusbroadcast/domain/City.java +++ b/gui/src/main/java/com/github/kuangcp/virusbroadcast/domain/City.java @@ -23,7 +23,10 @@ public class City implements Runnable { private int centerX = 400; private int centerY = 400; private int personCount = CITY_PERSON_SCALE; + private volatile boolean init = false; + private static City city = new City(); + public List personList = new CopyOnWriteArrayList<>(); private static Map counts; static { @@ -33,28 +36,6 @@ public class City implements Runnable { counts.get(PersonState.NORMAL).set(CITY_PERSON_SCALE); } - private static City city = new City(); - public List personList = new CopyOnWriteArrayList<>(); - - public static City getInstance() { - return city; - } - - public List getPersonList() { - return personList; - } - - public void dead(Person person) { - personList.remove(person); - } - - public static void trans(int from, int to) { - AtomicInteger fromCount = counts.get(from); - AtomicInteger toCount = counts.get(to); - toCount.incrementAndGet(); - fromCount.decrementAndGet(); - } - private City() { for (int i = 0; i < this.personCount; i++) { int x = (int) (100 * ThreadLocalRandom.current().nextGaussian() + centerX); @@ -65,7 +46,9 @@ private City() { Person person = new Person(x, y); personList.add(person); } + } + public void initInfected() { // 初始感染 for (int i = 0; i < Constants.ORIGINAL_COUNT; i++) { int index = new Random().nextInt(personList.size() - 1); @@ -79,11 +62,35 @@ private City() { } } + + public static City getInstance() { + return city; + } + + public List getPersonList() { + return personList; + } + + public void dead(Person person) { + personList.remove(person); + } + + public static void trans(int from, int to) { + AtomicInteger fromCount = counts.get(from); + AtomicInteger toCount = counts.get(to); + toCount.incrementAndGet(); + fromCount.decrementAndGet(); + } + @Override public void run() { while (true) { + if (!init) { + this.initInfected(); + this.init = true; + } try { - Thread.sleep(3000); + Thread.sleep(1000); showCityInfo(); } catch (Exception e) { log.error("", e); @@ -93,13 +100,15 @@ public void run() { public void showCityInfo() { StringBuilder temp = new StringBuilder(); + int sum = 0; for (Entry entry : counts.entrySet()) { Optional stateOpt = PersonStateEnum.getByValue(entry.getKey()); if (stateOpt.isPresent()) { int value = entry.getValue().get(); temp.append(String.format("%6s: %5d ▌", stateOpt.get().getDesc(), value)); + sum += value; } } - log.info("{}", temp.toString()); + log.info("{} sum:{}", temp.toString(), sum); } } diff --git a/gui/src/main/java/com/github/kuangcp/virusbroadcast/domain/Person.java b/gui/src/main/java/com/github/kuangcp/virusbroadcast/domain/Person.java index db277a32..6fbdb621 100644 --- a/gui/src/main/java/com/github/kuangcp/virusbroadcast/domain/Person.java +++ b/gui/src/main/java/com/github/kuangcp/virusbroadcast/domain/Person.java @@ -116,12 +116,12 @@ private void moveTo(int x, int y) { } private boolean freeze(Bed bed) { - City.trans(PersonState.CONFIRMED, PersonState.FREEZE); if (Objects.nonNull(this.bed) || Objects.isNull(bed)) { // System.out.println("隔离区没有空床位"); return false; } + City.trans(PersonState.CONFIRMED, PersonState.FREEZE); this.bed = bed; state = PersonState.FREEZE; x = bed.getX(); From 5340fa2d6704e0e9c656e4e901004bab74b9b15a Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Wed, 19 Feb 2020 00:06:33 +0800 Subject: [PATCH 146/476] -) remove useless field --- .../com/github/kuangcp/virusbroadcast/domain/Person.java | 2 +- .../github/kuangcp/virusbroadcast/gui/DisplayPanel.java | 7 ++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/gui/src/main/java/com/github/kuangcp/virusbroadcast/domain/Person.java b/gui/src/main/java/com/github/kuangcp/virusbroadcast/domain/Person.java index 6fbdb621..525d76f0 100644 --- a/gui/src/main/java/com/github/kuangcp/virusbroadcast/domain/Person.java +++ b/gui/src/main/java/com/github/kuangcp/virusbroadcast/domain/Person.java @@ -151,7 +151,7 @@ private boolean dead() { return true; } - // 随机移动 + // 随机移动 TODO 确诊无法移动 private void action() { if (state == PersonState.FREEZE) { return; diff --git a/gui/src/main/java/com/github/kuangcp/virusbroadcast/gui/DisplayPanel.java b/gui/src/main/java/com/github/kuangcp/virusbroadcast/gui/DisplayPanel.java index e15cd201..d2f618f9 100644 --- a/gui/src/main/java/com/github/kuangcp/virusbroadcast/gui/DisplayPanel.java +++ b/gui/src/main/java/com/github/kuangcp/virusbroadcast/gui/DisplayPanel.java @@ -24,8 +24,6 @@ public class DisplayPanel extends JPanel implements Runnable { public static int worldTime = 0; private volatile boolean runnable = true; - private volatile int infected; - public DisplayPanel() { this.setBackground(new Color(0x444444)); @@ -37,7 +35,7 @@ public DisplayPanel() { colorMap.put(PersonState.NORMAL, Color.white); colorMap.put(PersonState.SHADOW, Color.yellow); colorMap.put(PersonState.CONFIRMED, Color.red); - colorMap.put(PersonState.FREEZE, Color.red); + colorMap.put(PersonState.FREEZE, Color.blue); } @Override @@ -45,7 +43,7 @@ public void paint(Graphics graphics) { super.paint(graphics); //draw border - graphics.setColor(Color.green); + graphics.setColor(Color.blue); Hospital hospital = Hospital.INSTANCE; graphics.drawRect(hospital.getX(), hospital.getY(), hospital.getWidth(), hospital.getHeight()); @@ -67,7 +65,6 @@ public void paint(Graphics graphics) { person.update(); graphics.fillOval(person.getX(), person.getY(), 3, 3); } - this.infected = sum; if (sum == 0) { this.stop(true); } From 24379ed42522a16d83d7d5973acb9e27bb5e6046 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Sat, 22 Feb 2020 01:15:57 +0800 Subject: [PATCH 147/476] -) remove useless --- .../InstantiationAndConstructorTest.java | 2 +- .../di/annotation/AnnotationParse.java | 36 ------------------- .../kuangcp/di/annotation/Description.java | 14 -------- .../HandleAnnotationTest.java} | 6 ++-- .../github/kuangcp/di/annotation/ITCAST.java | 14 -------- .../github/kuangcp/di/annotation/Name.java | 25 ------------- .../di/{scan => annotation}/Person.java | 9 +++-- .../{di/xml/set => annotation}/Student.java | 2 +- .../AutoScanTest.java} | 8 ++--- .../github/kuangcp/di/autoscan/Person.java | 31 ++++++++++++++++ .../di/{scan => autoscan}/Student.java | 6 ++-- .../kuangcp/di/di/annotation/Person.java | 29 --------------- .../di/{di => }/xml/constructor/Person.java | 2 +- .../{di => }/xml/constructor/PersonTest.java | 2 +- .../di/{di => }/xml/constructor/Student.java | 2 +- .../xml/constructor/applicationContext.xml | 4 +-- .../kuangcp/di/{di => }/xml/set/Person.java | 2 +- .../di/{di => }/xml/set/PersonTest.java | 2 +- .../{di/annotation => xml/set}/Student.java | 2 +- .../{di => }/xml/set/applicationContext.xml | 4 +-- .../github/kuangcp/document/DocumentTest.java | 2 +- .../kuangcp/document/ExcelDocument.java | 2 -- .../github/kuangcp/document/PDFDocument.java | 2 -- .../github/kuangcp/document/WordDocument.java | 2 -- .../kuangcp/document/spring/Document.java | 8 ----- .../document/spring/DocumentManager.java | 2 ++ .../document/spring/ExcelDocument.java | 17 --------- .../kuangcp/document/spring/PDFDocument.java | 17 --------- .../kuangcp/document/spring/WordDocument.java | 17 --------- .../document/spring/applicationContext.xml | 29 ++++++--------- .../kuangcp/exception/ExceptionTest.java | 3 +- .../kuangcp/exception/ServiceInvocation.java | 9 ++--- .../kuangcp/exception/ServiceMapping.java | 32 +++++------------ .../exception/service/PersonServiceImpl.java | 33 ++++++++--------- .../com/github/kuangcp/extend/Person.java | 11 ++---- .../kuangcp/extend/applicationContext.xml | 2 +- .../di/annotation/applicationContext.xml | 6 ++-- .../di/autoscan}/applicationContext.xml | 2 +- 38 files changed, 107 insertions(+), 291 deletions(-) delete mode 100644 spring/src/test/java/com/github/kuangcp/di/annotation/AnnotationParse.java delete mode 100644 spring/src/test/java/com/github/kuangcp/di/annotation/Description.java rename spring/src/test/java/com/github/kuangcp/di/{di/annotation/PersonTest.java => annotation/HandleAnnotationTest.java} (89%) delete mode 100644 spring/src/test/java/com/github/kuangcp/di/annotation/ITCAST.java delete mode 100644 spring/src/test/java/com/github/kuangcp/di/annotation/Name.java rename spring/src/test/java/com/github/kuangcp/di/{scan => annotation}/Person.java (70%) rename spring/src/test/java/com/github/kuangcp/di/{di/xml/set => annotation}/Student.java (67%) rename spring/src/test/java/com/github/kuangcp/di/{scan/PersonTest.java => autoscan/AutoScanTest.java} (72%) create mode 100644 spring/src/test/java/com/github/kuangcp/di/autoscan/Person.java rename spring/src/test/java/com/github/kuangcp/di/{scan => autoscan}/Student.java (51%) delete mode 100644 spring/src/test/java/com/github/kuangcp/di/di/annotation/Person.java rename spring/src/test/java/com/github/kuangcp/di/{di => }/xml/constructor/Person.java (89%) rename spring/src/test/java/com/github/kuangcp/di/{di => }/xml/constructor/PersonTest.java (87%) rename spring/src/test/java/com/github/kuangcp/di/{di => }/xml/constructor/Student.java (62%) rename spring/src/test/java/com/github/kuangcp/di/{di => }/xml/constructor/applicationContext.xml (82%) rename spring/src/test/java/com/github/kuangcp/di/{di => }/xml/set/Person.java (96%) rename spring/src/test/java/com/github/kuangcp/di/{di => }/xml/set/PersonTest.java (93%) rename spring/src/test/java/com/github/kuangcp/di/{di/annotation => xml/set}/Student.java (66%) rename spring/src/test/java/com/github/kuangcp/di/{di => }/xml/set/applicationContext.xml (90%) delete mode 100644 spring/src/test/java/com/github/kuangcp/document/spring/Document.java delete mode 100644 spring/src/test/java/com/github/kuangcp/document/spring/ExcelDocument.java delete mode 100644 spring/src/test/java/com/github/kuangcp/document/spring/PDFDocument.java delete mode 100644 spring/src/test/java/com/github/kuangcp/document/spring/WordDocument.java rename spring/src/test/{java/com/github/kuangcp/di => resources}/di/annotation/applicationContext.xml (80%) rename spring/src/test/{java/com/github/kuangcp/di/scan => resources/di/autoscan}/applicationContext.xml (91%) diff --git a/class/src/test/java/com/github/kuangcp/instantiation/InstantiationAndConstructorTest.java b/class/src/test/java/com/github/kuangcp/instantiation/InstantiationAndConstructorTest.java index 00c2554e..ff755c8a 100644 --- a/class/src/test/java/com/github/kuangcp/instantiation/InstantiationAndConstructorTest.java +++ b/class/src/test/java/com/github/kuangcp/instantiation/InstantiationAndConstructorTest.java @@ -18,7 +18,7 @@ /** * @author kuangcp on 3/9/19-5:48 PM * - * 实例化 对象的方式 + * 实例化 对象的方式 https://juejin.im/post/5d44530a6fb9a06aed7103bd * 1. new * 1. 反射获取构造器调用 * 1. clone diff --git a/spring/src/test/java/com/github/kuangcp/di/annotation/AnnotationParse.java b/spring/src/test/java/com/github/kuangcp/di/annotation/AnnotationParse.java deleted file mode 100644 index 5ec94db2..00000000 --- a/spring/src/test/java/com/github/kuangcp/di/annotation/AnnotationParse.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.github.kuangcp.di.annotation; - -import java.lang.reflect.Method; - -import org.junit.Test; - -/** - * 注解解析器 - * @author Administrator - * - */ -public class AnnotationParse { - public static void parse(){ - Class classt = ITCAST.class; - /** - * 类上的注解 - */ - if(classt.isAnnotationPresent(Name.class)){ - Name name = (Name)classt.getAnnotation(Name.class); - System.out.println(name.value()); - } - - Method[] methods = classt.getMethods(); - for(Method method:methods){ - if(method.isAnnotationPresent(Description.class)){ - Description description = (Description)method.getAnnotation(Description.class); - System.out.println(description.value()); - } - } - } - - @Test - public void test(){ - AnnotationParse.parse(); - } -} diff --git a/spring/src/test/java/com/github/kuangcp/di/annotation/Description.java b/spring/src/test/java/com/github/kuangcp/di/annotation/Description.java deleted file mode 100644 index ab1d45d2..00000000 --- a/spring/src/test/java/com/github/kuangcp/di/annotation/Description.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.github.kuangcp.di.annotation; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Documented -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.METHOD) -public @interface Description { - String value(); -} diff --git a/spring/src/test/java/com/github/kuangcp/di/di/annotation/PersonTest.java b/spring/src/test/java/com/github/kuangcp/di/annotation/HandleAnnotationTest.java similarity index 89% rename from spring/src/test/java/com/github/kuangcp/di/di/annotation/PersonTest.java rename to spring/src/test/java/com/github/kuangcp/di/annotation/HandleAnnotationTest.java index 4607c4b6..cf115bc6 100644 --- a/spring/src/test/java/com/github/kuangcp/di/di/annotation/PersonTest.java +++ b/spring/src/test/java/com/github/kuangcp/di/annotation/HandleAnnotationTest.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.di.di.annotation; +package com.github.kuangcp.di.annotation; import com.github.kuangcp.util.SpringHelper; import org.junit.Test; @@ -21,11 +21,11 @@ * * @author Administrator */ -public class PersonTest extends SpringHelper { +public class HandleAnnotationTest extends SpringHelper { @Override public String getXmlPath() { - return "proxy/salary/applicationContext.xml"; + return "di/annotation/applicationContext.xml"; } @Test diff --git a/spring/src/test/java/com/github/kuangcp/di/annotation/ITCAST.java b/spring/src/test/java/com/github/kuangcp/di/annotation/ITCAST.java deleted file mode 100644 index 4aea2ad0..00000000 --- a/spring/src/test/java/com/github/kuangcp/di/annotation/ITCAST.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.github.kuangcp.di.annotation; - -@Name("传智播客") -public class ITCAST { - @Description("java学科") - public void java(){ - System.out.println("很牛"); - } - - @Description("net学科") - public void net(){ - System.out.println("也很牛"); - } -} diff --git a/spring/src/test/java/com/github/kuangcp/di/annotation/Name.java b/spring/src/test/java/com/github/kuangcp/di/annotation/Name.java deleted file mode 100644 index a99f3221..00000000 --- a/spring/src/test/java/com/github/kuangcp/di/annotation/Name.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.github.kuangcp.di.annotation; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * 该注解描述了作用范围 - * java RetentionPolicy.SOURCE - * java+class RetentionPolicy.CLASS - * java+class+jvm RetentionPolicy.RUNTIME - */ -@Retention(RetentionPolicy.RUNTIME) -/** - * 该注解既能在类上也能在方法上出现 - * @author Administrator - * - */ -@Target({ElementType.TYPE}) -@Documented//是否在帮助文档中出现 -public @interface Name { - String value() default "";//Name注解有一个属性为value -} diff --git a/spring/src/test/java/com/github/kuangcp/di/scan/Person.java b/spring/src/test/java/com/github/kuangcp/di/annotation/Person.java similarity index 70% rename from spring/src/test/java/com/github/kuangcp/di/scan/Person.java rename to spring/src/test/java/com/github/kuangcp/di/annotation/Person.java index 989b4b83..0f98eeb6 100644 --- a/spring/src/test/java/com/github/kuangcp/di/scan/Person.java +++ b/spring/src/test/java/com/github/kuangcp/di/annotation/Person.java @@ -1,20 +1,19 @@ -package com.github.kuangcp.di.scan; +package com.github.kuangcp.di.annotation; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import javax.annotation.Resource; -import org.springframework.stereotype.Component; -@Component("perso") public class Person { @Resource(name = "student") - private Student studen; + private Student student; + @Resource private Long pid; public void say() { - this.studen.say(); + this.student.say(); } @PostConstruct diff --git a/spring/src/test/java/com/github/kuangcp/di/di/xml/set/Student.java b/spring/src/test/java/com/github/kuangcp/di/annotation/Student.java similarity index 67% rename from spring/src/test/java/com/github/kuangcp/di/di/xml/set/Student.java rename to spring/src/test/java/com/github/kuangcp/di/annotation/Student.java index 851a2d73..b9c9cde6 100644 --- a/spring/src/test/java/com/github/kuangcp/di/di/xml/set/Student.java +++ b/spring/src/test/java/com/github/kuangcp/di/annotation/Student.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.di.di.xml.set; +package com.github.kuangcp.di.annotation; public class Student { diff --git a/spring/src/test/java/com/github/kuangcp/di/scan/PersonTest.java b/spring/src/test/java/com/github/kuangcp/di/autoscan/AutoScanTest.java similarity index 72% rename from spring/src/test/java/com/github/kuangcp/di/scan/PersonTest.java rename to spring/src/test/java/com/github/kuangcp/di/autoscan/AutoScanTest.java index 1605654e..78e0bc6d 100644 --- a/spring/src/test/java/com/github/kuangcp/di/scan/PersonTest.java +++ b/spring/src/test/java/com/github/kuangcp/di/autoscan/AutoScanTest.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.di.scan; +package com.github.kuangcp.di.autoscan; import com.github.kuangcp.util.SpringHelper; import org.junit.Test; @@ -14,16 +14,16 @@ * * @author Administrator */ -public class PersonTest extends SpringHelper { +public class AutoScanTest extends SpringHelper { @Override public String getXmlPath() { - return "proxy/salary/applicationContext.xml"; + return "di/autoscan/applicationContext.xml"; } @Test public void test() { - Person person = (Person) context.getBean("perso"); + Person person = (Person) context.getBean("person"); person.say(); } } diff --git a/spring/src/test/java/com/github/kuangcp/di/autoscan/Person.java b/spring/src/test/java/com/github/kuangcp/di/autoscan/Person.java new file mode 100644 index 00000000..a0247252 --- /dev/null +++ b/spring/src/test/java/com/github/kuangcp/di/autoscan/Person.java @@ -0,0 +1,31 @@ +package com.github.kuangcp.di.autoscan; + +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; +import javax.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +@Slf4j +@Component("person") +public class Person { + + @Resource(name = "student") + private Student student; + + private Long pid; + + public void say() { + this.student.say(); + } + + @PostConstruct + public void init() { + log.info("init"); + } + + @PreDestroy + public void destroy() { + log.info("destroy"); + } +} diff --git a/spring/src/test/java/com/github/kuangcp/di/scan/Student.java b/spring/src/test/java/com/github/kuangcp/di/autoscan/Student.java similarity index 51% rename from spring/src/test/java/com/github/kuangcp/di/scan/Student.java rename to spring/src/test/java/com/github/kuangcp/di/autoscan/Student.java index c315c0a8..97c109a0 100644 --- a/spring/src/test/java/com/github/kuangcp/di/scan/Student.java +++ b/spring/src/test/java/com/github/kuangcp/di/autoscan/Student.java @@ -1,11 +1,13 @@ -package com.github.kuangcp.di.scan; +package com.github.kuangcp.di.autoscan; +import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; +@Slf4j @Component public class Student { public void say() { - System.out.println("student"); + log.info("student"); } } diff --git a/spring/src/test/java/com/github/kuangcp/di/di/annotation/Person.java b/spring/src/test/java/com/github/kuangcp/di/di/annotation/Person.java deleted file mode 100644 index 31987ac7..00000000 --- a/spring/src/test/java/com/github/kuangcp/di/di/annotation/Person.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.github.kuangcp.di.di.annotation; - -import javax.annotation.PostConstruct; -import javax.annotation.PreDestroy; -import javax.annotation.Resource; - -public class Person { - @Resource(name="student") - //@Autowired//按照类型进行匹配 - //@Qualifier("student") - private Student studen; - - @Resource - private Long pid; - - public void say(){ - this.studen.say(); - } - - @PostConstruct - public void init(){ - System.out.println("init"); - } - - @PreDestroy - public void destroy(){ - System.out.println("destroy"); - } -} diff --git a/spring/src/test/java/com/github/kuangcp/di/di/xml/constructor/Person.java b/spring/src/test/java/com/github/kuangcp/di/xml/constructor/Person.java similarity index 89% rename from spring/src/test/java/com/github/kuangcp/di/di/xml/constructor/Person.java rename to spring/src/test/java/com/github/kuangcp/di/xml/constructor/Person.java index 2b1ff431..d8dc3019 100644 --- a/spring/src/test/java/com/github/kuangcp/di/di/xml/constructor/Person.java +++ b/spring/src/test/java/com/github/kuangcp/di/xml/constructor/Person.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.di.di.xml.constructor; +package com.github.kuangcp.di.xml.constructor; public class Person { private Long pid; diff --git a/spring/src/test/java/com/github/kuangcp/di/di/xml/constructor/PersonTest.java b/spring/src/test/java/com/github/kuangcp/di/xml/constructor/PersonTest.java similarity index 87% rename from spring/src/test/java/com/github/kuangcp/di/di/xml/constructor/PersonTest.java rename to spring/src/test/java/com/github/kuangcp/di/xml/constructor/PersonTest.java index 7157663e..984e3262 100644 --- a/spring/src/test/java/com/github/kuangcp/di/di/xml/constructor/PersonTest.java +++ b/spring/src/test/java/com/github/kuangcp/di/xml/constructor/PersonTest.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.di.di.xml.constructor; +package com.github.kuangcp.di.xml.constructor; import com.github.kuangcp.util.SpringHelper; import org.junit.Test; diff --git a/spring/src/test/java/com/github/kuangcp/di/di/xml/constructor/Student.java b/spring/src/test/java/com/github/kuangcp/di/xml/constructor/Student.java similarity index 62% rename from spring/src/test/java/com/github/kuangcp/di/di/xml/constructor/Student.java rename to spring/src/test/java/com/github/kuangcp/di/xml/constructor/Student.java index d3f546de..742fbfb1 100644 --- a/spring/src/test/java/com/github/kuangcp/di/di/xml/constructor/Student.java +++ b/spring/src/test/java/com/github/kuangcp/di/xml/constructor/Student.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.di.di.xml.constructor; +package com.github.kuangcp.di.xml.constructor; public class Student { public void say(){ diff --git a/spring/src/test/java/com/github/kuangcp/di/di/xml/constructor/applicationContext.xml b/spring/src/test/java/com/github/kuangcp/di/xml/constructor/applicationContext.xml similarity index 82% rename from spring/src/test/java/com/github/kuangcp/di/di/xml/constructor/applicationContext.xml rename to spring/src/test/java/com/github/kuangcp/di/xml/constructor/applicationContext.xml index 8b45a8ec..063b941b 100644 --- a/spring/src/test/java/com/github/kuangcp/di/di/xml/constructor/applicationContext.xml +++ b/spring/src/test/java/com/github/kuangcp/di/xml/constructor/applicationContext.xml @@ -3,7 +3,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"> - + - - - - - - - + + + + + - - - - - + + + \ No newline at end of file diff --git a/spring/src/test/java/com/github/kuangcp/exception/ExceptionTest.java b/spring/src/test/java/com/github/kuangcp/exception/ExceptionTest.java index 81de0b62..715c1edf 100644 --- a/spring/src/test/java/com/github/kuangcp/exception/ExceptionTest.java +++ b/spring/src/test/java/com/github/kuangcp/exception/ExceptionTest.java @@ -1,5 +1,6 @@ package com.github.kuangcp.exception; +import com.github.kuangcp.exception.service.StudentServiceImpl; import org.junit.Test; public class ExceptionTest { @@ -7,7 +8,7 @@ public class ExceptionTest { @Test public void test() { ServiceMapping serviceMapping = new ServiceMapping(); - serviceMapping.setServiceClass("cn.itcast.exception.service.StudentServiceImpl"); + serviceMapping.setServiceClass(StudentServiceImpl.class.getName()); serviceMapping.setMethod("savePerson"); ServiceInvocation.execution(serviceMapping); } diff --git a/spring/src/test/java/com/github/kuangcp/exception/ServiceInvocation.java b/spring/src/test/java/com/github/kuangcp/exception/ServiceInvocation.java index 18e4355d..189e63b4 100644 --- a/spring/src/test/java/com/github/kuangcp/exception/ServiceInvocation.java +++ b/spring/src/test/java/com/github/kuangcp/exception/ServiceInvocation.java @@ -2,7 +2,9 @@ import com.github.kuangcp.exception.service.Service; +import lombok.extern.slf4j.Slf4j; +@Slf4j public class ServiceInvocation { /** @@ -10,17 +12,12 @@ public class ServiceInvocation { * 该方法是service总的调用接口,所以能在这里统一处理异常 */ public static Object execution(ServiceMapping serviceMapping) { - /** - * serviceMapping - * serviceClass cn.itcast.exception.service.StudentServiceImpl - * methodName savePerson - */ Object obj = null; try { Service service = (Service) Class.forName(serviceMapping.getServiceClass()).newInstance(); obj = service.service(serviceMapping); } catch (Exception e) { - e.printStackTrace(); + log.error("", e); } return obj; } diff --git a/spring/src/test/java/com/github/kuangcp/exception/ServiceMapping.java b/spring/src/test/java/com/github/kuangcp/exception/ServiceMapping.java index 2638f607..8a552bb1 100644 --- a/spring/src/test/java/com/github/kuangcp/exception/ServiceMapping.java +++ b/spring/src/test/java/com/github/kuangcp/exception/ServiceMapping.java @@ -1,29 +1,15 @@ package com.github.kuangcp.exception; + +import lombok.Data; + /** * 只是一个数据格式而已,作为各个类通信的数据格式,指明某类某方法要执行 - * @TODO - * @author Myth - * @date 2016年11月21日 上午9:49:37 + * + * @author Myth */ +@Data public class ServiceMapping { - private String serviceClass;//封装service的全名 - private String method;//某一个service的方法 - - public String getServiceClass() { - return serviceClass; - } - - public void setServiceClass(String serviceClass) { - this.serviceClass = serviceClass; - } - - public String getMethod() { - return method; - } - - public void setMethod(String method) { - this.method = method; - } - -} + private String serviceClass;//封装service的全名 + private String method;//某一个service的方法 +} diff --git a/spring/src/test/java/com/github/kuangcp/exception/service/PersonServiceImpl.java b/spring/src/test/java/com/github/kuangcp/exception/service/PersonServiceImpl.java index 5ff9895e..fa15c089 100644 --- a/spring/src/test/java/com/github/kuangcp/exception/service/PersonServiceImpl.java +++ b/spring/src/test/java/com/github/kuangcp/exception/service/PersonServiceImpl.java @@ -2,29 +2,24 @@ import com.github.kuangcp.exception.ServiceMapping; import java.lang.reflect.Method; +import lombok.extern.slf4j.Slf4j; /** * 接口的实现类,提供了反射实现某方法的具体实现 * 之后的类要继承,通过ServiceInvocation类来调用,从而执行指定类和方法 - * 如果不继承的话 就至少要实现service接口又要重写service方法(假如重写的方法不是这样写,就不会执行指定方法了) - * @TODO - * @author Myth - * @date 2016年11月21日 上午9:41:49 + * 如果不继承的话 就至少要实现service接口又要重写service方法(假如重写的方法不是这样写,就不会执行指定方法了) + * + * @author Myth */ -public class PersonServiceImpl implements Service{ +@Slf4j +public class PersonServiceImpl implements Service { - @Override - public Object service(ServiceMapping serviceMapping) throws Exception{ - // TODO Auto-generated method stub - /** - * serviceMapping - * serviceClass cn.itcast.exception.service.StudentServiceImpl - * methodName savePerson - */ - String methodName = serviceMapping.getMethod(); - System.out.println("方法是:"+methodName); - Object obj = Class.forName(serviceMapping.getServiceClass()).newInstance(); - Method method = Class.forName(serviceMapping.getServiceClass()).getMethod(methodName); - return method.invoke(obj); - } + @Override + public Object service(ServiceMapping serviceMapping) throws Exception { + String methodName = serviceMapping.getMethod(); + log.info("方法是:" + methodName); + Object obj = Class.forName(serviceMapping.getServiceClass()).newInstance(); + Method method = Class.forName(serviceMapping.getServiceClass()).getMethod(methodName); + return method.invoke(obj); + } } diff --git a/spring/src/test/java/com/github/kuangcp/extend/Person.java b/spring/src/test/java/com/github/kuangcp/extend/Person.java index a57fa76d..91818e2c 100644 --- a/spring/src/test/java/com/github/kuangcp/extend/Person.java +++ b/spring/src/test/java/com/github/kuangcp/extend/Person.java @@ -1,14 +1,9 @@ package com.github.kuangcp.extend; +import lombok.Data; + +@Data public abstract class Person { private String name; - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } } diff --git a/spring/src/test/java/com/github/kuangcp/extend/applicationContext.xml b/spring/src/test/java/com/github/kuangcp/extend/applicationContext.xml index 38ae18db..d5e5b8a7 100644 --- a/spring/src/test/java/com/github/kuangcp/extend/applicationContext.xml +++ b/spring/src/test/java/com/github/kuangcp/extend/applicationContext.xml @@ -8,7 +8,7 @@ spring容器不会为该类创建对象 --> - + - - - + + + \ No newline at end of file diff --git a/spring/src/test/java/com/github/kuangcp/di/scan/applicationContext.xml b/spring/src/test/resources/di/autoscan/applicationContext.xml similarity index 91% rename from spring/src/test/java/com/github/kuangcp/di/scan/applicationContext.xml rename to spring/src/test/resources/di/autoscan/applicationContext.xml index 95d137b6..b93875dd 100644 --- a/spring/src/test/java/com/github/kuangcp/di/scan/applicationContext.xml +++ b/spring/src/test/resources/di/autoscan/applicationContext.xml @@ -20,5 +20,5 @@ base-package 会在base-package的值所在的包及子包下扫描所有的类 --> - + \ No newline at end of file From 72b894765748131d44ad4bba5cb90e05c6dee8ef Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Mon, 16 Mar 2020 00:58:42 +0800 Subject: [PATCH 148/476] -) remove useless class --- .../github/kuangcp/lock/dead/DeadLock.java | 15 ++-- .../kuangcp/lock/dead/DeadLockDemo.java | 22 ------ .../kuangcp/lock/dead/DeadLockHandler.java | 41 ++++++----- .../lock/dead/DeadLockHandlerDemo.java | 25 ------- .../kuangcp/lock/dead/DeadLockRewrite.java | 25 ++++--- .../lock/dead/DeadLockRewriteDemo.java | 24 ------- .../kuangcp/lock/dead/MythDeadLockAble.java | 17 +++++ .../com/github/kuangcp/lock/pvp/Player.java | 3 +- .../java/com/github/kuangcp/old/Food.java | 17 ++--- .../kuangcp/lock/dead/DeadLockTest.java | 70 +++++++++++++++++++ gui/Readme.md | 6 ++ .../kuangcp/di/xml/constructor/Readme.md | 1 + 12 files changed, 155 insertions(+), 111 deletions(-) delete mode 100644 concurrency/src/main/java/com/github/kuangcp/lock/dead/DeadLockDemo.java delete mode 100644 concurrency/src/main/java/com/github/kuangcp/lock/dead/DeadLockHandlerDemo.java delete mode 100644 concurrency/src/main/java/com/github/kuangcp/lock/dead/DeadLockRewriteDemo.java create mode 100644 concurrency/src/main/java/com/github/kuangcp/lock/dead/MythDeadLockAble.java create mode 100644 concurrency/src/test/java/com/github/kuangcp/lock/dead/DeadLockTest.java create mode 100644 gui/Readme.md create mode 100644 spring/src/test/java/com/github/kuangcp/di/xml/constructor/Readme.md diff --git a/concurrency/src/main/java/com/github/kuangcp/lock/dead/DeadLock.java b/concurrency/src/main/java/com/github/kuangcp/lock/dead/DeadLock.java index 8fd1ca47..81c3aef0 100644 --- a/concurrency/src/main/java/com/github/kuangcp/lock/dead/DeadLock.java +++ b/concurrency/src/main/java/com/github/kuangcp/lock/dead/DeadLock.java @@ -12,20 +12,21 @@ * @author kuangcp on 3/4/19-7:52 AM */ @Slf4j -class DeadLock { +class DeadLock implements MythDeadLockAble { private final String id; + private final Lock locks = new ReentrantLock(); DeadLock(String id) { this.id = id; } - private String getId() { + public String getId() { return id; } - void prepareRun(Food food, DeadLock lock) { + public void prepareRun(Food food, MythDeadLockAble lock) { boolean required = false; while (!required) { try { @@ -50,7 +51,8 @@ void prepareRun(Food food, DeadLock lock) { } } - private void confirmRun(Food food, DeadLock lock) { + @Override + public void confirmRun(Food food, MythDeadLockAble lock) { locks.lock(); // 尝试锁住其他线程 正是这里可能出现死锁,因为这个其他线程已经加锁这里就死锁了 try { log.info("run: currentId={} resource={} lock={} time={}", @@ -59,4 +61,9 @@ private void confirmRun(Food food, DeadLock lock) { locks.unlock(); } } + + @Override + public boolean confirmRun2(Food food, MythDeadLockAble lock) { + return false; + } } \ No newline at end of file diff --git a/concurrency/src/main/java/com/github/kuangcp/lock/dead/DeadLockDemo.java b/concurrency/src/main/java/com/github/kuangcp/lock/dead/DeadLockDemo.java deleted file mode 100644 index 30783994..00000000 --- a/concurrency/src/main/java/com/github/kuangcp/lock/dead/DeadLockDemo.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.github.kuangcp.lock.dead; - -import com.github.kuangcp.old.Food; - -/** - * Created by https://github.com/kuangcp on 17-8-14 下午8:58 - * 尝试 解决死锁 - * 当遇到锁进行休眠,但是没有释放已经占用的锁,所以还是很大几率死锁 - */ -public class DeadLockDemo { - - public static void main(String[] s) { - - DeadLock one = new DeadLock("one"); - DeadLock other = new DeadLock("other"); - Food fooda = new Food("a"); - Food foodb = new Food("b"); - - new Thread(() -> one.prepareRun(fooda, other)).start(); - new Thread(() -> other.prepareRun(foodb, one)).start(); - } -} \ No newline at end of file diff --git a/concurrency/src/main/java/com/github/kuangcp/lock/dead/DeadLockHandler.java b/concurrency/src/main/java/com/github/kuangcp/lock/dead/DeadLockHandler.java index 87ab7598..ac264c97 100644 --- a/concurrency/src/main/java/com/github/kuangcp/lock/dead/DeadLockHandler.java +++ b/concurrency/src/main/java/com/github/kuangcp/lock/dead/DeadLockHandler.java @@ -4,16 +4,18 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; +import lombok.extern.slf4j.Slf4j; /** * created by https://gitee.com/gin9 * * @author kuangcp on 3/4/19-8:03 AM */ -class DeadLockHandler { +@Slf4j +class DeadLockHandler implements MythDeadLockAble { private final String id; - private final Lock locks = new ReentrantLock(); + private final Lock lock = new ReentrantLock(); public DeadLockHandler(String id) { this.id = id; @@ -23,56 +25,61 @@ public String getId() { return id; } - public void preparRun(Food food, DeadLockHandler lock) { + public void prepareRun(Food food, MythDeadLockAble lock) { boolean required = false; boolean done = false; while (!done) { int wait = (int) (Math.random() * 10); try { -// Thread.sleep(2000); - required = locks.tryLock(wait, TimeUnit.MILLISECONDS); // 尝试与锁定,超时时长随机 + required = this.lock.tryLock(wait, TimeUnit.MILLISECONDS); // 尝试与锁定,超时时长随机 if (required) { - System.out.println( - "准备 currentId: " + id + " resource: " + food.getName() + " prepar other: " + lock - .getId() + System.currentTimeMillis()); - done = lock.confirmRun(food, this); // 检查返回值 + log.info("{} 准备 currentId: {} resource: {} prepare other: {}", + System.currentTimeMillis(), id, food.getName(), lock.getId()); + done = lock.confirmRun2(food, this); // 检查返回值 } else { Thread.sleep(wait); } } catch (InterruptedException e) { + log.error("", e); } finally { if (done) { - locks.unlock(); // 如果done为假,释放锁并等待 + this.lock.unlock(); // 如果done为假,释放锁并等待 } } if (!done) { try { Thread.sleep(wait); } catch (InterruptedException e) { + log.error("", e); } } } } - public boolean confirmRun(Food food, DeadLockHandler lock) { + @Override + public void confirmRun(Food food, MythDeadLockAble lock) { + + } + + @Override + public boolean confirmRun2(Food food, MythDeadLockAble lock) { boolean required = false; try { int wait = (int) (Math.random() * 10); - required = locks.tryLock(wait, TimeUnit.MILLISECONDS); + required = this.lock.tryLock(wait, TimeUnit.MILLISECONDS); if (required) { - System.out.println( - "确认 currentId: " + id + " resource: " + food.getName() + "confirm other: " + lock - .getId() + System.currentTimeMillis()); + log.info("{} 确认 currentId: {} resource: {} confirm other: {}", + System.currentTimeMillis(), id, food.getName(), lock.getId()); return true; } } catch (InterruptedException e) { + log.error("", e); } finally { if (required) { - locks.unlock(); + this.lock.unlock(); } } return false; } - } diff --git a/concurrency/src/main/java/com/github/kuangcp/lock/dead/DeadLockHandlerDemo.java b/concurrency/src/main/java/com/github/kuangcp/lock/dead/DeadLockHandlerDemo.java deleted file mode 100644 index 29418872..00000000 --- a/concurrency/src/main/java/com/github/kuangcp/lock/dead/DeadLockHandlerDemo.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.github.kuangcp.lock.dead; - -import com.github.kuangcp.old.Food; - -/** - * Created by https://github.com/kuangcp on 17-8-14 下午9:16 - * 解决死锁: - * 如果在取得第二个锁的时候失败了,就将第一个锁释放掉,从头开始等待 - * 但是还是会有问题,并不是书上说的那么美好,如果遇到锁就释放,然后等待,两个线程都是这样的情况, - * 然后就不停的释放,等待,加锁,死锁在了各自的第一个方法上,控制台就不停的刷准备方法的内容 - */ -public class DeadLockHandlerDemo { - - public static void main(String[] s) { - - DeadLockHandler one = new DeadLockHandler("one"); - DeadLockHandler other = new DeadLockHandler("other"); - Food foodA = new Food("a"); - Food foodB = new Food("b"); - - new Thread(() -> one.preparRun(foodA, other)).start(); - new Thread(() -> other.preparRun(foodB, one)).start(); - } -} - diff --git a/concurrency/src/main/java/com/github/kuangcp/lock/dead/DeadLockRewrite.java b/concurrency/src/main/java/com/github/kuangcp/lock/dead/DeadLockRewrite.java index e4113a3c..e4d60443 100644 --- a/concurrency/src/main/java/com/github/kuangcp/lock/dead/DeadLockRewrite.java +++ b/concurrency/src/main/java/com/github/kuangcp/lock/dead/DeadLockRewrite.java @@ -3,13 +3,15 @@ import com.github.kuangcp.old.Food; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; +import lombok.extern.slf4j.Slf4j; /** * created by https://gitee.com/gin9 * * @author kuangcp on 3/4/19-8:03 AM */ -class DeadLockRewrite { +@Slf4j +class DeadLockRewrite implements MythDeadLockAble { private final String id; private final Lock locks = new ReentrantLock(); @@ -18,16 +20,15 @@ class DeadLockRewrite { this.id = id; } - private String getId() { + public String getId() { return id; } - void prepareRun(Food food, DeadLockRewrite lock) { + public void prepareRun(Food food, MythDeadLockAble lock) { locks.lock(); // 线程先锁住自己的锁 try { - System.out.println( - "准备 currentId: " + id + " resource: " + food.getName() + " prepar other: " + lock.getId() - + System.currentTimeMillis()); + log.info("{} 准备 currentId: {} resource: {} prepare other: {}", + System.currentTimeMillis(), id, food.getName(), lock.getId()); try { Thread.sleep(4000); } catch (InterruptedException e) { @@ -39,14 +40,18 @@ void prepareRun(Food food, DeadLockRewrite lock) { lock.confirmRun(food, this); } - private void confirmRun(Food food, DeadLockRewrite lock) { + public void confirmRun(Food food, MythDeadLockAble lock) { locks.lock(); // 尝试锁住其他线程 正是这里可能出现死锁,因为这个其他线程已经加锁这里就死锁了 try { - System.out.println( - "确认 currentId: " + id + " resource: " + food.getName() + "confirm other: " + lock.getId() - + System.currentTimeMillis()); + log.info("{} 确认 currentId: {} resource: {} confirm other: {}", + System.currentTimeMillis(), id, food.getName(), lock.getId()); } finally { locks.unlock(); } } + + @Override + public boolean confirmRun2(Food food, MythDeadLockAble lock) { + return false; + } } diff --git a/concurrency/src/main/java/com/github/kuangcp/lock/dead/DeadLockRewriteDemo.java b/concurrency/src/main/java/com/github/kuangcp/lock/dead/DeadLockRewriteDemo.java deleted file mode 100644 index cbbe3a56..00000000 --- a/concurrency/src/main/java/com/github/kuangcp/lock/dead/DeadLockRewriteDemo.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.github.kuangcp.lock.dead; - -import com.github.kuangcp.old.Food; - -/** - * Created by https://github.com/kuangcp on 17-8-13 下午10:23 - * 使用并发包重写死锁案例: - * 但是使用的时候,似乎lock方法会一直尝试获得锁,睡眠时间决定了死锁状况的长短(这应该不算死锁,只是等待)准备到确认的延时恰好是睡眠时间 - * 根据 API 知道这个lock如果发现资源被别的线程锁住,他就会休眠,等待锁的释放,所以这里的写法是不会有死锁的,可能7和8的区别?书上用的是7,说的是死锁 - */ -public class DeadLockRewriteDemo { - - public static void main(String[] s) { - - DeadLockRewrite one = new DeadLockRewrite("one"); - DeadLockRewrite other = new DeadLockRewrite("other"); - Food foodA = new Food("a"); - Food foodB = new Food("b"); - - new Thread(() -> one.prepareRun(foodA, other)).start(); - new Thread(() -> other.prepareRun(foodB, one)).start(); - } -} - diff --git a/concurrency/src/main/java/com/github/kuangcp/lock/dead/MythDeadLockAble.java b/concurrency/src/main/java/com/github/kuangcp/lock/dead/MythDeadLockAble.java new file mode 100644 index 00000000..4c4a06d3 --- /dev/null +++ b/concurrency/src/main/java/com/github/kuangcp/lock/dead/MythDeadLockAble.java @@ -0,0 +1,17 @@ +package com.github.kuangcp.lock.dead; + +import com.github.kuangcp.old.Food; + +/** + * @author https://github.com/kuangcp on 2020-03-16 00:47 + */ +public interface MythDeadLockAble { + + void prepareRun(Food food, MythDeadLockAble lock); + + String getId(); + + void confirmRun(Food food, MythDeadLockAble lock); + + boolean confirmRun2(Food food, MythDeadLockAble lock); +} diff --git a/concurrency/src/main/java/com/github/kuangcp/lock/pvp/Player.java b/concurrency/src/main/java/com/github/kuangcp/lock/pvp/Player.java index 3a43f9b1..f5e82dc2 100644 --- a/concurrency/src/main/java/com/github/kuangcp/lock/pvp/Player.java +++ b/concurrency/src/main/java/com/github/kuangcp/lock/pvp/Player.java @@ -2,7 +2,8 @@ /** * @author kuangcp on 3/28/19-9:32 PM - * two lock + * two lock p1 p2 lock p1 p2 un lock + * TODO lock sort and unlock sort */ class Player { diff --git a/concurrency/src/main/java/com/github/kuangcp/old/Food.java b/concurrency/src/main/java/com/github/kuangcp/old/Food.java index e07b3d55..8bac1778 100644 --- a/concurrency/src/main/java/com/github/kuangcp/old/Food.java +++ b/concurrency/src/main/java/com/github/kuangcp/old/Food.java @@ -3,14 +3,15 @@ /** * Created by https://github.com/kuangcp on 17-8-13 下午10:27 */ -public class Food{ - String name; +public class Food { - public Food(String name) { - this.name = name; - } + String name; - public String getName() { - return name; - } + public Food(String name) { + this.name = name; + } + + public String getName() { + return name; + } } diff --git a/concurrency/src/test/java/com/github/kuangcp/lock/dead/DeadLockTest.java b/concurrency/src/test/java/com/github/kuangcp/lock/dead/DeadLockTest.java new file mode 100644 index 00000000..2f1f3a90 --- /dev/null +++ b/concurrency/src/test/java/com/github/kuangcp/lock/dead/DeadLockTest.java @@ -0,0 +1,70 @@ +package com.github.kuangcp.lock.dead; + +import com.github.kuangcp.old.Food; +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; + +/** + * @author https://github.com/kuangcp on 2020-03-16 00:22 + * + * 尝试 解决死锁 + * 当遇到锁进行休眠,但是没有释放已经占用的锁,所以还是很大几率死锁 + */ +@Slf4j +public class DeadLockTest { + + @Test + public void testDeadLock() throws Exception { + DeadLock one = new DeadLock("one"); + DeadLock other = new DeadLock("other"); + commonLogic(one, other); + } + + + /** + * 解决死锁: + * 如果在取得第二个锁的时候失败了,就将第一个锁释放掉,从头开始等待 + * 但是还是会有问题,并不是书上说的那么美好,如果遇到锁就释放,然后等待,两个线程都是这样的情况, + * 然后就不停的释放,等待,加锁,死锁在了各自的第一个方法上,控制台就不停的刷准备方法的内容 + */ + @Test + public void testHandle() throws Exception { + DeadLockHandler one = new DeadLockHandler("one"); + DeadLockHandler other = new DeadLockHandler("other"); + + commonLogic(one, other); + } + + /** + * 使用并发包重写死锁案例: + * + * 但是使用的时候,似乎lock方法会一直尝试获得锁,睡眠时间决定了死锁状况的长短 + * (这应该不算死锁,只是等待)准备到确认的延时恰好是睡眠时间 + * 根据 API 知道这个lock如果发现资源被别的线程锁住,他就会休眠,等待锁的释放,所以这里的写法是不会有死锁的, + * 可能7和8的区别?书上用的是7,说的是死锁 + */ + @Test + public void testRewrite() throws Exception { + DeadLockRewrite one = new DeadLockRewrite("one"); + DeadLockRewrite other = new DeadLockRewrite("other"); + commonLogic(one, other); + } + + private void commonLogic(MythDeadLockAble one, MythDeadLockAble other) throws Exception { + Food foodA = new Food("a"); + Food foodB = new Food("b"); + + new Thread(() -> { + one.prepareRun(foodA, other); + log.info("end"); + }).start(); + + new Thread(() -> { + other.prepareRun(foodB, one); + log.info("end"); + }).start(); + + Thread.currentThread().join(60000); + log.info("thread end"); + } +} diff --git a/gui/Readme.md b/gui/Readme.md new file mode 100644 index 00000000..31c083de --- /dev/null +++ b/gui/Readme.md @@ -0,0 +1,6 @@ +# GUI + +## GTK +> [java-gnome](http://java-gnome.sourceforge.net/README.html) + + diff --git a/spring/src/test/java/com/github/kuangcp/di/xml/constructor/Readme.md b/spring/src/test/java/com/github/kuangcp/di/xml/constructor/Readme.md new file mode 100644 index 00000000..882f6cd4 --- /dev/null +++ b/spring/src/test/java/com/github/kuangcp/di/xml/constructor/Readme.md @@ -0,0 +1 @@ +- [ ] 循环依赖 From 408e0b85e2b0e8bb1be6eb635f668e3e2936a063 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Tue, 17 Mar 2020 00:11:50 +0800 Subject: [PATCH 149/476] +) test dead lock --- .../com/github/kuangcp/lock/pvp/Player.java | 6 + .../github/kuangcp/lock/pvp/PlayerTest.java | 191 ++++++++++++++++-- 2 files changed, 175 insertions(+), 22 deletions(-) diff --git a/concurrency/src/main/java/com/github/kuangcp/lock/pvp/Player.java b/concurrency/src/main/java/com/github/kuangcp/lock/pvp/Player.java index f5e82dc2..f6e11407 100644 --- a/concurrency/src/main/java/com/github/kuangcp/lock/pvp/Player.java +++ b/concurrency/src/main/java/com/github/kuangcp/lock/pvp/Player.java @@ -1,10 +1,16 @@ package com.github.kuangcp.lock.pvp; +import java.util.concurrent.locks.ReentrantLock; +import lombok.Data; + /** * @author kuangcp on 3/28/19-9:32 PM * two lock p1 p2 lock p1 p2 un lock * TODO lock sort and unlock sort */ +@Data class Player { + private ReentrantLock lock = new ReentrantLock(); + } diff --git a/concurrency/src/test/java/com/github/kuangcp/lock/pvp/PlayerTest.java b/concurrency/src/test/java/com/github/kuangcp/lock/pvp/PlayerTest.java index 62b11321..288998fb 100644 --- a/concurrency/src/test/java/com/github/kuangcp/lock/pvp/PlayerTest.java +++ b/concurrency/src/test/java/com/github/kuangcp/lock/pvp/PlayerTest.java @@ -1,41 +1,188 @@ package com.github.kuangcp.lock.pvp; -import java.util.concurrent.TimeUnit; +import com.github.kuangcp.tuple.Tuple2; +import java.time.Duration; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.locks.ReentrantLock; +import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.Test; /** * @author kuangcp on 2019-04-21 11:14 AM */ +@Slf4j public class PlayerTest { + private Map playerMap = new HashMap<>(); + private ReentrantLock lock = new ReentrantLock(); - // TODO 锁 + private void arenaLogic(Player a, Player b) throws InterruptedException { + log.info("enter arena: a={} b={}", a, b); + Thread.sleep(ThreadLocalRandom.current().nextInt(40)); + } + + private void init() { + Player a = new Player(); + Player b = new Player(); + Player c = new Player(); + + playerMap.put("a", a); + playerMap.put("b", b); + playerMap.put("c", c); + } + + private Tuple2 randomPlayer() { + ArrayList list = new ArrayList<>(playerMap.keySet()); + + int aIndex = 0; + int bIndex = 0; + while (aIndex == bIndex) { + aIndex = ThreadLocalRandom.current().nextInt(list.size()); + bIndex = ThreadLocalRandom.current().nextInt(list.size()); + } + + String aId = list.get(aIndex); + String bId = list.get(bIndex); + return Tuple2.of(aId, bId); + } + + @Test + public void testDeadLock() throws Exception { + init(); + ExecutorService pool = Executors.newFixedThreadPool(6); + + for (int i = 0; i < 100; i++) { + pool.submit(() -> { + Tuple2 playerPair = this.randomPlayer(); + String aId = playerPair.getFirst(); + String bId = playerPair.getSecond(); + Player a = playerMap.get(aId); + Player b = playerMap.get(bId); + + log.debug("{} {} prepare enter arena", aId, bId); + + boolean enter = false; + try { + a.getLock().lock(); + b.getLock().lock(); + + enter = true; + log.info("enter ids: a={} b={}", aId, bId); + arenaLogic(a, b); + } catch (Exception e) { + log.error("", e); + } finally { + if (enter) { + a.getLock().unlock(); + b.getLock().unlock(); + log.info("{} {} exit arena", aId, bId); + } + } + }); + Thread.sleep(10); + } + log.warn("end loop"); + + Thread.currentThread().join(Duration.ofMinutes(10).toMillis()); + } + + /** + * 保证获取锁时的顺序,释放锁的顺序无所谓 + */ + @Test + public void testSortLock() throws Exception { + init(); + ExecutorService pool = Executors.newFixedThreadPool(6); + + for (int i = 0; i < 100000; i++) { + pool.submit(() -> { + Tuple2 playerPair = this.randomPlayer(); + String aId = playerPair.getFirst(); + String bId = playerPair.getSecond(); + + if (aId.compareTo(bId) > 0) { + String temp = aId; + aId = bId; + bId = temp; + } + + Player a = playerMap.get(aId); + Player b = playerMap.get(bId); + + log.debug("{} {} prepare enter arena", aId, bId); + + boolean enter = false; + try { + a.getLock().lock(); + b.getLock().lock(); + + enter = true; + log.info("enter ids: a={} b={}", aId, bId); + arenaLogic(a, b); + } catch (Exception e) { + log.error("", e); + } finally { + if (enter) { + a.getLock().unlock(); + b.getLock().unlock(); + log.info("{} {} exit arena", aId, bId); + } + } + }); + Thread.sleep(10); + } + log.warn("end loop"); + + Thread.currentThread().join(Duration.ofMinutes(10).toMillis()); + } + @Test - public void testRead() throws InterruptedException { + public void testTryThenLock() throws Exception { + init(); + ExecutorService pool = Executors.newFixedThreadPool(6); + + for (int i = 0; i < 1000; i++) { + pool.submit(() -> { + Tuple2 playerPair = this.randomPlayer(); + String aId = playerPair.getFirst(); + String bId = playerPair.getSecond(); + Player a = playerMap.get(aId); + Player b = playerMap.get(bId); + + log.debug("{} {} prepare enter arena", aId, bId); - Thread thread = new Thread(() -> { - for (int i = 0; i < 40; i++) { + boolean enter = false; try { - Thread.sleep(100); - } catch (InterruptedException e) { - e.printStackTrace(); + + if (a.getLock().tryLock() && b.getLock().tryLock()) { + a.getLock().lock(); + b.getLock().lock(); + enter = true; + log.info("enter ids: a={} b={}", aId, bId); + arenaLogic(a, b); + } else { + log.warn("failed get lock"); + } + } catch (Exception e) { + log.error("", e); + } finally { + if (enter) { + a.getLock().unlock(); + b.getLock().unlock(); + log.info("{} {} exit arena", aId, bId); + } } - lock.lock(); - } - }); - thread.setDaemon(true); - thread.start(); - - TimeUnit.SECONDS.sleep(2); - try { - // 如果这里获取锁失败, finally 里又去释放锁, 会得到IllegalMonitorStateException, 掩盖了这里获取锁的异常 - lock.lock(); - } catch (Exception e) { - e.printStackTrace(); - } finally { - lock.unlock(); + }); + Thread.sleep(10); } + + log.warn("end loop"); + Thread.currentThread().join(Duration.ofMinutes(10).toMillis()); } } From d18a3cafc1f329ab3245bdace359f1467eb54f67 Mon Sep 17 00:00:00 2001 From: kcp Date: Thu, 19 Mar 2020 18:03:21 +0800 Subject: [PATCH 150/476] x) compile for gradle 6.2.2 --- mybatis/build.gradle | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/mybatis/build.gradle b/mybatis/build.gradle index 4d79d185..68214cca 100644 --- a/mybatis/build.gradle +++ b/mybatis/build.gradle @@ -1,18 +1,8 @@ -buildscript { - repositories { - mavenCentral() - } - dependencies { - classpath("org.springframework.boot:spring-boot-gradle-plugin:2.1.6.RELEASE") - } +plugins { + id("org.springframework.boot") version "2.2.5.RELEASE" + id("io.spring.dependency-management") version "1.0.9.RELEASE" } -apply plugin: 'java' -apply plugin: 'eclipse' -apply plugin: 'idea' -apply plugin: 'org.springframework.boot' -apply plugin: 'io.spring.dependency-management' - repositories { mavenCentral() } From 71a6e07ea1ab455b3359124822400bc650aaf3ee Mon Sep 17 00:00:00 2001 From: kcp Date: Fri, 20 Mar 2020 10:03:59 +0800 Subject: [PATCH 151/476] +) random --- .../github/kuangcp/random/MT19937Random.java | 213 ++++++++++++++++++ .../kuangcp/random/MT19937RandomTest.java | 26 +++ 2 files changed, 239 insertions(+) create mode 100644 algorithms/src/main/java/com/github/kuangcp/random/MT19937Random.java create mode 100644 algorithms/src/test/java/com/github/kuangcp/random/MT19937RandomTest.java diff --git a/algorithms/src/main/java/com/github/kuangcp/random/MT19937Random.java b/algorithms/src/main/java/com/github/kuangcp/random/MT19937Random.java new file mode 100644 index 00000000..8e6666de --- /dev/null +++ b/algorithms/src/main/java/com/github/kuangcp/random/MT19937Random.java @@ -0,0 +1,213 @@ +package com.github.kuangcp.random; + +/** + * @author https://github.com/kuangcp on 2020-03-19 18:09 + */ +public class MT19937Random { + + /** + * Auto-generated serial version UID. Note that MTRandomTest does NOT support serialisation of its + * internal state and it may even be necessary to implement read/write methods to re-seed it + * properly. This is only here to make Eclipse shut up about it being missing. + */ + // Constants used in the original C implementation + private final static int UPPER_MASK = 0x80000000; + private final static int LOWER_MASK = 0x7fffffff; + private final static int N = 624; + private final static int M = 397; + private final static int[] MAGIC = {0x0, 0x9908b0df}; + private final static int MAGIC_FACTOR1 = 1812433253; + private final static int MAGIC_FACTOR2 = 1664525; + private final static int MAGIC_FACTOR3 = 1566083941; + private final static int MAGIC_MASK1 = 0x9d2c5680; + private final static int MAGIC_MASK2 = 0xefc60000; + private final static int MAGIC_SEED = 19650218; + + // Internal state + private transient int[] mt; + private transient int mti; + private transient boolean compact = false; + + // Temporary buffer used during setSeed(long) + private transient int[] ibuf; + + public MT19937Random(long seed) { + setSeed(seed); + } + + + /** + * Initializes mt[N] with a simple integer seed. This method is required as part of the Mersenne + * Twister algorithm but need not be made public. + */ + private void setSeed(int seed) { + + // Annoying runtime check for initialisation of internal data + // caused by java.util.Random invoking setSeed() during init. + // This is unavoidable because no fields in our instance will + // have been initialised at this point, not even if the code + // were placed at the declaration of the member variable. + if (mt == null) { + mt = new int[N]; + } + + // ---- Begin Mersenne Twister Algorithm ---- + mt[0] = seed; + for (mti = 1; mti < N; mti++) { + mt[mti] = (MAGIC_FACTOR1 * (mt[mti - 1] ^ (mt[mti - 1] >>> 30)) + mti); + } + // ---- End Mersenne Twister Algorithm ---- + } + + /** + * This method resets the state of this instance using the 64 bits of seed data provided. Note + * that if the same seed data is passed to two different instances of MTRandomTest (both of which + * share the same compatibility state) then the sequence of numbers generated by both instances + * will be identical. + *

+ * If this instance was initialised in 'compatibility' mode then this method will only use the + * lower 32 bits of any seed value passed in and will match the behaviour of the original C code + * exactly with respect to state initialisation. + * + * @param seed The 64 bit value used to initialise the random number generator state. + */ + public final synchronized void setSeed(long seed) { + if (compact) { + setSeed((int) seed); + } else { + // Annoying runtime check for initialisation of internal data + // caused by java.util.Random invoking setSeed() during init. + // This is unavoidable because no fields in our instance will + // have been initialised at this point, not even if the code + // were placed at the declaration of the member variable. + if (ibuf == null) { + ibuf = new int[2]; + } + + ibuf[0] = (int) seed; + ibuf[1] = (int) (seed >>> 32); + setSeed(ibuf); + } + } + + /** + * This method resets the state of this instance using the integer array of seed data provided. + * This is the canonical way of resetting the pseudo random number sequence. + * + * @param buf The non-empty integer array of seed information. + * @throws NullPointerException if the buffer is null. + * @throws IllegalArgumentException if the buffer has zero length. + */ + public final synchronized void setSeed(int[] buf) { + int length = buf.length; + + if (length == 0) { + throw new IllegalArgumentException("Seed buffer may not be empty"); + } + + // ---- Begin Mersenne Twister Algorithm ---- + int i = 1, j = 0, k = (Math.max(N, length)); + setSeed(MAGIC_SEED); + + for (; k > 0; k--) { + mt[i] = (mt[i] ^ ((mt[i - 1] ^ (mt[i - 1] >>> 30)) * MAGIC_FACTOR2)) + buf[j] + j; + i++; + j++; + + if (i >= N) { + mt[0] = mt[N - 1]; + i = 1; + } + + if (j >= length) { + j = 0; + } + } + + for (k = N - 1; k > 0; k--) { + mt[i] = (mt[i] ^ ((mt[i - 1] ^ (mt[i - 1] >>> 30)) * MAGIC_FACTOR3)) - i; + i++; + if (i >= N) { + mt[0] = mt[N - 1]; + i = 1; + } + } + + mt[0] = UPPER_MASK; + // ---- End Mersenne Twister Algorithm ---- + } + + /** + * This method forms the basis for generating a pseudo random number sequence from this class. If + * given a value of 32, this method behaves identically to the genrand_int32 function in the + * original C code and ensures that using the standard nextInt() function (inherited from Random) + * we are able to replicate behaviour exactly. + *

+ * Note that where the number of bits requested is not equal to 32 then bits will simply be masked + * out from the top of the returned integer value. That is to say that: + *

+ * + *

+   * mt.setSeed(12345);
+   * int foo = mt.nextInt(16) + (mt.nextInt(16) << 16);
+   * 
+ *

+ *

+ * will not give the same result as + *

+ * + *

+   * mt.setSeed(12345);
+   * int foo = mt.next(32);
+   * 
+ *

+ * The number of significant bits desired in the output. + * + * @return The next value in the pseudo random sequence with the specified number of bits in the + * lower part of the integer. + */ + public synchronized int next() { + return next(32); + } + + public synchronized int next(int bits) { + // ---- Begin Mersenne Twister Algorithm ---- + int y, kk; + + if (mti >= N) { + // generate N words at one time + // In the original C implementation, mti is checked here + // to determine if initialisation has occurred; if not + // it initialises this instance with DEFAULT_SEED (5489). + // This is no longer necessary as initialisation of the + // Java instance must result in initialisation occurring + // Use the constructor MTRandomTest(true) to enable backwards + // compatible behaviour. + + for (kk = 0; kk < N - M; kk++) { + y = (mt[kk] & UPPER_MASK) | (mt[kk + 1] & LOWER_MASK); + mt[kk] = mt[kk + M] ^ (y >>> 1) ^ MAGIC[y & 0x1]; + } + + for (; kk < N - 1; kk++) { + y = (mt[kk] & UPPER_MASK) | (mt[kk + 1] & LOWER_MASK); + mt[kk] = mt[kk + (M - N)] ^ (y >>> 1) ^ MAGIC[y & 0x1]; + } + + y = (mt[N - 1] & UPPER_MASK) | (mt[0] & LOWER_MASK); + mt[N - 1] = mt[M - 1] ^ (y >>> 1) ^ MAGIC[y & 0x1]; + + mti = 0; + } + + y = mt[mti++]; + + // Tempering + y ^= (y >>> 11); + y ^= (y << 7) & MAGIC_MASK1; + y ^= (y << 15) & MAGIC_MASK2; + y ^= (y >>> 18); + // ---- End Mersenne Twister Algorithm ---- + return Math.abs((y >>> (32 - bits))); + } +} diff --git a/algorithms/src/test/java/com/github/kuangcp/random/MT19937RandomTest.java b/algorithms/src/test/java/com/github/kuangcp/random/MT19937RandomTest.java new file mode 100644 index 00000000..c1503616 --- /dev/null +++ b/algorithms/src/test/java/com/github/kuangcp/random/MT19937RandomTest.java @@ -0,0 +1,26 @@ +package com.github.kuangcp.random; + +import static org.hamcrest.Matchers.equalTo; +import static org.junit.Assert.assertThat; + +import java.util.HashSet; +import java.util.Set; +import org.junit.Test; + +/** + * @author https://github.com/kuangcp on 2020-03-20 09:58 + */ +public class MT19937RandomTest { + + @Test + public void testRandom() { + int maxSize = 100000; + Set pool = new HashSet<>(); + for (int i = 0; i < maxSize; i++) { + MT19937Random random = new MT19937Random(System.nanoTime()); + pool.add(random.next()); + } + + assertThat(pool.size(), equalTo(maxSize)); + } +} From 0ff943c09b30d6def89199a1888a46359431c575 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Fri, 20 Mar 2020 22:56:06 +0800 Subject: [PATCH 152/476] -) too simple code --- .../github/kuangcp/list/ShowCopyOnWrite.java | 1 - .../github/kuangcp/lock/dead/DeadLock.java | 69 --------------- .../kuangcp/lock/dead/DeadLockHandler.java | 85 ------------------- .../kuangcp/lock/dead/DeadLockRewrite.java | 57 ------------- .../kuangcp/lock/dead/MythDeadLockAble.java | 17 ---- .../com/github/kuangcp/lock/pvp/Player.java | 2 - .../com/github/kuangcp/lock/sync/Action.java | 49 ----------- .../github/kuangcp/lock/sync/SleepDemo.java | 1 - .../com/github/kuangcp/old/BuildFactory.java | 11 ++- .../kuangcp/old/CreateObjByBuilder.groovy | 57 ------------- .../java/com/github/kuangcp/old/DeadLock.java | 33 ------- .../com/github/kuangcp/old/DeadLockShow.java | 46 ---------- .../java/com/github/kuangcp/old/Food.java | 17 ---- .../com/github/kuangcp/old/MainThread.java | 11 --- .../com/github/kuangcp/old/ObjBuilder.java | 11 --- .../kuangcp/lock/dead/DeadLockTest.java | 70 --------------- .../github/kuangcp/lock/pvp/PlayerTest.java | 5 +- .../github/kuangcp/lock/sync/ActionTest.java | 37 -------- mybatis/build.gradle | 6 +- 19 files changed, 11 insertions(+), 574 deletions(-) delete mode 100644 concurrency/src/main/java/com/github/kuangcp/lock/dead/DeadLock.java delete mode 100644 concurrency/src/main/java/com/github/kuangcp/lock/dead/DeadLockHandler.java delete mode 100644 concurrency/src/main/java/com/github/kuangcp/lock/dead/DeadLockRewrite.java delete mode 100644 concurrency/src/main/java/com/github/kuangcp/lock/dead/MythDeadLockAble.java delete mode 100644 concurrency/src/main/java/com/github/kuangcp/lock/sync/Action.java delete mode 100644 concurrency/src/main/java/com/github/kuangcp/old/CreateObjByBuilder.groovy delete mode 100644 concurrency/src/main/java/com/github/kuangcp/old/DeadLock.java delete mode 100644 concurrency/src/main/java/com/github/kuangcp/old/DeadLockShow.java delete mode 100644 concurrency/src/main/java/com/github/kuangcp/old/Food.java delete mode 100644 concurrency/src/main/java/com/github/kuangcp/old/MainThread.java delete mode 100644 concurrency/src/main/java/com/github/kuangcp/old/ObjBuilder.java delete mode 100644 concurrency/src/test/java/com/github/kuangcp/lock/dead/DeadLockTest.java delete mode 100644 concurrency/src/test/java/com/github/kuangcp/lock/sync/ActionTest.java diff --git a/concurrency/src/main/java/com/github/kuangcp/list/ShowCopyOnWrite.java b/concurrency/src/main/java/com/github/kuangcp/list/ShowCopyOnWrite.java index 97679127..6650662f 100644 --- a/concurrency/src/main/java/com/github/kuangcp/list/ShowCopyOnWrite.java +++ b/concurrency/src/main/java/com/github/kuangcp/list/ShowCopyOnWrite.java @@ -1,6 +1,5 @@ package com.github.kuangcp.list; -import com.github.kuangcp.list.ElementList; import com.github.kuangcp.old.BuildFactory; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CountDownLatch; diff --git a/concurrency/src/main/java/com/github/kuangcp/lock/dead/DeadLock.java b/concurrency/src/main/java/com/github/kuangcp/lock/dead/DeadLock.java deleted file mode 100644 index 81c3aef0..00000000 --- a/concurrency/src/main/java/com/github/kuangcp/lock/dead/DeadLock.java +++ /dev/null @@ -1,69 +0,0 @@ -package com.github.kuangcp.lock.dead; - -import com.github.kuangcp.old.Food; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; -import lombok.extern.slf4j.Slf4j; - -/** - * created by https://gitee.com/gin9 - * - * @author kuangcp on 3/4/19-7:52 AM - */ -@Slf4j -class DeadLock implements MythDeadLockAble { - - private final String id; - - private final Lock locks = new ReentrantLock(); - - DeadLock(String id) { - this.id = id; - } - - public String getId() { - return id; - } - - public void prepareRun(Food food, MythDeadLockAble lock) { - boolean required = false; - while (!required) { - try { - int wait = (int) (Math.random() * 10); - required = locks.tryLock(wait, TimeUnit.MILLISECONDS); // 尝试与锁定,超时时长随机 - if (required) { - log.info("prepare: currentId={} resource={} lock={} time={}", - id, food.getName(), lock.getId(), System.currentTimeMillis()); - - lock.confirmRun(food, this); // 给主线程确认 - } else { - Thread.sleep(wait); - } - - } catch (InterruptedException e) { - log.error(e.getMessage(), e); - } finally { - if (required) { - locks.unlock(); // 如果锁定就解锁 - } - } - } - } - - @Override - public void confirmRun(Food food, MythDeadLockAble lock) { - locks.lock(); // 尝试锁住其他线程 正是这里可能出现死锁,因为这个其他线程已经加锁这里就死锁了 - try { - log.info("run: currentId={} resource={} lock={} time={}", - id, food.getName(), lock.getId(), System.currentTimeMillis()); - } finally { - locks.unlock(); - } - } - - @Override - public boolean confirmRun2(Food food, MythDeadLockAble lock) { - return false; - } -} \ No newline at end of file diff --git a/concurrency/src/main/java/com/github/kuangcp/lock/dead/DeadLockHandler.java b/concurrency/src/main/java/com/github/kuangcp/lock/dead/DeadLockHandler.java deleted file mode 100644 index ac264c97..00000000 --- a/concurrency/src/main/java/com/github/kuangcp/lock/dead/DeadLockHandler.java +++ /dev/null @@ -1,85 +0,0 @@ -package com.github.kuangcp.lock.dead; - -import com.github.kuangcp.old.Food; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; -import lombok.extern.slf4j.Slf4j; - -/** - * created by https://gitee.com/gin9 - * - * @author kuangcp on 3/4/19-8:03 AM - */ -@Slf4j -class DeadLockHandler implements MythDeadLockAble { - - private final String id; - private final Lock lock = new ReentrantLock(); - - public DeadLockHandler(String id) { - this.id = id; - } - - public String getId() { - return id; - } - - public void prepareRun(Food food, MythDeadLockAble lock) { - boolean required = false; - boolean done = false; - - while (!done) { - int wait = (int) (Math.random() * 10); - try { - required = this.lock.tryLock(wait, TimeUnit.MILLISECONDS); // 尝试与锁定,超时时长随机 - if (required) { - log.info("{} 准备 currentId: {} resource: {} prepare other: {}", - System.currentTimeMillis(), id, food.getName(), lock.getId()); - done = lock.confirmRun2(food, this); // 检查返回值 - } else { - Thread.sleep(wait); - } - } catch (InterruptedException e) { - log.error("", e); - } finally { - if (done) { - this.lock.unlock(); // 如果done为假,释放锁并等待 - } - } - if (!done) { - try { - Thread.sleep(wait); - } catch (InterruptedException e) { - log.error("", e); - } - } - } - } - - @Override - public void confirmRun(Food food, MythDeadLockAble lock) { - - } - - @Override - public boolean confirmRun2(Food food, MythDeadLockAble lock) { - boolean required = false; - try { - int wait = (int) (Math.random() * 10); - required = this.lock.tryLock(wait, TimeUnit.MILLISECONDS); - if (required) { - log.info("{} 确认 currentId: {} resource: {} confirm other: {}", - System.currentTimeMillis(), id, food.getName(), lock.getId()); - return true; - } - } catch (InterruptedException e) { - log.error("", e); - } finally { - if (required) { - this.lock.unlock(); - } - } - return false; - } -} diff --git a/concurrency/src/main/java/com/github/kuangcp/lock/dead/DeadLockRewrite.java b/concurrency/src/main/java/com/github/kuangcp/lock/dead/DeadLockRewrite.java deleted file mode 100644 index e4d60443..00000000 --- a/concurrency/src/main/java/com/github/kuangcp/lock/dead/DeadLockRewrite.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.github.kuangcp.lock.dead; - -import com.github.kuangcp.old.Food; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; -import lombok.extern.slf4j.Slf4j; - -/** - * created by https://gitee.com/gin9 - * - * @author kuangcp on 3/4/19-8:03 AM - */ -@Slf4j -class DeadLockRewrite implements MythDeadLockAble { - - private final String id; - private final Lock locks = new ReentrantLock(); - - DeadLockRewrite(String id) { - this.id = id; - } - - public String getId() { - return id; - } - - public void prepareRun(Food food, MythDeadLockAble lock) { - locks.lock(); // 线程先锁住自己的锁 - try { - log.info("{} 准备 currentId: {} resource: {} prepare other: {}", - System.currentTimeMillis(), id, food.getName(), lock.getId()); - try { - Thread.sleep(4000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } finally { - locks.unlock(); - } - lock.confirmRun(food, this); - } - - public void confirmRun(Food food, MythDeadLockAble lock) { - locks.lock(); // 尝试锁住其他线程 正是这里可能出现死锁,因为这个其他线程已经加锁这里就死锁了 - try { - log.info("{} 确认 currentId: {} resource: {} confirm other: {}", - System.currentTimeMillis(), id, food.getName(), lock.getId()); - } finally { - locks.unlock(); - } - } - - @Override - public boolean confirmRun2(Food food, MythDeadLockAble lock) { - return false; - } -} diff --git a/concurrency/src/main/java/com/github/kuangcp/lock/dead/MythDeadLockAble.java b/concurrency/src/main/java/com/github/kuangcp/lock/dead/MythDeadLockAble.java deleted file mode 100644 index 4c4a06d3..00000000 --- a/concurrency/src/main/java/com/github/kuangcp/lock/dead/MythDeadLockAble.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.github.kuangcp.lock.dead; - -import com.github.kuangcp.old.Food; - -/** - * @author https://github.com/kuangcp on 2020-03-16 00:47 - */ -public interface MythDeadLockAble { - - void prepareRun(Food food, MythDeadLockAble lock); - - String getId(); - - void confirmRun(Food food, MythDeadLockAble lock); - - boolean confirmRun2(Food food, MythDeadLockAble lock); -} diff --git a/concurrency/src/main/java/com/github/kuangcp/lock/pvp/Player.java b/concurrency/src/main/java/com/github/kuangcp/lock/pvp/Player.java index f6e11407..7f924640 100644 --- a/concurrency/src/main/java/com/github/kuangcp/lock/pvp/Player.java +++ b/concurrency/src/main/java/com/github/kuangcp/lock/pvp/Player.java @@ -5,8 +5,6 @@ /** * @author kuangcp on 3/28/19-9:32 PM - * two lock p1 p2 lock p1 p2 un lock - * TODO lock sort and unlock sort */ @Data class Player { diff --git a/concurrency/src/main/java/com/github/kuangcp/lock/sync/Action.java b/concurrency/src/main/java/com/github/kuangcp/lock/sync/Action.java deleted file mode 100644 index 3cf5336d..00000000 --- a/concurrency/src/main/java/com/github/kuangcp/lock/sync/Action.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.github.kuangcp.lock.sync; - -import java.util.concurrent.TimeUnit; -import lombok.extern.slf4j.Slf4j; - -/** - * @author kuangcp on 2019-04-22 6:33 PM - */ -@Slf4j -public class Action { - - private Object lock = new Object(); - - synchronized void syncMethod() { - log.info("invoke syncMethod"); - sleep(100); - log.info("exit syncMethod"); - } - - synchronized void syncMethod2() { - log.info("invoke syncMethod2"); - sleep(200); - log.info("exit syncMethod2"); - } - - void syncMethodWithLock() { - synchronized (lock) { - log.info("invoke syncMethodWithLock"); - sleep(100); - log.info("exit syncMethodWithLock"); - } - } - - void syncMethodWithLock2() { - synchronized (lock) { - log.info("invoke syncMethodWithLock2"); - sleep(200); - log.info("exit syncMethodWithLock2"); - } - } - - private void sleep(int mills) { - try { - TimeUnit.MILLISECONDS.sleep(mills); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } -} diff --git a/concurrency/src/main/java/com/github/kuangcp/lock/sync/SleepDemo.java b/concurrency/src/main/java/com/github/kuangcp/lock/sync/SleepDemo.java index 0375aa8a..016593dc 100644 --- a/concurrency/src/main/java/com/github/kuangcp/lock/sync/SleepDemo.java +++ b/concurrency/src/main/java/com/github/kuangcp/lock/sync/SleepDemo.java @@ -23,7 +23,6 @@ public Tool setName(String name) { @Override public void run() { - // TODO why warning while (true) { synchronized (this) { try { diff --git a/concurrency/src/main/java/com/github/kuangcp/old/BuildFactory.java b/concurrency/src/main/java/com/github/kuangcp/old/BuildFactory.java index d87d2ffd..4ba3b57e 100644 --- a/concurrency/src/main/java/com/github/kuangcp/old/BuildFactory.java +++ b/concurrency/src/main/java/com/github/kuangcp/old/BuildFactory.java @@ -2,6 +2,9 @@ import lombok.Data; +/** + * 构造者模式,相当于 Builder 注解 + */ @Data public class BuildFactory { @@ -14,7 +17,7 @@ private BuildFactory(Builder builder) { } // 内部静态类 - public static class Builder implements ObjBuilder { + public static class Builder { private String name; private String addr; @@ -29,12 +32,8 @@ public Builder addr(String add) { return this; } - @Override public BuildFactory build() { return new BuildFactory(this); } - } -} - - +} \ No newline at end of file diff --git a/concurrency/src/main/java/com/github/kuangcp/old/CreateObjByBuilder.groovy b/concurrency/src/main/java/com/github/kuangcp/old/CreateObjByBuilder.groovy deleted file mode 100644 index 5a41cdb2..00000000 --- a/concurrency/src/main/java/com/github/kuangcp/old/CreateObjByBuilder.groovy +++ /dev/null @@ -1,57 +0,0 @@ -package com.github.kuangcp.old - -import java.util.concurrent.CountDownLatch - -/** - * Created by https://github.com/kuangcp on 17-8-16 上午8:57 - * 1 构建器模式 实例化对象 - * 2 锁存器的使用 - * countDown 只是为了减数,当0就唤醒所有的线程 - * await 判断,为0不操作,不为0 就休眠当前语句所处线程 - * 终于理解了写的案例类 ShowCopyOnWrite 里的语句运行逻辑了 - */ -// 1 -//BuildFactory.Builder ub = new BuildFactory.Builder(); -//BuildFactory u = ub.name("sd").addr("12").build(); -//System.out.println(u.getCount()); - -// 2 -firstLatch = new CountDownLatch(1) -secondLatch = new CountDownLatch(1) - -def runs() { - Thread.start { - println("进入 线程 1") -// secondLatch.await() - println("1 前" + firstLatch.getCount()) - // 只是将计数器减一,如果减后的数值是0, 就唤醒所有的等待线程 - firstLatch.countDown() - - // 参数用的是第二个锁存器的参数,但是休眠的是这个线程,当前线程 - // 因为线程2 还没有减1 所以这里是必然的要休眠, 直到线程2 的countDown运行,这里才唤醒 - secondLatch.await() - - println("1 后 " + firstLatch.getCount() + ":" + secondLatch.getCount()) - println("线程 1 ") - } - Thread.start { -// Thread.sleep(5000) - println("线程2 休眠1 前 :" + firstLatch.getCount()) - - firstLatch.await() - - println("进入 线程 2") -// secondLatch.await() - println("2 前" + secondLatch.getCount()) - - // 唤醒了休眠的线程 1 - // 所以还是不能确保线程的顺序,应该要将 目标代码运行完后,再进行唤醒,线程的随机性太大,这样稳妥些 - secondLatch.countDown() -// Thread.sleep(1000) - println("2 后 ${secondLatch.getCount()}") - println("线程 2") - - } -} - -runs() \ No newline at end of file diff --git a/concurrency/src/main/java/com/github/kuangcp/old/DeadLock.java b/concurrency/src/main/java/com/github/kuangcp/old/DeadLock.java deleted file mode 100644 index 45191f27..00000000 --- a/concurrency/src/main/java/com/github/kuangcp/old/DeadLock.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.github.kuangcp.old; - -/** - * - */ -public class DeadLock { - - private final String id; - - public DeadLock(String id) { - this.id = id; - } - - public String getId() { - return id; - } - - public synchronized void preparRun(Food food, DeadLock lock) { - System.out.println( - "准备 currentId: " + id + " resource: " + food.getName() + " prepar other: " + lock.getId()); - try { - Thread.sleep(120); - } catch (InterruptedException e) { - e.printStackTrace(); - } - lock.confirmRun(food, this); - } - - public synchronized void confirmRun(Food food, DeadLock lock) { - System.out.println( - "确认 currentId: " + id + " resource: " + food.getName() + "confirm other: " + lock.getId()); - } -} \ No newline at end of file diff --git a/concurrency/src/main/java/com/github/kuangcp/old/DeadLockShow.java b/concurrency/src/main/java/com/github/kuangcp/old/DeadLockShow.java deleted file mode 100644 index cb169d32..00000000 --- a/concurrency/src/main/java/com/github/kuangcp/old/DeadLockShow.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.github.kuangcp.old; - -/** - * Created by https://github.com/kuangcp on 17-8-13 下午9:40 - * 线程死锁的案例类 : - * 原理是 - * 线程1 得到资源A的锁,然后等主线程确认后再获取B的锁 - * 线程2得到B的锁,然后等主线程确认后去获取A的锁,双方都在等对方释放锁,然后就陷入了死锁, - * 但是: 因为线程在JVM的运行是随机性的,所以可能出现线程1 占有了两个资源,运行结束了, - * 线程2才开始运行,所以就没有死锁 - * 所以在准备那个方法里加上睡眠时间,延长运行时间就加大了死锁的概率 - * 消除死锁: - * 让所有的线程以相同的顺序去获取线程锁,这个案例是AB BA的顺序获取,就很有可能出现死锁 - * 对于完全同步对象而言,防止这种死锁出现是因为这样的代码 破坏了状态一致性规则,当有调用准备方法时 - * 接收的方法会在方法内调用另外一个对象,这个调用时的状态是不一致的 - * - */ -public class DeadLockShow{ - public static void main(String []s){ - mainTest(); - } - - public static void mainTest(){ - DeadLock one = new DeadLock("one"); - DeadLock other = new DeadLock("other"); - Food fooda = new Food("a"); - Food foodb = new Food("b"); - - new Thread(new Runnable() { - @Override - public void run() { - one.preparRun(fooda, other); - } - }).start(); - - new Thread(new Runnable() { - @Override - public void run() { - other.preparRun(foodb, one); - } - }).start(); - } -} - - - diff --git a/concurrency/src/main/java/com/github/kuangcp/old/Food.java b/concurrency/src/main/java/com/github/kuangcp/old/Food.java deleted file mode 100644 index 8bac1778..00000000 --- a/concurrency/src/main/java/com/github/kuangcp/old/Food.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.github.kuangcp.old; - -/** - * Created by https://github.com/kuangcp on 17-8-13 下午10:27 - */ -public class Food { - - String name; - - public Food(String name) { - this.name = name; - } - - public String getName() { - return name; - } -} diff --git a/concurrency/src/main/java/com/github/kuangcp/old/MainThread.java b/concurrency/src/main/java/com/github/kuangcp/old/MainThread.java deleted file mode 100644 index a8735b59..00000000 --- a/concurrency/src/main/java/com/github/kuangcp/old/MainThread.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.github.kuangcp.old; - -/** - * Created by https://github.com/kuangcp on 17-8-14 下午9:07 - */ -public class MainThread implements Runnable{ - @Override - public void run() { - - } -} diff --git a/concurrency/src/main/java/com/github/kuangcp/old/ObjBuilder.java b/concurrency/src/main/java/com/github/kuangcp/old/ObjBuilder.java deleted file mode 100644 index f37f3431..00000000 --- a/concurrency/src/main/java/com/github/kuangcp/old/ObjBuilder.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.github.kuangcp.old; - -/** - * Created by https://github.com/kuangcp on 17-8-13 下午8:05 - * 不可变对象以及构建器 - */ -public interface ObjBuilder { - - T build(); -} - diff --git a/concurrency/src/test/java/com/github/kuangcp/lock/dead/DeadLockTest.java b/concurrency/src/test/java/com/github/kuangcp/lock/dead/DeadLockTest.java deleted file mode 100644 index 2f1f3a90..00000000 --- a/concurrency/src/test/java/com/github/kuangcp/lock/dead/DeadLockTest.java +++ /dev/null @@ -1,70 +0,0 @@ -package com.github.kuangcp.lock.dead; - -import com.github.kuangcp.old.Food; -import lombok.extern.slf4j.Slf4j; -import org.junit.Test; - -/** - * @author https://github.com/kuangcp on 2020-03-16 00:22 - * - * 尝试 解决死锁 - * 当遇到锁进行休眠,但是没有释放已经占用的锁,所以还是很大几率死锁 - */ -@Slf4j -public class DeadLockTest { - - @Test - public void testDeadLock() throws Exception { - DeadLock one = new DeadLock("one"); - DeadLock other = new DeadLock("other"); - commonLogic(one, other); - } - - - /** - * 解决死锁: - * 如果在取得第二个锁的时候失败了,就将第一个锁释放掉,从头开始等待 - * 但是还是会有问题,并不是书上说的那么美好,如果遇到锁就释放,然后等待,两个线程都是这样的情况, - * 然后就不停的释放,等待,加锁,死锁在了各自的第一个方法上,控制台就不停的刷准备方法的内容 - */ - @Test - public void testHandle() throws Exception { - DeadLockHandler one = new DeadLockHandler("one"); - DeadLockHandler other = new DeadLockHandler("other"); - - commonLogic(one, other); - } - - /** - * 使用并发包重写死锁案例: - * - * 但是使用的时候,似乎lock方法会一直尝试获得锁,睡眠时间决定了死锁状况的长短 - * (这应该不算死锁,只是等待)准备到确认的延时恰好是睡眠时间 - * 根据 API 知道这个lock如果发现资源被别的线程锁住,他就会休眠,等待锁的释放,所以这里的写法是不会有死锁的, - * 可能7和8的区别?书上用的是7,说的是死锁 - */ - @Test - public void testRewrite() throws Exception { - DeadLockRewrite one = new DeadLockRewrite("one"); - DeadLockRewrite other = new DeadLockRewrite("other"); - commonLogic(one, other); - } - - private void commonLogic(MythDeadLockAble one, MythDeadLockAble other) throws Exception { - Food foodA = new Food("a"); - Food foodB = new Food("b"); - - new Thread(() -> { - one.prepareRun(foodA, other); - log.info("end"); - }).start(); - - new Thread(() -> { - other.prepareRun(foodB, one); - log.info("end"); - }).start(); - - Thread.currentThread().join(60000); - log.info("thread end"); - } -} diff --git a/concurrency/src/test/java/com/github/kuangcp/lock/pvp/PlayerTest.java b/concurrency/src/test/java/com/github/kuangcp/lock/pvp/PlayerTest.java index 288998fb..2a8a3c61 100644 --- a/concurrency/src/test/java/com/github/kuangcp/lock/pvp/PlayerTest.java +++ b/concurrency/src/test/java/com/github/kuangcp/lock/pvp/PlayerTest.java @@ -8,20 +8,19 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadLocalRandom; -import java.util.concurrent.locks.ReentrantLock; import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.Test; /** * @author kuangcp on 2019-04-21 11:14 AM + * + * 加锁顺序问题测试, 结论 加锁需要顺序,释放锁无需顺序 */ @Slf4j public class PlayerTest { private Map playerMap = new HashMap<>(); - private ReentrantLock lock = new ReentrantLock(); - private void arenaLogic(Player a, Player b) throws InterruptedException { log.info("enter arena: a={} b={}", a, b); Thread.sleep(ThreadLocalRandom.current().nextInt(40)); diff --git a/concurrency/src/test/java/com/github/kuangcp/lock/sync/ActionTest.java b/concurrency/src/test/java/com/github/kuangcp/lock/sync/ActionTest.java deleted file mode 100644 index 7ac2149d..00000000 --- a/concurrency/src/test/java/com/github/kuangcp/lock/sync/ActionTest.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.github.kuangcp.lock.sync; - -import lombok.extern.slf4j.Slf4j; -import org.junit.Test; - -/** - * @author kuangcp on 2019-04-22 6:38 PM - */ -@Slf4j -public class ActionTest { - - - @Test - public void testSyncMethod() throws InterruptedException { - Action action = new Action(); - - Thread thread0 = new Thread(() -> { - action.syncMethod(); - action.syncMethod2(); - }); - Thread thread1 = new Thread(() -> { - action.syncMethod(); - action.syncMethod2(); - }); - - thread0.start(); - thread1.start(); - - // 如果两个方法睡眠时间一致, 很有可能会一直出现 0 线程先执行完两个方法才轮到线程 1 - thread0.join(); - thread1.join(); - -// while(Thread.activeCount() > 1){ -// Thread.yield(); -// } - } -} diff --git a/mybatis/build.gradle b/mybatis/build.gradle index 68214cca..0ef557c1 100644 --- a/mybatis/build.gradle +++ b/mybatis/build.gradle @@ -1,8 +1,10 @@ plugins { - id("org.springframework.boot") version "2.2.5.RELEASE" - id("io.spring.dependency-management") version "1.0.9.RELEASE" + id 'org.springframework.boot' version '2.2.5.RELEASE' } +apply plugin: 'java' +apply plugin: 'io.spring.dependency-management' + repositories { mavenCentral() } From 0959bcde6213743990c6248ecf40c937edd11221 Mon Sep 17 00:00:00 2001 From: kcp Date: Mon, 30 Mar 2020 16:20:55 +0800 Subject: [PATCH 153/476] x) path --- README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index cc4b3cdc..9b708e44 100644 --- a/README.md +++ b/README.md @@ -10,14 +10,14 @@ | 基础 | 进阶 | 应用 | |:----|:----|:----| -| [Java8](/java-8) | [并发](/java-concurrency) | [Netty](/java-netty)| -| [集合](/java-collection) | [网络](/java-network) | [Guava](/java-guava)| -| [算法](/java-algorithms) | [设计模式](/java-pattern) | [Spring](/java-spring) -| [字节码](/java-class) | [测试](/java-test) | [Kafka](/java-kafka)| -| [泛型](/java-generic) | | [Hadoop](/java-hadoop) | -| [IO](/java-io) | | | - -[图形化](/java-gui) | [实际问题](/java-question) +| [Java8](/java8) | [并发](/concurrency) | [Netty](/netty)| +| [集合](/collection) | [网络](/network) | [Guava](/guava)| +| [算法](/algorithms) | [设计模式](/pattern) | [Spring](/spring) +| [字节码](/class) | [测试](/test) | [Kafka](/kafka)| +| [泛型](/generic) | | [Hadoop](/hadoop) | +| [IO](/io) | | | + +[图形化](/gui) | [实际问题](/question) ************************ From 566453b1de78c5619f320a262213bef5b13c7697 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Tue, 7 Apr 2020 01:17:18 +0800 Subject: [PATCH 154/476] *) binary code --- .../test/java/syntax/bit/BitOperatorsTest.java | 18 +++++++++++------- .../test/java/syntax/bytes/OperatorTest.java | 10 ++++++---- .../kuangcp/stream/filter/FilterTest.java | 6 +++--- 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/class/src/test/java/syntax/bit/BitOperatorsTest.java b/class/src/test/java/syntax/bit/BitOperatorsTest.java index e29bd07e..9fa7d231 100644 --- a/class/src/test/java/syntax/bit/BitOperatorsTest.java +++ b/class/src/test/java/syntax/bit/BitOperatorsTest.java @@ -63,15 +63,18 @@ public void testBitAndAssignment() { } + /** + * 右移 + */ @Test public void testRight() { // 带符号的 右移操作 正数则左补0 负数则左补1 assertThat(0b101 >> 2, equalTo(0b1)); - // 11111111111111111111111111111011 -0b101的补码表示(也是实际存储的值) + // 11111111111111111111111111111011 -0b101的补码 (也是实际存储的值) + // 11111111111111111111111111111110 右移两位后的补码 + // 10000000000000000000000000000010 从补码 -1 取反 得到 原码 show(-0b101); - // 11111111111111111111111111111110 右移两位 - // 10000000000000000000000000000010 -1 取反 得到 原码 assertThat(-0b101 >> 2, equalTo(-0b10)); // 无视符号位的 右移操作 左补0 @@ -87,6 +90,9 @@ public void testRight() { assertThat(0b110011 >> 3, equalTo(0b110011 >>> 3)); } + /** + * 左移 + */ @Test public void testLeft() { assertThat(0b11_0110 << 2, equalTo(0b1101_1000)); @@ -119,14 +125,12 @@ public void testIdempotentOperation() { public void testCompareSpeed() { GetRunTime runTime = new GetRunTime().startCount(); IntStream.rangeClosed(1, 10000).forEach(BitOperatorsTest::idempotentOperations); - runTime.endCount("bit "); + runTime.endCountOneLine("bit "); runTime.startCount(); IntStream.rangeClosed(1, 10000) .forEach(i -> Math.pow(2, Math.floor(Math.log(i) / Math.log(2)) + 1)); - runTime.endCount("log and pow"); - - // 后者性能优于前者, 因为虽然前者是位运算, 但是有太多多余操作 + runTime.endCountOneLine("log and pow"); } /** diff --git a/class/src/test/java/syntax/bytes/OperatorTest.java b/class/src/test/java/syntax/bytes/OperatorTest.java index 74c12a5e..237a60dd 100644 --- a/class/src/test/java/syntax/bytes/OperatorTest.java +++ b/class/src/test/java/syntax/bytes/OperatorTest.java @@ -27,12 +27,14 @@ public void testAdd() { @Test public void testIntToByte() { int a = 13232; - // 直接赋值 无法编译通过 + // 直接赋值 无法编译通过 需强转 byte b = (byte) a; - // int 赋值 byte 会截断后8位, 由于是有符号的, 结果是 10110000 - // 原码是 01010000 : 所以 b是-80 - log.info("{}: {} -> {}", b, ShowBinary.toBinary(a), ShowBinary.toBinary(b)); + // int 赋值给 byte 仅保留后8位, 结果是 10110000 + // 反码 10101111 + // 原码 11010000 + // 所以 b是-80 + log.info("{} int: {} -> byte: {}", b, ShowBinary.toBinary(a), ShowBinary.toBinary(b)); } // 和 int 一样的计算方式 diff --git a/java8/src/test/java/com/github/kuangcp/stream/filter/FilterTest.java b/java8/src/test/java/com/github/kuangcp/stream/filter/FilterTest.java index 8f95bc42..ba3c099a 100644 --- a/java8/src/test/java/com/github/kuangcp/stream/filter/FilterTest.java +++ b/java8/src/test/java/com/github/kuangcp/stream/filter/FilterTest.java @@ -1,5 +1,8 @@ package com.github.kuangcp.stream.filter; +import static org.hamcrest.Matchers.equalTo; +import static org.junit.Assert.assertThat; + import java.util.Arrays; import java.util.List; import java.util.Objects; @@ -7,9 +10,6 @@ import lombok.extern.slf4j.Slf4j; import org.junit.Test; -import static org.hamcrest.Matchers.equalTo; -import static org.junit.Assert.assertThat; - /** * @author kuangcp on 3/6/19-5:08 PM */ From 80e040466263d68a9d2bdbb4d197892349256863 Mon Sep 17 00:00:00 2001 From: kcp Date: Wed, 22 Apr 2020 10:13:44 +0800 Subject: [PATCH 155/476] +) bean copy --- class/src/main/java/jvm/load/Readme.md | 1 + .../kuangcp/aop/annotation/PersonTest.java | 2 +- .../kuangcp/aop/exception/ExceptionTest.java | 2 +- .../github/kuangcp/aop/xml/PersonTest.java | 2 +- .../kuangcp/aop/xml/salary/SalaryTest.java | 2 +- .../{util => common}/SpringHelper.java | 2 +- .../di/annotation/HandleAnnotationTest.java | 2 +- .../kuangcp/di/autoscan/AutoScanTest.java | 2 +- .../di/xml/constructor/PersonTest.java | 2 +- .../github/kuangcp/di/xml/set/PersonTest.java | 2 +- .../kuangcp/document/spring/DocumentTest.java | 2 +- .../com/github/kuangcp/extend/PersonTest.java | 2 +- .../hibernate/annotation/PersonTest.java | 2 +- .../kuangcp/hibernate/xml/PersonTest.java | 2 +- .../github/kuangcp/ioc/alias/AliasTest.java | 2 +- .../ioc/createobject/CreateObjectTest.java | 2 +- .../ioc/createobject/when/WhenTest.java | 2 +- .../ioc/initdestroy/InitDestroyTest.java | 2 +- .../github/kuangcp/ioc/scope/ScopeTest.java | 2 +- .../kuangcp/jdbc/jdbc/DataSourceTest.java | 2 +- .../github/kuangcp/jdbc/jdbc/PersonTest.java | 2 +- .../jdbc/jdbc/transaction/PersonTest.java | 2 +- .../transaction/annotation/PersonTest.java | 2 +- .../kuangcp/jdbc/transaction/PersonTest.java | 2 +- .../com/github/kuangcp/mvc/PersonTest.java | 2 +- .../kuangcp/mvc/annotation/PersonTest.java | 2 +- .../proxy/salary/aop/xml/PersonTest.java | 2 +- .../com/github/kuangcp/util/BeanCopyTest.java | 63 +++++++++++++++++++ 28 files changed, 90 insertions(+), 26 deletions(-) create mode 100644 class/src/main/java/jvm/load/Readme.md rename spring/src/test/java/com/github/kuangcp/{util => common}/SpringHelper.java (91%) create mode 100644 spring/src/test/java/com/github/kuangcp/util/BeanCopyTest.java diff --git a/class/src/main/java/jvm/load/Readme.md b/class/src/main/java/jvm/load/Readme.md new file mode 100644 index 00000000..8705d541 --- /dev/null +++ b/class/src/main/java/jvm/load/Readme.md @@ -0,0 +1 @@ +Load and Unload class diff --git a/spring/src/test/java/com/github/kuangcp/aop/annotation/PersonTest.java b/spring/src/test/java/com/github/kuangcp/aop/annotation/PersonTest.java index f76d6b70..d5ba55d3 100644 --- a/spring/src/test/java/com/github/kuangcp/aop/annotation/PersonTest.java +++ b/spring/src/test/java/com/github/kuangcp/aop/annotation/PersonTest.java @@ -1,6 +1,6 @@ package com.github.kuangcp.aop.annotation; -import com.github.kuangcp.util.SpringHelper; +import com.github.kuangcp.common.SpringHelper; import org.junit.Test; /** diff --git a/spring/src/test/java/com/github/kuangcp/aop/exception/ExceptionTest.java b/spring/src/test/java/com/github/kuangcp/aop/exception/ExceptionTest.java index dfc181d4..528384ac 100644 --- a/spring/src/test/java/com/github/kuangcp/aop/exception/ExceptionTest.java +++ b/spring/src/test/java/com/github/kuangcp/aop/exception/ExceptionTest.java @@ -1,7 +1,7 @@ package com.github.kuangcp.aop.exception; import com.github.kuangcp.aop.exception.action.PersonAction; -import com.github.kuangcp.util.SpringHelper; +import com.github.kuangcp.common.SpringHelper; import org.junit.Test; public class ExceptionTest extends SpringHelper { diff --git a/spring/src/test/java/com/github/kuangcp/aop/xml/PersonTest.java b/spring/src/test/java/com/github/kuangcp/aop/xml/PersonTest.java index bf6bd05d..091ac896 100644 --- a/spring/src/test/java/com/github/kuangcp/aop/xml/PersonTest.java +++ b/spring/src/test/java/com/github/kuangcp/aop/xml/PersonTest.java @@ -1,7 +1,7 @@ package com.github.kuangcp.aop.xml; -import com.github.kuangcp.util.SpringHelper; +import com.github.kuangcp.common.SpringHelper; import org.junit.Test; /** diff --git a/spring/src/test/java/com/github/kuangcp/aop/xml/salary/SalaryTest.java b/spring/src/test/java/com/github/kuangcp/aop/xml/salary/SalaryTest.java index abd3d538..ea955650 100644 --- a/spring/src/test/java/com/github/kuangcp/aop/xml/salary/SalaryTest.java +++ b/spring/src/test/java/com/github/kuangcp/aop/xml/salary/SalaryTest.java @@ -1,6 +1,6 @@ package com.github.kuangcp.aop.xml.salary; -import com.github.kuangcp.util.SpringHelper; +import com.github.kuangcp.common.SpringHelper; import org.junit.Test; public class SalaryTest extends SpringHelper { diff --git a/spring/src/test/java/com/github/kuangcp/util/SpringHelper.java b/spring/src/test/java/com/github/kuangcp/common/SpringHelper.java similarity index 91% rename from spring/src/test/java/com/github/kuangcp/util/SpringHelper.java rename to spring/src/test/java/com/github/kuangcp/common/SpringHelper.java index 2c3ea1f1..1ce86473 100644 --- a/spring/src/test/java/com/github/kuangcp/util/SpringHelper.java +++ b/spring/src/test/java/com/github/kuangcp/common/SpringHelper.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.util; +package com.github.kuangcp.common; import org.junit.Before; import org.springframework.context.ApplicationContext; diff --git a/spring/src/test/java/com/github/kuangcp/di/annotation/HandleAnnotationTest.java b/spring/src/test/java/com/github/kuangcp/di/annotation/HandleAnnotationTest.java index cf115bc6..b2e9f676 100644 --- a/spring/src/test/java/com/github/kuangcp/di/annotation/HandleAnnotationTest.java +++ b/spring/src/test/java/com/github/kuangcp/di/annotation/HandleAnnotationTest.java @@ -1,6 +1,6 @@ package com.github.kuangcp.di.annotation; -import com.github.kuangcp.util.SpringHelper; +import com.github.kuangcp.common.SpringHelper; import org.junit.Test; import org.springframework.context.support.ClassPathXmlApplicationContext; diff --git a/spring/src/test/java/com/github/kuangcp/di/autoscan/AutoScanTest.java b/spring/src/test/java/com/github/kuangcp/di/autoscan/AutoScanTest.java index 78e0bc6d..549a2987 100644 --- a/spring/src/test/java/com/github/kuangcp/di/autoscan/AutoScanTest.java +++ b/spring/src/test/java/com/github/kuangcp/di/autoscan/AutoScanTest.java @@ -1,6 +1,6 @@ package com.github.kuangcp.di.autoscan; -import com.github.kuangcp.util.SpringHelper; +import com.github.kuangcp.common.SpringHelper; import org.junit.Test; diff --git a/spring/src/test/java/com/github/kuangcp/di/xml/constructor/PersonTest.java b/spring/src/test/java/com/github/kuangcp/di/xml/constructor/PersonTest.java index 984e3262..722d550e 100644 --- a/spring/src/test/java/com/github/kuangcp/di/xml/constructor/PersonTest.java +++ b/spring/src/test/java/com/github/kuangcp/di/xml/constructor/PersonTest.java @@ -1,6 +1,6 @@ package com.github.kuangcp.di.xml.constructor; -import com.github.kuangcp.util.SpringHelper; +import com.github.kuangcp.common.SpringHelper; import org.junit.Test; diff --git a/spring/src/test/java/com/github/kuangcp/di/xml/set/PersonTest.java b/spring/src/test/java/com/github/kuangcp/di/xml/set/PersonTest.java index 156aa1e2..57ee1f33 100644 --- a/spring/src/test/java/com/github/kuangcp/di/xml/set/PersonTest.java +++ b/spring/src/test/java/com/github/kuangcp/di/xml/set/PersonTest.java @@ -1,6 +1,6 @@ package com.github.kuangcp.di.xml.set; -import com.github.kuangcp.util.SpringHelper; +import com.github.kuangcp.common.SpringHelper; import java.util.List; import org.junit.Test; diff --git a/spring/src/test/java/com/github/kuangcp/document/spring/DocumentTest.java b/spring/src/test/java/com/github/kuangcp/document/spring/DocumentTest.java index aabb0480..ea9fcf24 100644 --- a/spring/src/test/java/com/github/kuangcp/document/spring/DocumentTest.java +++ b/spring/src/test/java/com/github/kuangcp/document/spring/DocumentTest.java @@ -1,6 +1,6 @@ package com.github.kuangcp.document.spring; -import com.github.kuangcp.util.SpringHelper; +import com.github.kuangcp.common.SpringHelper; import org.junit.Test; public class DocumentTest extends SpringHelper { diff --git a/spring/src/test/java/com/github/kuangcp/extend/PersonTest.java b/spring/src/test/java/com/github/kuangcp/extend/PersonTest.java index 91721613..b9d080c6 100644 --- a/spring/src/test/java/com/github/kuangcp/extend/PersonTest.java +++ b/spring/src/test/java/com/github/kuangcp/extend/PersonTest.java @@ -1,6 +1,6 @@ package com.github.kuangcp.extend; -import com.github.kuangcp.util.SpringHelper; +import com.github.kuangcp.common.SpringHelper; import org.junit.Test; public class PersonTest extends SpringHelper { diff --git a/spring/src/test/java/com/github/kuangcp/hibernate/annotation/PersonTest.java b/spring/src/test/java/com/github/kuangcp/hibernate/annotation/PersonTest.java index 4c886039..611cb2a5 100644 --- a/spring/src/test/java/com/github/kuangcp/hibernate/annotation/PersonTest.java +++ b/spring/src/test/java/com/github/kuangcp/hibernate/annotation/PersonTest.java @@ -1,6 +1,6 @@ package com.github.kuangcp.hibernate.annotation; -import com.github.kuangcp.util.SpringHelper; +import com.github.kuangcp.common.SpringHelper; import org.junit.Test; public class PersonTest extends SpringHelper { diff --git a/spring/src/test/java/com/github/kuangcp/hibernate/xml/PersonTest.java b/spring/src/test/java/com/github/kuangcp/hibernate/xml/PersonTest.java index 62bf99b8..3222a66c 100644 --- a/spring/src/test/java/com/github/kuangcp/hibernate/xml/PersonTest.java +++ b/spring/src/test/java/com/github/kuangcp/hibernate/xml/PersonTest.java @@ -1,6 +1,6 @@ package com.github.kuangcp.hibernate.xml; -import com.github.kuangcp.util.SpringHelper; +import com.github.kuangcp.common.SpringHelper; import org.junit.Test; diff --git a/spring/src/test/java/com/github/kuangcp/ioc/alias/AliasTest.java b/spring/src/test/java/com/github/kuangcp/ioc/alias/AliasTest.java index 3560c98f..56c31de8 100644 --- a/spring/src/test/java/com/github/kuangcp/ioc/alias/AliasTest.java +++ b/spring/src/test/java/com/github/kuangcp/ioc/alias/AliasTest.java @@ -1,6 +1,6 @@ package com.github.kuangcp.ioc.alias; -import com.github.kuangcp.util.SpringHelper; +import com.github.kuangcp.common.SpringHelper; import org.junit.Test; public class AliasTest extends SpringHelper { diff --git a/spring/src/test/java/com/github/kuangcp/ioc/createobject/CreateObjectTest.java b/spring/src/test/java/com/github/kuangcp/ioc/createobject/CreateObjectTest.java index 0d1b5bc8..436d8b74 100644 --- a/spring/src/test/java/com/github/kuangcp/ioc/createobject/CreateObjectTest.java +++ b/spring/src/test/java/com/github/kuangcp/ioc/createobject/CreateObjectTest.java @@ -1,6 +1,6 @@ package com.github.kuangcp.ioc.createobject; -import com.github.kuangcp.util.SpringHelper; +import com.github.kuangcp.common.SpringHelper; import org.junit.Test; diff --git a/spring/src/test/java/com/github/kuangcp/ioc/createobject/when/WhenTest.java b/spring/src/test/java/com/github/kuangcp/ioc/createobject/when/WhenTest.java index 92586e4b..ec7f8f7d 100644 --- a/spring/src/test/java/com/github/kuangcp/ioc/createobject/when/WhenTest.java +++ b/spring/src/test/java/com/github/kuangcp/ioc/createobject/when/WhenTest.java @@ -1,6 +1,6 @@ package com.github.kuangcp.ioc.createobject.when; -import com.github.kuangcp.util.SpringHelper; +import com.github.kuangcp.common.SpringHelper; import org.junit.Test; public class WhenTest extends SpringHelper { diff --git a/spring/src/test/java/com/github/kuangcp/ioc/initdestroy/InitDestroyTest.java b/spring/src/test/java/com/github/kuangcp/ioc/initdestroy/InitDestroyTest.java index 2df763ee..365a22fe 100644 --- a/spring/src/test/java/com/github/kuangcp/ioc/initdestroy/InitDestroyTest.java +++ b/spring/src/test/java/com/github/kuangcp/ioc/initdestroy/InitDestroyTest.java @@ -1,6 +1,6 @@ package com.github.kuangcp.ioc.initdestroy; -import com.github.kuangcp.util.SpringHelper; +import com.github.kuangcp.common.SpringHelper; import org.junit.Test; import org.springframework.context.support.ClassPathXmlApplicationContext; diff --git a/spring/src/test/java/com/github/kuangcp/ioc/scope/ScopeTest.java b/spring/src/test/java/com/github/kuangcp/ioc/scope/ScopeTest.java index 2dc07166..8ee3312d 100644 --- a/spring/src/test/java/com/github/kuangcp/ioc/scope/ScopeTest.java +++ b/spring/src/test/java/com/github/kuangcp/ioc/scope/ScopeTest.java @@ -1,7 +1,7 @@ package spring_ioc.cn.itcast.spring0909.scope; import com.github.kuangcp.ioc.scope.HelloWorld; -import com.github.kuangcp.util.SpringHelper; +import com.github.kuangcp.common.SpringHelper; import org.junit.Test; /** diff --git a/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/DataSourceTest.java b/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/DataSourceTest.java index d85a01fa..bc2d80b3 100644 --- a/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/DataSourceTest.java +++ b/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/DataSourceTest.java @@ -1,6 +1,6 @@ package com.github.kuangcp.jdbc.jdbc; -import com.github.kuangcp.util.SpringHelper; +import com.github.kuangcp.common.SpringHelper; import javax.sql.DataSource; import org.junit.Test; diff --git a/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/PersonTest.java b/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/PersonTest.java index 12cf745d..b005e0ff 100644 --- a/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/PersonTest.java +++ b/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/PersonTest.java @@ -1,6 +1,6 @@ package com.github.kuangcp.jdbc.jdbc; -import com.github.kuangcp.util.SpringHelper; +import com.github.kuangcp.common.SpringHelper; import java.util.List; import org.junit.Test; diff --git a/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/PersonTest.java b/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/PersonTest.java index 5422b9fd..a831307c 100644 --- a/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/PersonTest.java +++ b/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/PersonTest.java @@ -1,6 +1,6 @@ package com.github.kuangcp.jdbc.jdbc.transaction; -import com.github.kuangcp.util.SpringHelper; +import com.github.kuangcp.common.SpringHelper; import org.junit.Test; public class PersonTest extends SpringHelper { diff --git a/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/annotation/PersonTest.java b/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/annotation/PersonTest.java index ae16e793..2ef95fab 100644 --- a/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/annotation/PersonTest.java +++ b/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/annotation/PersonTest.java @@ -1,6 +1,6 @@ package com.github.kuangcp.jdbc.jdbc.transaction.annotation; -import com.github.kuangcp.util.SpringHelper; +import com.github.kuangcp.common.SpringHelper; import org.junit.Test; diff --git a/spring/src/test/java/com/github/kuangcp/jdbc/transaction/PersonTest.java b/spring/src/test/java/com/github/kuangcp/jdbc/transaction/PersonTest.java index 7ee3b5e9..49d7feec 100644 --- a/spring/src/test/java/com/github/kuangcp/jdbc/transaction/PersonTest.java +++ b/spring/src/test/java/com/github/kuangcp/jdbc/transaction/PersonTest.java @@ -1,6 +1,6 @@ package com.github.kuangcp.jdbc.transaction; -import com.github.kuangcp.util.SpringHelper; +import com.github.kuangcp.common.SpringHelper; import org.junit.Test; public class PersonTest extends SpringHelper { diff --git a/spring/src/test/java/com/github/kuangcp/mvc/PersonTest.java b/spring/src/test/java/com/github/kuangcp/mvc/PersonTest.java index d96dfafe..44010753 100644 --- a/spring/src/test/java/com/github/kuangcp/mvc/PersonTest.java +++ b/spring/src/test/java/com/github/kuangcp/mvc/PersonTest.java @@ -1,6 +1,6 @@ package com.github.kuangcp.mvc; -import com.github.kuangcp.util.SpringHelper; +import com.github.kuangcp.common.SpringHelper; import org.junit.Test; diff --git a/spring/src/test/java/com/github/kuangcp/mvc/annotation/PersonTest.java b/spring/src/test/java/com/github/kuangcp/mvc/annotation/PersonTest.java index 87aa9f46..ece58ee8 100644 --- a/spring/src/test/java/com/github/kuangcp/mvc/annotation/PersonTest.java +++ b/spring/src/test/java/com/github/kuangcp/mvc/annotation/PersonTest.java @@ -1,6 +1,6 @@ package com.github.kuangcp.mvc.annotation; -import com.github.kuangcp.util.SpringHelper; +import com.github.kuangcp.common.SpringHelper; import org.junit.Test; diff --git a/spring/src/test/java/com/github/kuangcp/proxy/salary/aop/xml/PersonTest.java b/spring/src/test/java/com/github/kuangcp/proxy/salary/aop/xml/PersonTest.java index 43d7ae6d..b7afa124 100644 --- a/spring/src/test/java/com/github/kuangcp/proxy/salary/aop/xml/PersonTest.java +++ b/spring/src/test/java/com/github/kuangcp/proxy/salary/aop/xml/PersonTest.java @@ -1,6 +1,6 @@ package com.github.kuangcp.proxy.salary.aop.xml; -import com.github.kuangcp.util.SpringHelper; +import com.github.kuangcp.common.SpringHelper; import org.junit.Test; /** diff --git a/spring/src/test/java/com/github/kuangcp/util/BeanCopyTest.java b/spring/src/test/java/com/github/kuangcp/util/BeanCopyTest.java new file mode 100644 index 00000000..7203a54d --- /dev/null +++ b/spring/src/test/java/com/github/kuangcp/util/BeanCopyTest.java @@ -0,0 +1,63 @@ +package com.github.kuangcp.util; + +import java.util.concurrent.TimeUnit; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.junit.Test; +import org.springframework.beans.BeanUtils; + +/** + * @author https://github.com/kuangcp on 2020-04-21 20:31 + */ +public class BeanCopyTest { + + + // 如何复现大量反射类 GeneratedMethodAccessor 装载和卸载 + @Test + public void testCopy() throws InterruptedException { + @Data + @NoArgsConstructor + class A { + + String name; + String info; + + @Override + protected void finalize() throws Throwable { + super.finalize(); + System.out.print("a final "); + } + } + @NoArgsConstructor + + @Data + class B { + + String name; + String info; + String len; + + @Override + protected void finalize() throws Throwable { + super.finalize(); + System.out.print("b final "); + } + } + + for (int i = 0; i < 100; i++) { + for (int j = 0; j < 10000; j++) { + A a = new A(); + a.name = "name"; + a.info = "info"; + + B b = new B(); + + BeanUtils.copyProperties(a, b); + BeanUtils.copyProperties(b, a); + } + + TimeUnit.SECONDS.sleep(5); + System.out.println("loop"); + } + } +} From ffa0e0a784fa07b2a668fd061f2738df08cecfd2 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Mon, 4 May 2020 22:13:37 +0800 Subject: [PATCH 156/476] *) test stream zip --- .../kuangcp/stream/CreateStreamTest.java | 68 ++++++++++++++++++- 1 file changed, 67 insertions(+), 1 deletion(-) diff --git a/java8/src/test/java/com/github/kuangcp/stream/CreateStreamTest.java b/java8/src/test/java/com/github/kuangcp/stream/CreateStreamTest.java index b3dac917..9847ed76 100644 --- a/java8/src/test/java/com/github/kuangcp/stream/CreateStreamTest.java +++ b/java8/src/test/java/com/github/kuangcp/stream/CreateStreamTest.java @@ -1,9 +1,16 @@ package com.github.kuangcp.stream; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.Arrays; +import java.util.Iterator; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; import java.util.stream.Stream; +import java.util.stream.StreamSupport; import org.junit.Test; /** @@ -12,9 +19,68 @@ public class CreateStreamTest { @Test - public void testOfWithNull(){ + public void testOfWithNull() { List list = Stream.of(null, "test", "name").filter(Objects::nonNull) .collect(Collectors.toList()); System.out.println(list); } + + /** + * 判断是否无限流, 理论上是无法实现的,但是这里可以估计 + */ + @Test + public void testJudgeFinite() { + assertFalse(isFinite(Stream.iterate(1, x -> x))); + assertTrue(isFinite(Stream.of(1))); + assertTrue(isFinite(Stream.of(1, 2, 3, 4).limit(1))); + } + + boolean isFinite(Stream stream) { + return !Objects.equals(stream.spliterator().estimateSize(), Long.MAX_VALUE); + } + + @Test + public void testZip() throws Exception { + + Integer[] result = new Integer[5]; + Stream zip = zip(Stream.of(1, 2, 4, 5).parallel(), Stream.of(2, 3,10).parallel()); + zip.collect(Collectors.toList()).toArray(result); + + List excepts = Arrays.asList(1, 2, 2, 3, 4, 10, 5); + Integer[] exceptArray = new Integer[5]; + excepts.toArray(exceptArray); + assertArrayEquals(result, exceptArray); + } + + Stream zip(Stream left, Stream right) { + Iterator leftIterator = left.iterator(); + Iterator rightIterator = right.iterator(); + + Iterator iterator = new Iterator() { + private boolean left = false; + + @Override + public boolean hasNext() { + return leftIterator.hasNext() || rightIterator.hasNext(); + } + + @Override + public T next() { + left = !left; + if (left && !leftIterator.hasNext()) { + return rightIterator.next(); + } + if (!left && !rightIterator.hasNext()) { + return leftIterator.next(); + } + return left + ? leftIterator.next() + : rightIterator.next(); + } + }; + + Iterable iterable = () -> iterator; + boolean parallel = left.isParallel() || right.isParallel(); + return StreamSupport.stream(iterable.spliterator(), parallel); + } } From 6b9745d64ac85a5b3d27fed9779695a4605a5066 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Wed, 6 May 2020 01:46:20 +0800 Subject: [PATCH 157/476] *) compare with jmh --- .../serialize/json/speed/GsonTool.java | 2 +- .../serialize/json/speed/JacksonTool.java | 2 +- .../serialize/json/speed/SpeedBenchMark.java | 23 +++++++ .../serialize/json/JsonSerializeTest.java | 61 ------------------- .../kuangcp/serialize/json/ReadJsonTest.java | 57 +++++++++++++++++ .../kuangcp/serialize/json/WriteJsonTest.java | 60 ++++++++++++++++++ 6 files changed, 142 insertions(+), 63 deletions(-) create mode 100644 class/src/main/java/com/github/kuangcp/serialize/json/speed/SpeedBenchMark.java delete mode 100644 class/src/test/java/com/github/kuangcp/serialize/json/JsonSerializeTest.java create mode 100644 class/src/test/java/com/github/kuangcp/serialize/json/ReadJsonTest.java create mode 100644 class/src/test/java/com/github/kuangcp/serialize/json/WriteJsonTest.java diff --git a/class/src/main/java/com/github/kuangcp/serialize/json/speed/GsonTool.java b/class/src/main/java/com/github/kuangcp/serialize/json/speed/GsonTool.java index 0a9752b6..beedb56a 100644 --- a/class/src/main/java/com/github/kuangcp/serialize/json/speed/GsonTool.java +++ b/class/src/main/java/com/github/kuangcp/serialize/json/speed/GsonTool.java @@ -13,7 +13,7 @@ @Slf4j public class GsonTool implements JsonTool { - private Gson gson = new Gson(); + private static final Gson gson = new Gson(); @Override public Person fromJSON(String json, Class target) { diff --git a/class/src/main/java/com/github/kuangcp/serialize/json/speed/JacksonTool.java b/class/src/main/java/com/github/kuangcp/serialize/json/speed/JacksonTool.java index 218595ff..90ce6266 100644 --- a/class/src/main/java/com/github/kuangcp/serialize/json/speed/JacksonTool.java +++ b/class/src/main/java/com/github/kuangcp/serialize/json/speed/JacksonTool.java @@ -13,7 +13,7 @@ */ public class JacksonTool implements JsonTool { - private ObjectMapper mapper = new ObjectMapper(); + public static final ObjectMapper mapper = new ObjectMapper(); @Override public Person fromJSON(String json, Class target) throws IOException { diff --git a/class/src/main/java/com/github/kuangcp/serialize/json/speed/SpeedBenchMark.java b/class/src/main/java/com/github/kuangcp/serialize/json/speed/SpeedBenchMark.java new file mode 100644 index 00000000..bea326e1 --- /dev/null +++ b/class/src/main/java/com/github/kuangcp/serialize/json/speed/SpeedBenchMark.java @@ -0,0 +1,23 @@ +package com.github.kuangcp.serialize.json.speed; + +import java.util.concurrent.TimeUnit; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Threads; +import org.openjdk.jmh.annotations.Warmup; + +/** + * @author https://github.com/kuangcp on 2020-05-06 00:33 + */ +@BenchmarkMode(Mode.Throughput) +@Warmup(iterations = 3) +@Measurement(iterations = 10, time = 1) +@Threads(4) +@Fork(2) +@OutputTimeUnit(TimeUnit.MILLISECONDS) +public class SpeedBenchMark { + +} diff --git a/class/src/test/java/com/github/kuangcp/serialize/json/JsonSerializeTest.java b/class/src/test/java/com/github/kuangcp/serialize/json/JsonSerializeTest.java deleted file mode 100644 index a0f8318a..00000000 --- a/class/src/test/java/com/github/kuangcp/serialize/json/JsonSerializeTest.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.github.kuangcp.serialize.json; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.github.kuangcp.serialize.Person; -import com.github.kuangcp.serialize.json.speed.FastJsonTool; -import com.github.kuangcp.serialize.json.speed.GsonTool; -import com.github.kuangcp.serialize.json.speed.JacksonTool; -import com.github.kuangcp.serialize.json.speed.JsonTool; -import com.github.kuangcp.time.GetRunTime; -import com.google.gson.Gson; -import java.io.IOException; -import java.util.Arrays; -import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.IntStream; -import org.junit.Test; - -/** - * Created by https://github.com/kuangcp - * - * @author kuangcp - */ -public class JsonSerializeTest { - - private static final int DATA_SIZE = 30; - - private List> list = Arrays - .asList(new GsonTool(), new JacksonTool(), new FastJsonTool()); - - private List personList = IntStream.range(1, DATA_SIZE) - .mapToObj(i -> new Person("name" + i)).collect(Collectors.toList()); - - @Test - public void compareRead() throws IOException { - ObjectMapper mapper = new ObjectMapper(); - Person one = new Person("one"); - String json = mapper.writeValueAsString(one); - -// Gson gson = new Gson(); -// String json = gson.toJson(one); - GetRunTime getRunTime = new GetRunTime(); - for (JsonTool tool : list) { - getRunTime.startCount(); - Person person = tool.fromJSON(json, Person.class); -// System.out.println(person); - getRunTime.endCountOneLine(tool.getName()); - } - } - - @Test - public void compareWrite() throws JsonProcessingException { - GetRunTime getRunTime = new GetRunTime(); - for (JsonTool tool : list) { - getRunTime.startCount(); - String json = tool.toJSON(personList); - System.out.println(json); - getRunTime.endCountOneLine(tool.getName()); - } - } -} \ No newline at end of file diff --git a/class/src/test/java/com/github/kuangcp/serialize/json/ReadJsonTest.java b/class/src/test/java/com/github/kuangcp/serialize/json/ReadJsonTest.java new file mode 100644 index 00000000..5b7a31c5 --- /dev/null +++ b/class/src/test/java/com/github/kuangcp/serialize/json/ReadJsonTest.java @@ -0,0 +1,57 @@ +package com.github.kuangcp.serialize.json; + +import com.github.kuangcp.serialize.Person; +import com.github.kuangcp.serialize.json.speed.FastJsonTool; +import com.github.kuangcp.serialize.json.speed.GsonTool; +import com.github.kuangcp.serialize.json.speed.JacksonTool; +import java.io.IOException; +import java.util.concurrent.TimeUnit; +import org.junit.Test; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Threads; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +/** + * Created by https://github.com/kuangcp + * + * @author kuangcp + */ +@BenchmarkMode(Mode.Throughput) +@Warmup(iterations = 1) +@Measurement(iterations = 1, time = 1) +@Threads(2) +@OutputTimeUnit(TimeUnit.MILLISECONDS) +public class ReadJsonTest { + + private static final String json = "{\"name\":\"one\",\"address\":\"any province\",\"phone\":null}"; + + @Benchmark + public void testGsonRead() throws IOException { + new GsonTool().fromJSON(json, Person.class); + } + + @Benchmark + public void testJacksonRead() throws IOException { + new JacksonTool().fromJSON(json, Person.class); + } + + @Benchmark + public void testFastJsonRead() throws IOException { + new FastJsonTool().fromJSON(json, Person.class); + } + + @Test + public void testCompareRead() throws Exception { + Options options = new OptionsBuilder() + .include(ReadJsonTest.class.getSimpleName()) + .output("/tmp/" + ReadJsonTest.class.getSimpleName() + ".log").build(); + new Runner(options).run(); + } +} \ No newline at end of file diff --git a/class/src/test/java/com/github/kuangcp/serialize/json/WriteJsonTest.java b/class/src/test/java/com/github/kuangcp/serialize/json/WriteJsonTest.java new file mode 100644 index 00000000..729c9a22 --- /dev/null +++ b/class/src/test/java/com/github/kuangcp/serialize/json/WriteJsonTest.java @@ -0,0 +1,60 @@ +package com.github.kuangcp.serialize.json; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.github.kuangcp.serialize.Person; +import com.github.kuangcp.serialize.json.speed.FastJsonTool; +import com.github.kuangcp.serialize.json.speed.GsonTool; +import com.github.kuangcp.serialize.json.speed.JacksonTool; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import org.junit.Test; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Threads; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +/** + * @author https://github.com/kuangcp on 2020-05-06 01:12 + */ +@BenchmarkMode(Mode.Throughput) +@Warmup(iterations = 1) +@Measurement(iterations = 10, time = 1) +@Threads(2) +@OutputTimeUnit(TimeUnit.MILLISECONDS) +public class WriteJsonTest { + + private static final int DATA_SIZE = 30; + private static final List personList = IntStream.range(1, DATA_SIZE) + .mapToObj(i -> new Person("name" + i)).collect(Collectors.toList()); + + @Benchmark + public void jacksonWrite() throws JsonProcessingException { + new JacksonTool().toJSON(personList); + } + + @Benchmark + public void fastJsonWrite() throws JsonProcessingException { + new FastJsonTool().toJSON(personList); + } + + @Benchmark + public void gsonWrite() throws JsonProcessingException { + new GsonTool().toJSON(personList); + } + + @Test + public void testCompareRead() throws Exception { + Options options = new OptionsBuilder() + .include(WriteJsonTest.class.getSimpleName()) + .output("/tmp/" + WriteJsonTest.class.getSimpleName() + ".log").build(); + new Runner(options).run(); + } +} From 6b78a376ab3c2b775c2c7c0e3fe046f4e4714027 Mon Sep 17 00:00:00 2001 From: kcp Date: Sat, 9 May 2020 10:42:16 +0800 Subject: [PATCH 158/476] +) sonarqube --- build.gradle | 12 ++++++++++++ common-config/build.gradle | 4 +++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 9a4d2d94..be70d4e1 100644 --- a/build.gradle +++ b/build.gradle @@ -1,9 +1,21 @@ apply from: 'dependency.gradle' +buildscript { + repositories { + maven { + url "https://plugins.gradle.org/m2/" + } + } + dependencies { + classpath "org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:2.8" + } +} + // parent and child project all use this config subprojects { apply plugin: 'java' apply plugin: 'maven-publish' + apply plugin: "org.sonarqube" group = 'com.github.kuangcp' diff --git a/common-config/build.gradle b/common-config/build.gradle index 96df3cbd..cdd97ca6 100644 --- a/common-config/build.gradle +++ b/common-config/build.gradle @@ -1,4 +1,6 @@ configurations { // void dependency loop compile.exclude module: 'common-config' -} \ No newline at end of file +} + +version = '1.0.0' \ No newline at end of file From 52c77b70045c128459505d395fa4b7ee23258146 Mon Sep 17 00:00:00 2001 From: kcp Date: Sat, 9 May 2020 11:53:15 +0800 Subject: [PATCH 159/476] +) notify --- .../src/main/java/thread/NotifyAndWait.java | 43 +++++++++ .../test/java/thread/NotifyAndWaitTest.java | 91 +++++++++++++++++++ 2 files changed, 134 insertions(+) create mode 100644 concurrency/src/main/java/thread/NotifyAndWait.java create mode 100644 concurrency/src/test/java/thread/NotifyAndWaitTest.java diff --git a/concurrency/src/main/java/thread/NotifyAndWait.java b/concurrency/src/main/java/thread/NotifyAndWait.java new file mode 100644 index 00000000..ffb1946d --- /dev/null +++ b/concurrency/src/main/java/thread/NotifyAndWait.java @@ -0,0 +1,43 @@ +package thread; + +import java.util.Optional; +import java.util.UUID; +import lombok.extern.slf4j.Slf4j; + +/** + * @author https://github.com/kuangcp on 2020-05-09 11:01 + */ +@Slf4j +public class NotifyAndWait { + + private final String id; + private final String lock; + private String result; + + public NotifyAndWait(String lock) { + this.id = UUID.randomUUID().toString(); + this.lock = lock; + } + + public Optional call() throws InterruptedException { + synchronized (lock) { + Thread.sleep(1000); + } + synchronized (this) { + log.info("start wait {}", id); + this.wait(2000L); + } + return Optional.ofNullable(result).map(v -> v + " " + id); + } + + public void back(String result) { + this.result = result; + log.info("set={}", result); + + synchronized (this) { + this.notify(); + // TODO 如果对象被多个线程持有锁 +// this.notifyAll(); + } + } +} diff --git a/concurrency/src/test/java/thread/NotifyAndWaitTest.java b/concurrency/src/test/java/thread/NotifyAndWaitTest.java new file mode 100644 index 00000000..260f0ede --- /dev/null +++ b/concurrency/src/test/java/thread/NotifyAndWaitTest.java @@ -0,0 +1,91 @@ +package thread; + +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; + +/** + * @author https://github.com/kuangcp on 2020-05-09 11:01 + */ +@Slf4j +public class NotifyAndWaitTest { + + private final Set set = new HashSet<>(); + + @Test + public void testLoop() throws InterruptedException { + List locks = IntStream.range(1, 6).mapToObj(String::valueOf) + .collect(Collectors.toList()); + for (int i = 0; i < 500; i++) { + NotifyAndWait notifyAndWait = new NotifyAndWait(locks.get(i % 5)); + set.add(notifyAndWait); + } + + for (NotifyAndWait ele : set) { + new Thread(() -> { + try { + Optional result = ele.call(); + log.info("result={}", result); + } catch (InterruptedException e) { + log.error("", e); + } + }).start(); + } + + for (NotifyAndWait ele : set) { + new Thread(() -> { + try { + Thread.sleep(4000); + ele.back(System.currentTimeMillis() + ""); + } catch (InterruptedException e) { + e.printStackTrace(); + } + }).start(); + } + + Thread.sleep(300000); + } + + @Test + public void testMultiLoop() throws InterruptedException { + ExecutorService pool = Executors.newFixedThreadPool(3); + List locks = IntStream.range(1, 6).mapToObj(String::valueOf) + .collect(Collectors.toList()); + for (int i = 0; i < 500; i++) { + NotifyAndWait notifyAndWait = new NotifyAndWait(locks.get(i % 5)); + set.add(notifyAndWait); + } + + for (NotifyAndWait ele : set) { + pool.submit(() -> { + try { + Optional result = ele.call(); + log.info(": result={}", result); + } catch (InterruptedException e) { + log.error("", e); + } + }); + } + + + for (NotifyAndWait ele : set) { + pool.submit(() -> { + try { + Thread.sleep(500); + ele.back(System.currentTimeMillis() + ""); + } catch (InterruptedException e) { + log.error("", e); + } + }); + } + + Thread.sleep(300000); + } +} From c0ae1faa671b1fc8468046f382937dfc2dc9f888 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Tue, 12 May 2020 08:41:11 +0800 Subject: [PATCH 160/476] *) refresh todo --- .../kuangcp/reflects/ReflectPerformanceTest.java | 2 +- spring/Readme.md | 14 +++++--------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/class/src/test/java/com/github/kuangcp/reflects/ReflectPerformanceTest.java b/class/src/test/java/com/github/kuangcp/reflects/ReflectPerformanceTest.java index e8293882..561215df 100644 --- a/class/src/test/java/com/github/kuangcp/reflects/ReflectPerformanceTest.java +++ b/class/src/test/java/com/github/kuangcp/reflects/ReflectPerformanceTest.java @@ -12,7 +12,7 @@ * 反射的性能问题 http://www.cnblogs.com/zhishan/p/3195771.html * cglib(已缓存) 耗时 50%-70% 于缓存, 10% 于 原始方式 * TODO 操作字节码方式取代反射 - * + * TODO jmh * @author kuangcp */ @Slf4j diff --git a/spring/Readme.md b/spring/Readme.md index 45443d6a..8b75a43d 100644 --- a/spring/Readme.md +++ b/spring/Readme.md @@ -1,12 +1,8 @@ -## 2018-11-25 23:16:17 -- 处理好了依赖问题, 但是 xml没有处理好 +# Spring 学习 +> [spring-learning](https://github.com/brianway/spring-learning) -## 2017-08-27 10:24:05 -- 新建成一个gradle项目,但是依赖还没处理 - - -## 2016.11.19 -> 通过一个简单例子重构多次,讲解AOP思想和Spring基础 +## Spring简易实现 +> [spring](https://github.com/Kuangcp/java-wheel/tree/master/spring)`刘欣 从0开始造Spring` ## AOP ### XML实现 @@ -16,4 +12,4 @@ ### 备注: - 该项目使用了详解代码,以便理解IOC,DI,AOP,JDBC和Hibernate的事务动态处理 -- 使用基本的几个案例一步步的升级实现方法 \ No newline at end of file +- 使用基本的几个案例一步步的升级实现方法 From 0ec404dfb9f91a729b780ebf96625e5bf2462042 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Sun, 17 May 2020 22:49:01 +0800 Subject: [PATCH 161/476] +) pool --- .../com/github/kuangcp/read/CommentTest.java | 16 +++++++ .../github/kuangcp/read/ClassScannerTest.java | 18 +++++++- .../kuangcp/serialize/SerializeTest.java | 4 +- .../kuangcp/runable/SlowRequestTest.java | 45 +++++++++++++++++++ .../kuangcp/runable/TuLingRobotTest.java | 6 +-- 5 files changed, 82 insertions(+), 7 deletions(-) create mode 100644 class/src/main/java/com/github/kuangcp/read/CommentTest.java create mode 100644 network/src/test/java/com/github/kuangcp/runable/SlowRequestTest.java diff --git a/class/src/main/java/com/github/kuangcp/read/CommentTest.java b/class/src/main/java/com/github/kuangcp/read/CommentTest.java new file mode 100644 index 00000000..7b20cfed --- /dev/null +++ b/class/src/main/java/com/github/kuangcp/read/CommentTest.java @@ -0,0 +1,16 @@ +package com.github.kuangcp.read; + +/** + * @author https://github.com/kuangcp on 2020-05-17 17:41 + */ +public class CommentTest { + + /** + * https://docs.oracle.com/javase/specs/jls/se7/html/jls-3.html + * @param args + */ + public static void main(String[] args) { + // Unicode 逃逸 + // \u000d System.out.println("hi"); + } +} diff --git a/class/src/test/java/com/github/kuangcp/read/ClassScannerTest.java b/class/src/test/java/com/github/kuangcp/read/ClassScannerTest.java index 309e2cfb..0fce27a3 100644 --- a/class/src/test/java/com/github/kuangcp/read/ClassScannerTest.java +++ b/class/src/test/java/com/github/kuangcp/read/ClassScannerTest.java @@ -7,6 +7,7 @@ import java.util.List; import java.util.Set; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.time.StopWatch; import org.junit.Test; @@ -43,14 +44,27 @@ public void testGetPackageAllClasses() { @Test public void testReadJar() { - GetRunTime getRunTime = new GetRunTime().startCount(); + StopWatch watch = new StopWatch(); ClassScanner scanner = new ClassScanner(true, true, Collections.emptyList()); String path = ResourceTool.class.getPackage().getName(); log.info("path={}", path); + + watch.start(); Set> result = scanner.getPackageAllClasses(path, true); log.info("result {}", result.size()); result.forEach(System.out::println); - getRunTime.endCountOneLine(""); + watch.stop(); + System.out.println(watch.toString()); + + watch.reset(); + watch.start(); + + scanner = new ClassScanner(true, true, Collections.emptyList()); + result = scanner.getPackageAllClasses("com.google.gson", true); + log.info("result {}", result.size()); + result.forEach(System.out::println); + watch.stop(); + System.out.println(watch.toString()); } private static String lowerCaseFirstLetter(String name) { diff --git a/class/src/test/java/com/github/kuangcp/serialize/SerializeTest.java b/class/src/test/java/com/github/kuangcp/serialize/SerializeTest.java index 70c34397..81ecbd6e 100644 --- a/class/src/test/java/com/github/kuangcp/serialize/SerializeTest.java +++ b/class/src/test/java/com/github/kuangcp/serialize/SerializeTest.java @@ -55,7 +55,7 @@ public void testSerializeWithFile() { private void writeFile() throws IOException { Person person = new Person("myth"); - FileOutputStream fileOutputStream = new FileOutputStream("/home/kcp/test/person.md"); + FileOutputStream fileOutputStream = new FileOutputStream("/tmp/person.log"); ObjectOutputStream out = new ObjectOutputStream(fileOutputStream); out.writeObject(person); out.close(); @@ -64,7 +64,7 @@ private void writeFile() throws IOException { } private void readFile() throws IOException, ClassNotFoundException { - FileInputStream fileInputStream = new FileInputStream("/home/kcp/test/person.md"); + FileInputStream fileInputStream = new FileInputStream("/tmp/person.log"); ObjectInputStream in = new ObjectInputStream(fileInputStream); Person object = (Person) in.readObject(); in.close(); diff --git a/network/src/test/java/com/github/kuangcp/runable/SlowRequestTest.java b/network/src/test/java/com/github/kuangcp/runable/SlowRequestTest.java new file mode 100644 index 00000000..d92476eb --- /dev/null +++ b/network/src/test/java/com/github/kuangcp/runable/SlowRequestTest.java @@ -0,0 +1,45 @@ +package com.github.kuangcp.runable; + +import java.util.Optional; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; + +/** + * @author https://github.com/kuangcp on 2020-05-17 22:07 + */ +@Slf4j +public class SlowRequestTest { + + @Test + public void testSlowSingleConnection() throws Exception { + int num = 200; + CountDownLatch latch = new CountDownLatch(num); + ExecutorService pool = Executors.newFixedThreadPool(10); + for (int i = 0; i < num; i++) { + pool.submit(() -> { + latch.countDown(); + try { + Optional result = TuLingRobotTest.get("http://localhost:8080/ping"); + assert result.isPresent(); +// log.info(result.get()); + } catch (Exception e) { + e.printStackTrace(); + } + }); + } + + while (latch.getCount() != 0) { + TimeUnit.SECONDS.sleep(10); + } + } + + // TODO Http connection pool + @Test + public void testSlowHttpPool() throws Exception { + + } +} diff --git a/network/src/test/java/com/github/kuangcp/runable/TuLingRobotTest.java b/network/src/test/java/com/github/kuangcp/runable/TuLingRobotTest.java index 0987f197..0b79c2d7 100644 --- a/network/src/test/java/com/github/kuangcp/runable/TuLingRobotTest.java +++ b/network/src/test/java/com/github/kuangcp/runable/TuLingRobotTest.java @@ -21,15 +21,15 @@ @Slf4j public class TuLingRobotTest { - private OkHttpClient client = new OkHttpClient(); + private final static OkHttpClient client = new OkHttpClient(); - private Optional get(String url) throws IOException { + static Optional get(String url) throws IOException { Request request = new Request.Builder() .url(url).build(); try (Response response = client.newCall(request).execute()) { if (response.body() != null) { - return Optional.ofNullable(response.body().string()); + return Optional.of(response.body().string()); } return Optional.empty(); } From 3a852a367055056093e4ac8a37d9e927391cdac7 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Sat, 13 Jun 2020 12:55:30 +0800 Subject: [PATCH 162/476] *) optimize --- .../com/github/kuangcp/file/CopyFile.java | 4 +- .../com/github/kuangcp/PropertiesTest.java | 12 ++-- .../com/github/kuangcp/file/CopyFileTest.java | 59 ++++++++++--------- .../test/resources/properties/main.properties | 4 +- .../function/sort/BaseTypeSortTest.java | 4 +- .../kuangcp/function/sort/CustomSortTest.java | 1 - .../kuangcp/function/sort/DateSortTest.java | 8 ++- .../kuangcp/lambda/firstdemo/MainTest.java | 2 +- 8 files changed, 46 insertions(+), 48 deletions(-) diff --git a/io/src/main/java/com/github/kuangcp/file/CopyFile.java b/io/src/main/java/com/github/kuangcp/file/CopyFile.java index a8113fcd..b724a106 100644 --- a/io/src/main/java/com/github/kuangcp/file/CopyFile.java +++ b/io/src/main/java/com/github/kuangcp/file/CopyFile.java @@ -19,9 +19,7 @@ /** * 关于相对路径文件的复制问题 * - * InputStream 所有输入流基类(建立流)(字节流) - * InputStreamReader 将字节流 转换成 字符流 - * BufferedReader 从字符流输入流读取文件 + * InputStream 所有输入流基类(建立流)(字节流) InputStreamReader 将字节流 转换成 字符流 BufferedReader 从字符流输入流读取文件 * * 注意: 如果输出流尚未关闭 内存的数据尚未刷新到硬盘上, 会导致不一致的情况 */ diff --git a/io/src/test/java/com/github/kuangcp/PropertiesTest.java b/io/src/test/java/com/github/kuangcp/PropertiesTest.java index c7739bba..bdebb642 100644 --- a/io/src/test/java/com/github/kuangcp/PropertiesTest.java +++ b/io/src/test/java/com/github/kuangcp/PropertiesTest.java @@ -1,6 +1,5 @@ package com.github.kuangcp; -import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; @@ -16,15 +15,14 @@ public class PropertiesTest { @Test public void testRead() throws IOException { + String path = Properties.class.getResource("/properties/main.properties").getPath(); Properties properties = new Properties(); -// String path = Properties.class.getResource("./properties/main.properties").getPath(); - System.out.println(new File("").getAbsolutePath()); - properties.load(new FileInputStream("src/test/resources/properties/main.properties")); + properties.load(new FileInputStream(path)); String a = properties.getProperty("A"); - String b = new String(properties.getProperty("B").getBytes(StandardCharsets.ISO_8859_1), - StandardCharsets.UTF_8); + String b = properties.getProperty("B"); + String decodedB = new String(b.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8); - log.info("a={} b={}", a, b); + log.info("a={} b=[{}] [{}]", a, b, decodedB); } } diff --git a/io/src/test/java/com/github/kuangcp/file/CopyFileTest.java b/io/src/test/java/com/github/kuangcp/file/CopyFileTest.java index b1092528..a1d7c829 100644 --- a/io/src/test/java/com/github/kuangcp/file/CopyFileTest.java +++ b/io/src/test/java/com/github/kuangcp/file/CopyFileTest.java @@ -21,65 +21,66 @@ @Slf4j public class CopyFileTest { - private CopyFile file = new CopyFile(); + private final CopyFile file = new CopyFile(); - private String from = "src/test/resources/test.log"; - private String dest = "src/test/resources/test2.log"; - private Path fromPath = Paths.get(from); - private Path destPath = Paths.get(dest); + private final String root = CopyFileTest.class.getResource("/").getPath(); + private final String from = root + "test.log"; + private final String dest = root + "test2.log"; + private final Path fromPath = Paths.get(from); + private final Path destPath = Paths.get(dest); - private String content = "hello world"; + private final String content = "hello world"; + + @Before + public void createFile() throws IOException { + Files.createFile(fromPath); + Files.write(fromPath, content.getBytes()); + assert Files.exists(fromPath); + } + + @After + public void deleteFile() throws IOException { + Files.delete(fromPath); + Files.delete(destPath); + + assert !Files.exists(fromPath); + assert !Files.exists(destPath); + } @Test public void testFirst() throws IOException { file.copyFileWithSixChannel(from, dest); - validate(); + validateResultFile(); } @Test public void testCopyByChar() throws IOException { file.copyFileByChar(from, dest); - validate(); + validateResultFile(); } @Test public void testCopyByByte() throws IOException { file.copyFileByByte(from, dest); - validate(); + validateResultFile(); } @Test public void testCopyByCharBuffer() throws IOException { file.copyFileByCharBuffer(from, dest); - validate(); + validateResultFile(); } @Test public void testCopyByFiles() throws IOException { file.copyByFiles(fromPath, destPath); - validate(); + validateResultFile(); } - @Before - public void createFile() throws IOException { - Files.createFile(fromPath); - Files.write(fromPath, content.getBytes()); - assert Files.exists(fromPath); - } - - private void validate() throws IOException { - Optional fileContent = Files.lines(fromPath).reduce(String::concat); + private void validateResultFile() throws IOException { + Optional fileContent = Files.lines(destPath).reduce(String::concat); assert fileContent.isPresent(); log.debug("validate: content={}", fileContent.get()); assertThat(fileContent.get(), equalTo(content)); } - - @After - public void deleteFile() throws IOException { - Files.delete(fromPath); - Files.delete(destPath); - - assert !Files.exists(fromPath); - assert !Files.exists(destPath); - } } \ No newline at end of file diff --git a/io/src/test/resources/properties/main.properties b/io/src/test/resources/properties/main.properties index 45d4b767..c38ddc28 100644 --- a/io/src/test/resources/properties/main.properties +++ b/io/src/test/resources/properties/main.properties @@ -1,2 +1,2 @@ -A=1 -B=使用 \ No newline at end of file +A=a +B=中文 \ No newline at end of file diff --git a/java8/src/test/java/com/github/kuangcp/function/sort/BaseTypeSortTest.java b/java8/src/test/java/com/github/kuangcp/function/sort/BaseTypeSortTest.java index 740fa3db..683866b4 100644 --- a/java8/src/test/java/com/github/kuangcp/function/sort/BaseTypeSortTest.java +++ b/java8/src/test/java/com/github/kuangcp/function/sort/BaseTypeSortTest.java @@ -27,7 +27,7 @@ public void testInt() { GetRunTime getRunTime = new GetRunTime().startCount(); lists.sort(Comparator.comparingInt(Integer::intValue)); - getRunTime.endCount("排序完成"); + getRunTime.endCount("int 排序完成"); } // TODO 一起执行时 为什么这个后执行的方法要快, 单独执行就不会有这样的情况 @@ -39,6 +39,6 @@ public void testLong() { GetRunTime getRunTime = new GetRunTime().startCount(); lists.sort(Comparator.comparingLong(Long::longValue)); - getRunTime.endCount("排序完成"); + getRunTime.endCount("long 排序完成"); } } diff --git a/java8/src/test/java/com/github/kuangcp/function/sort/CustomSortTest.java b/java8/src/test/java/com/github/kuangcp/function/sort/CustomSortTest.java index d79e2354..d9dabedf 100644 --- a/java8/src/test/java/com/github/kuangcp/function/sort/CustomSortTest.java +++ b/java8/src/test/java/com/github/kuangcp/function/sort/CustomSortTest.java @@ -26,7 +26,6 @@ public void testString() { return 0; }); - assertTrue(maxOpt.isPresent()); assertThat(maxOpt.get(), equalTo("map23")); } } diff --git a/java8/src/test/java/com/github/kuangcp/function/sort/DateSortTest.java b/java8/src/test/java/com/github/kuangcp/function/sort/DateSortTest.java index 23b8ed5d..e9e5e08c 100644 --- a/java8/src/test/java/com/github/kuangcp/function/sort/DateSortTest.java +++ b/java8/src/test/java/com/github/kuangcp/function/sort/DateSortTest.java @@ -38,12 +38,14 @@ public void testLocalDateTime() { @Test public void testSimple() { IntStream.rangeClosed(1, 10) - .mapToObj(i -> LocalDateTime.now().plusDays(i)).sorted((a, b) -> a.compareTo(b) * -1) + .mapToObj(i -> LocalDateTime.now().plusDays(i)) + .sorted((a, b) -> a.compareTo(b) * -1) .forEach(System.out::println); List data = IntStream.rangeClosed(1, 10) - .mapToObj(i -> LocalDateTime.now().plusDays(i)).collect(Collectors.toList()); - data.sort((a, b) -> a.compareTo(b) * -1); + .mapToObj(i -> LocalDateTime.now().plusDays(i)) + .sorted((a, b) -> a.compareTo(b) * -1) + .collect(Collectors.toList()); data.forEach(System.out::println); } diff --git a/java8/src/test/java/com/github/kuangcp/lambda/firstdemo/MainTest.java b/java8/src/test/java/com/github/kuangcp/lambda/firstdemo/MainTest.java index 5d36d3c5..92df5d6a 100644 --- a/java8/src/test/java/com/github/kuangcp/lambda/firstdemo/MainTest.java +++ b/java8/src/test/java/com/github/kuangcp/lambda/firstdemo/MainTest.java @@ -17,8 +17,8 @@ public void init() { pointArrayList.add(new Point(5, 5)); } - @Test // 1. 利用内部迭代, 将一个接口实现类的对象传入 + @Test public void testByPolymorphic() { pointArrayList.forEach(new TranslateByOne()); From 3407b2ba187df57fce607e0e3f77770c2cd03cde Mon Sep 17 00:00:00 2001 From: kcp Date: Thu, 13 Aug 2020 15:46:55 +0800 Subject: [PATCH 163/476] +) serialize with non serializable field --- .../com/github/kuangcp/serialize/Address.java | 22 ++++++++++ .../com/github/kuangcp/serialize/Person.java | 2 - .../kuangcp/serialize/SerializeTest.java | 41 ++++++++++++++++++- 3 files changed, 61 insertions(+), 4 deletions(-) create mode 100644 class/src/main/java/com/github/kuangcp/serialize/Address.java diff --git a/class/src/main/java/com/github/kuangcp/serialize/Address.java b/class/src/main/java/com/github/kuangcp/serialize/Address.java new file mode 100644 index 00000000..4091e982 --- /dev/null +++ b/class/src/main/java/com/github/kuangcp/serialize/Address.java @@ -0,0 +1,22 @@ +package com.github.kuangcp.serialize; + +import java.io.Serializable; +import lombok.Data; + +/** + * @author https://github.com/kuangcp on 2020-08-13 15:19 + */ +@Data +public class Address implements Serializable { + + private String country; + + private Street street; + +} + +@Data +class Street { + + private String street; +} \ No newline at end of file diff --git a/class/src/main/java/com/github/kuangcp/serialize/Person.java b/class/src/main/java/com/github/kuangcp/serialize/Person.java index 4ef3165d..61e5b588 100644 --- a/class/src/main/java/com/github/kuangcp/serialize/Person.java +++ b/class/src/main/java/com/github/kuangcp/serialize/Person.java @@ -15,8 +15,6 @@ public class Person implements Serializable { private String address; private String phone; - public Person(){} - public Person(String name) { this.name = name; } diff --git a/class/src/test/java/com/github/kuangcp/serialize/SerializeTest.java b/class/src/test/java/com/github/kuangcp/serialize/SerializeTest.java index 81ecbd6e..3c9fe664 100644 --- a/class/src/test/java/com/github/kuangcp/serialize/SerializeTest.java +++ b/class/src/test/java/com/github/kuangcp/serialize/SerializeTest.java @@ -8,6 +8,7 @@ import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; +import java.io.NotSerializableException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import lombok.extern.slf4j.Slf4j; @@ -15,8 +16,7 @@ import org.junit.Test; /** - * Created by https://github.com/kuangcp on 17-10-24 下午2:27 - * 使用jdk自带的Serializable接口序列化对象 然后反序列化对象 + * Created by https://github.com/kuangcp on 17-10-24 下午2:27 使用jdk自带的Serializable接口序列化对象 然后反序列化对象 * * @author kuangcp */ @@ -41,6 +41,43 @@ public void testSerializeWithByte() throws IOException, ClassNotFoundException { assertThat(result.getName(), equalTo("name")); } + /** + * 序列化对象上某个属性没有实现序列化接口,但该属性没有值 + */ + @Test + public void testSerializeWithNonSerializableField() throws IOException, ClassNotFoundException { + Address address = new Address(); + address.setCountry("country"); + + ByteArrayOutputStream byteOutput = new ByteArrayOutputStream(); + ObjectOutputStream output = new ObjectOutputStream(byteOutput); + output.writeObject(address); + + log.info("content={}", byteOutput.toString()); + + ByteArrayInputStream byteInput = new ByteArrayInputStream(byteOutput.toByteArray()); + + ObjectInputStream input = new ObjectInputStream(byteInput); + Address result = (Address) input.readObject(); + assertThat(result.getCountry(), equalTo("country")); + } + + /** + * 序列化对象上某个属性没有实现序列化接口,且该属性有值 + */ + @Test(expected = NotSerializableException.class) + public void testSerializeWithNonSerializableFieldAndValue() throws IOException { + Address address = new Address(); + address.setCountry("country"); + Street street = new Street(); + street.setStreet("street"); + address.setStreet(street); + + ByteArrayOutputStream byteOutput = new ByteArrayOutputStream(); + ObjectOutputStream output = new ObjectOutputStream(byteOutput); + output.writeObject(address); + } + @Test public void testSerializeWithFile() { try { From 66c55648a65cc09b2318bdf3fd3076ca9c30cc0d Mon Sep 17 00:00:00 2001 From: kcp Date: Sat, 22 Aug 2020 15:08:59 +0800 Subject: [PATCH 164/476] *) open msg api --- .../java/netty/timeServer/TimeClient.java | 28 ++++++- .../netty/timeServer/TimeClientHandler.java | 49 ++++++++++-- .../java/netty/timeServer/TimeServer.java | 15 +++- .../netty/timeServer/TimeServerHandler.java | 18 ++++- .../java/netty/timeServer/TimeClientTest.java | 77 +++++++++++++++++++ .../java/netty/timeServer/TimeServerTest.java | 9 +-- 6 files changed, 174 insertions(+), 22 deletions(-) create mode 100644 netty/src/test/java/netty/timeServer/TimeClientTest.java diff --git a/netty/src/main/java/netty/timeServer/TimeClient.java b/netty/src/main/java/netty/timeServer/TimeClient.java index 6432768c..4e0fd3d9 100644 --- a/netty/src/main/java/netty/timeServer/TimeClient.java +++ b/netty/src/main/java/netty/timeServer/TimeClient.java @@ -1,6 +1,7 @@ package netty.timeServer; import io.netty.bootstrap.Bootstrap; +import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOption; @@ -13,6 +14,9 @@ @Slf4j class TimeClient { + private final TimeClientHandler timeClientHandler = new TimeClientHandler(); + private Channel channel; + void connectLocal(int port) throws Exception { connect(port, "127.0.0.1"); } @@ -20,6 +24,7 @@ void connectLocal(int port) throws Exception { void connect(int port, String host) throws Exception { // 配置客户端NIO线程组 EventLoopGroup group = new NioEventLoopGroup(); + try { Bootstrap bootstrap = new Bootstrap(); bootstrap.group(group).channel(NioSocketChannel.class) @@ -27,7 +32,7 @@ void connect(int port, String host) throws Exception { .handler(new ChannelInitializer() { @Override public void initChannel(SocketChannel ch) { - ch.pipeline().addLast(new TimeClientHandler()); + ch.pipeline().addLast(timeClientHandler); log.info("add new client handler"); } }); @@ -36,10 +41,29 @@ public void initChannel(SocketChannel ch) { ChannelFuture channelFuture = bootstrap.connect(host, port).sync(); // 当前客户端链路关闭 - channelFuture.channel().closeFuture().sync(); + this.channel = channelFuture.channel(); + channel.closeFuture().sync(); } finally { // 优雅退出,释放NIO线程组 group.shutdownGracefully(); } } + + void sendMsg(String msg) { + timeClientHandler.sendMsg(msg); + } + + void flush() { + this.timeClientHandler.getCtx().flush(); + } + + void stop() { + this.timeClientHandler.getCtx().flush(); + this.timeClientHandler.close(); + this.channel.close(); + } + + boolean isReady() { + return timeClientHandler.isConnected(); + } } diff --git a/netty/src/main/java/netty/timeServer/TimeClientHandler.java b/netty/src/main/java/netty/timeServer/TimeClientHandler.java index 7212bf01..824186be 100644 --- a/netty/src/main/java/netty/timeServer/TimeClientHandler.java +++ b/netty/src/main/java/netty/timeServer/TimeClientHandler.java @@ -4,7 +4,9 @@ import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; +import io.netty.util.internal.StringUtil; import java.nio.charset.StandardCharsets; +import java.util.Objects; import lombok.extern.slf4j.Slf4j; /** @@ -13,16 +15,20 @@ @Slf4j class TimeClientHandler extends SimpleChannelInboundHandler { + private ChannelHandlerContext ctx; + /** * 当客户端和服务端成功建立连接后, 就会调用该方法 */ @Override public void channelActive(ChannelHandlerContext ctx) { - byte[] req = Command.QUERY_TIME.getBytes(); - ByteBuf firstMessage = Unpooled.buffer(req.length); - firstMessage.writeBytes(req); - // 将消息发送至服务端 - ctx.writeAndFlush(firstMessage); + log.info("connected"); + if (Objects.isNull(this.ctx)) { + this.ctx = ctx; + } + } + public boolean isConnected (){ + return Objects.nonNull(ctx); } /** @@ -38,8 +44,8 @@ public void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception log.info("client receive msg: {}", body); - // 收到消息就关闭连接 - ctx.close(); +// // 收到消息就关闭连接 +// ctx.close(); } /** @@ -51,4 +57,33 @@ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { log.warn("Unexpected exception from downstream: {}", cause.getMessage()); ctx.close(); } + + /** + * 这里就需要考虑和服务端协商 发送数据的正确反序列化问题了,也就是伪TCP问题: "拆包" + */ + public void sendMsg(String msg) { + if (StringUtil.isNullOrEmpty(msg)) { + return; + } + + if (Objects.isNull(ctx)) { + log.warn("not connect"); + return; + } + + log.info("msg={}", msg); + byte[] req = msg.getBytes(); + ByteBuf firstMessage = Unpooled.buffer(req.length); + firstMessage.writeBytes(req); + // 将消息发送至服务端 + ctx.writeAndFlush(firstMessage); + } + + public ChannelHandlerContext getCtx() { + return ctx; + } + + public void close() { + this.ctx.close(); + } } diff --git a/netty/src/main/java/netty/timeServer/TimeServer.java b/netty/src/main/java/netty/timeServer/TimeServer.java index 719f184b..35d4e182 100644 --- a/netty/src/main/java/netty/timeServer/TimeServer.java +++ b/netty/src/main/java/netty/timeServer/TimeServer.java @@ -1,6 +1,7 @@ package netty.timeServer; import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOption; @@ -10,9 +11,11 @@ import io.netty.channel.socket.nio.NioServerSocketChannel; import lombok.extern.slf4j.Slf4j; +@Slf4j class TimeServer { static final int port = 8080; + private Channel channel; public void start() throws Exception { // NIO 结合两个线程池 @@ -26,11 +29,11 @@ public void start() throws Exception { serverBootstrap.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .option(ChannelOption.SO_BACKLOG, 512) - .childHandler(new ChannelInit()); + .childHandler(new ChannelInit(this)); // 绑定端口,同步等待成功 返回一个ChannelFuture, 用于异步操作的通知回调 ChannelFuture future = serverBootstrap.bind(port).sync(); - + this.channel = future.channel(); // 等待服务端监听端口关闭 future.channel().closeFuture().sync(); } finally { @@ -40,13 +43,19 @@ public void start() throws Exception { } } + public void stop() { + log.info("stop server"); + this.channel.close(); + } + @Slf4j private static class ChannelInit extends ChannelInitializer { TimeServerHandler handler = new TimeServerHandler(); - public ChannelInit() { + public ChannelInit(TimeServer timeServer) { log.info("init a initializer"); + handler.setTimeServer(timeServer); } @Override diff --git a/netty/src/main/java/netty/timeServer/TimeServerHandler.java b/netty/src/main/java/netty/timeServer/TimeServerHandler.java index 548e7e1e..b5830dd6 100644 --- a/netty/src/main/java/netty/timeServer/TimeServerHandler.java +++ b/netty/src/main/java/netty/timeServer/TimeServerHandler.java @@ -14,14 +14,17 @@ * 服务端的业务代码 继承自 5: ChannelHandlerAdapter 4: SimpleChannelInboundHandler */ @Slf4j -@Sharable // 可被注册到多个 pipeline +@Sharable + // 可被注册到多个 pipeline class TimeServerHandler extends SimpleChannelInboundHandler { + private TimeServer timeServer; + public TimeServerHandler() { log.warn("init a instance"); } - private static AtomicInteger counter = new AtomicInteger(); + private static final AtomicInteger counter = new AtomicInteger(); /** * 成功建立连接后, 读取服务端的消息 @@ -38,6 +41,9 @@ public void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception String result; if (Command.QUERY_TIME.equalsIgnoreCase(body)) { result = LocalDateTime.now().toString(); + } else if (Command.STOP_SERVER.equalsIgnoreCase(body)) { + timeServer.stop(); + result = Command.STOP_SERVER; } else { result = "BAD ORDER"; } @@ -60,4 +66,12 @@ public void channelReadComplete(ChannelHandlerContext ctx) { public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { ctx.close(); } + + public TimeServer getTimeServer() { + return timeServer; + } + + public void setTimeServer(TimeServer timeServer) { + this.timeServer = timeServer; + } } diff --git a/netty/src/test/java/netty/timeServer/TimeClientTest.java b/netty/src/test/java/netty/timeServer/TimeClientTest.java new file mode 100644 index 00000000..24973468 --- /dev/null +++ b/netty/src/test/java/netty/timeServer/TimeClientTest.java @@ -0,0 +1,77 @@ +package netty.timeServer; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import lombok.extern.slf4j.Slf4j; +import org.testng.annotations.Test; + +/** + * @author https://github.com/kuangcp on 2020-08-22 14:28 + */ +@Slf4j +public class TimeClientTest { + + private final TimeClient timeClient = new TimeClient(); + + // @Test + @Test(threadPoolSize = 5, invocationCount = 20) + public void testClient() throws Exception { + startClient(); + timeClient.sendMsg(Command.QUERY_TIME); + } + + @Test + public void testConcurrencyClient() throws Exception { + ExecutorService pool = Executors.newFixedThreadPool(2); + + startClient(); + + int num = 100; + CountDownLatch countDownLatch = new CountDownLatch(num); + for (int i = 0; i < num; i++) { + pool.submit(() -> { + try { + timeClient.sendMsg(Command.QUERY_TIME); + } catch (Exception e) { + log.error("", e); + } finally { + countDownLatch.countDown(); + } + }); + } + + countDownLatch.await(); + + // why not flush cache + timeClient.flush(); + Thread.sleep(4000); + +// timeClient.sendMsg(Command.STOP_SERVER); + timeClient.stop(); + } + + void startClient() throws InterruptedException { + if (timeClient.isReady()) { + return; + } + + Thread thread = new Thread(() -> { + try { + timeClient.connectLocal(TimeServer.port); + } catch (Exception e) { + log.error("", e); + } + }); + + thread.start(); + + // 当循环体为空时会陷入死循环,即使已经准备好也没有退出 + while (!timeClient.isReady()) { +// Thread.sleep(10); + } + log.info("client ready"); + } + +} + diff --git a/netty/src/test/java/netty/timeServer/TimeServerTest.java b/netty/src/test/java/netty/timeServer/TimeServerTest.java index 29e16d4c..68fea0c4 100644 --- a/netty/src/test/java/netty/timeServer/TimeServerTest.java +++ b/netty/src/test/java/netty/timeServer/TimeServerTest.java @@ -10,14 +10,7 @@ @Slf4j public class TimeServerTest { - private TimeClient timeClient = new TimeClient(); - private TimeServer timeServer = new TimeServer(); - - // @Test - @Test(threadPoolSize = 5, invocationCount = 20) - public void testClient() throws Exception { - timeClient.connectLocal(TimeServer.port); - } + private final TimeServer timeServer = new TimeServer(); @Test public void testServer() throws Exception { From 948e2887508d3dde6de2c6f5765282091ad5c273 Mon Sep 17 00:00:00 2001 From: kcp Date: Mon, 24 Aug 2020 10:02:02 +0800 Subject: [PATCH 165/476] *) factory --- .../virusbroadcast/VirusBroadcast.java | 36 +++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/gui/src/main/java/com/github/kuangcp/virusbroadcast/VirusBroadcast.java b/gui/src/main/java/com/github/kuangcp/virusbroadcast/VirusBroadcast.java index 70f1e8c1..f665ab67 100644 --- a/gui/src/main/java/com/github/kuangcp/virusbroadcast/VirusBroadcast.java +++ b/gui/src/main/java/com/github/kuangcp/virusbroadcast/VirusBroadcast.java @@ -3,12 +3,17 @@ import com.github.kuangcp.virusbroadcast.domain.City; import com.github.kuangcp.virusbroadcast.gui.DisplayPanel; import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; import javax.swing.JFrame; public class VirusBroadcast { - private static ExecutorService executors = Executors.newFixedThreadPool(2); + private static final ExecutorService executors = new ThreadPoolExecutor(2, 2, 0L, + TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(), new DefaultThreadFactory("virus")); public static void main(String[] args) { DisplayPanel panel = new DisplayPanel(); @@ -23,3 +28,30 @@ public static void main(String[] args) { executors.submit(City.getInstance()); } } + +class DefaultThreadFactory implements ThreadFactory { + + private final AtomicInteger threadNumber = new AtomicInteger(1); + private final ThreadGroup group; + private final String namePrefix; + + DefaultThreadFactory(String namePrefix) { + SecurityManager var1 = System.getSecurityManager(); + this.group = var1 != null ? var1.getThreadGroup() : Thread.currentThread().getThreadGroup(); + this.namePrefix = namePrefix; + } + + public Thread newThread(Runnable runnable) { + Thread var2 = new Thread(this.group, runnable, + this.namePrefix + "-" + this.threadNumber.getAndIncrement(), 0L); + if (var2.isDaemon()) { + var2.setDaemon(false); + } + + if (var2.getPriority() != 5) { + var2.setPriority(5); + } + + return var2; + } +} \ No newline at end of file From d6b7aed9d4e701b8e024f5c700448001a608df14 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Tue, 25 Aug 2020 00:49:56 +0800 Subject: [PATCH 166/476] *) CompletableFuture --- .../InstantiationAndConstructorTest.java | 4 +- class/src/test/java/log/LogLevelTest.java | 32 +++++++++ .../concurrent/CompletableFutureTest.java | 24 ------- .../kuangcp/future/CompletableFutureTest.java | 70 +++++++++++++++++++ 4 files changed, 104 insertions(+), 26 deletions(-) create mode 100644 class/src/test/java/log/LogLevelTest.java delete mode 100644 java8/src/test/java/com/github/kuangcp/concurrent/CompletableFutureTest.java create mode 100644 java8/src/test/java/com/github/kuangcp/future/CompletableFutureTest.java diff --git a/class/src/test/java/com/github/kuangcp/instantiation/InstantiationAndConstructorTest.java b/class/src/test/java/com/github/kuangcp/instantiation/InstantiationAndConstructorTest.java index ff755c8a..e63b672d 100644 --- a/class/src/test/java/com/github/kuangcp/instantiation/InstantiationAndConstructorTest.java +++ b/class/src/test/java/com/github/kuangcp/instantiation/InstantiationAndConstructorTest.java @@ -30,7 +30,7 @@ @Slf4j public class InstantiationAndConstructorTest { - // 调用构造器 + // 直接调用构造器 @Test public void testInitByNew() { InstantiationAndConstructor instance = new InstantiationAndConstructor("name"); @@ -94,7 +94,7 @@ public void testInitByDeserialize() throws IOException, ClassNotFoundException { assertThat(deserialize.getName(), equalTo("name")); } - // Unsafe allocateInstance 方法 + // Unsafe allocateInstance 方法 不会调用构造器 @Test public void testInitByUnsafe() throws Exception { Field field = Unsafe.class.getDeclaredField("theUnsafe"); diff --git a/class/src/test/java/log/LogLevelTest.java b/class/src/test/java/log/LogLevelTest.java new file mode 100644 index 00000000..5733f890 --- /dev/null +++ b/class/src/test/java/log/LogLevelTest.java @@ -0,0 +1,32 @@ +package log; + +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; + +/** + * @author https://github.com/kuangcp on 2020-07-11 17:01 + */ +@Slf4j +public class LogLevelTest { + + private String invoke() { + System.out.println("invoke"); + return ""; + } + + @Test + public void testDebug() throws Exception { + System.out.println("start"); + log.info("start"); + if (log.isDebugEnabled()) { + log.debug(invoke()); + } + } + + @Test + public void testI() throws Exception { + int i = 1; + i = ++i + ++i; + System.out.println(i); + } +} diff --git a/java8/src/test/java/com/github/kuangcp/concurrent/CompletableFutureTest.java b/java8/src/test/java/com/github/kuangcp/concurrent/CompletableFutureTest.java deleted file mode 100644 index d5db5d1c..00000000 --- a/java8/src/test/java/com/github/kuangcp/concurrent/CompletableFutureTest.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.github.kuangcp.concurrent; - -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import lombok.extern.slf4j.Slf4j; -import org.junit.Test; - -@Slf4j -public class CompletableFutureTest { - - @Test - public void testAsync() throws InterruptedException, ExecutionException, TimeoutException { - CompletableFuture future = CompletableFuture.runAsync(() -> { - System.out.println("temp"); - }); - - future.get(5, TimeUnit.SECONDS); - if (future.isDone()) { - System.out.println("complete"); - } - } -} diff --git a/java8/src/test/java/com/github/kuangcp/future/CompletableFutureTest.java b/java8/src/test/java/com/github/kuangcp/future/CompletableFutureTest.java new file mode 100644 index 00000000..12df4ecd --- /dev/null +++ b/java8/src/test/java/com/github/kuangcp/future/CompletableFutureTest.java @@ -0,0 +1,70 @@ +package com.github.kuangcp.future; + +import static org.hamcrest.Matchers.equalTo; +import static org.junit.Assert.assertThat; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; + +@Slf4j +public class CompletableFutureTest { + + @Test + public void testAsync() throws Exception { + CompletableFuture future = CompletableFuture.runAsync(() -> System.out.println("temp")); + + future.get(5, TimeUnit.SECONDS); + if (future.isDone()) { + System.out.println("complete"); + } + } + + @Test + public void testFutureNeedOptimize() throws Exception { + ExecutorService executorService = Executors.newFixedThreadPool(10); + int num = 3; + + CompletableFuture func1Future = new CompletableFuture<>(); + executorService.submit(() -> func1Future.complete(func1(num))); + int b = func2(num); + int result = b + func1Future.get(); + System.out.println(result); + assertThat(result, equalTo(9)); + executorService.shutdown(); + } + + @Test + public void testFutureAndCombine() throws Exception { + ExecutorService executorService = Executors.newFixedThreadPool(10); + int num = 3; + + CompletableFuture func1Future = new CompletableFuture<>(); + CompletableFuture func2Future = new CompletableFuture<>(); + + // 只会在两个函数都完成计算后进行 避免get() 产生阻塞,并发的损失和死锁问题 + // CompletableFuture 和 结合器 一起使用 + CompletableFuture resultFuture = func1Future.thenCombine(func2Future, (a, b) -> a + b); + + executorService.submit(() -> func1Future.complete(func1(num))); + executorService.submit(() -> func2Future.complete(func2(num))); + + Integer result = resultFuture.get(); + System.out.println(result); + assertThat(result, equalTo(9)); + + executorService.shutdown(); + } + + private int func1(int value) { + return value + 1; + } + + private int func2(int value) { + return value + 2; + } + +} From fb53c418a571e519e0f97f27cb11cc109464358c Mon Sep 17 00:00:00 2001 From: kcp Date: Thu, 3 Sep 2020 15:46:34 +0800 Subject: [PATCH 167/476] +) dp --- .../src/test/java/com/github/kuangcp/a/A.java | 114 ------------------ .../src/test/java/com/github/kuangcp/a/B.java | 33 ----- .../dynamicprogramming/ClimbStairs.java | 83 +++++++++++++ .../MinCostClimbingStairs.java | 96 +++++++++++++++ concurrency/Readme.md | 2 + 5 files changed, 181 insertions(+), 147 deletions(-) delete mode 100644 algorithms/src/test/java/com/github/kuangcp/a/A.java delete mode 100644 algorithms/src/test/java/com/github/kuangcp/a/B.java create mode 100644 algorithms/src/test/java/com/github/kuangcp/dynamicprogramming/ClimbStairs.java create mode 100644 algorithms/src/test/java/com/github/kuangcp/dynamicprogramming/MinCostClimbingStairs.java diff --git a/algorithms/src/test/java/com/github/kuangcp/a/A.java b/algorithms/src/test/java/com/github/kuangcp/a/A.java deleted file mode 100644 index 124454d4..00000000 --- a/algorithms/src/test/java/com/github/kuangcp/a/A.java +++ /dev/null @@ -1,114 +0,0 @@ -package com.github.kuangcp.a; - -import static org.hamcrest.core.IsEqual.equalTo; -import static org.junit.Assert.assertThat; - -import lombok.AllArgsConstructor; -import lombok.ToString; -import lombok.extern.slf4j.Slf4j; -import org.junit.Test; - -/** - * created by https://gitee.com/gin9 - * 70 - * @author kuangcp on 18-9-16-下午8:33 - * https://www.jianshu.com/u/cbba9a1a8f0a - */ -@AllArgsConstructor -@ToString -class Temp implements Cloneable{ - private String name; - private String addr; - - public void a(){ - int a = 9; - Integer b = 4; - Long num = Long.valueOf(b); - try { - Temp clone = (Temp) this.clone(); - } catch (CloneNotSupportedException e) { - e.printStackTrace(); - } - - } -} - -@Slf4j -public class A { - - // 爬楼梯 - - // TODO 动态规划 找规律 找到.最终结果和输入 的关系 - - // 状态转换方程 - private static int n = 6; - - @Test - public void testSplit(){ - int length = "a,b,c,,".split(",").length; - System.out.println(length); - assertThat(length, equalTo(3)); - - Temp temp = new Temp("1", "1"); - temp.a(); - - } - - @Test - public void testA() { - helper(1, n); - helper(2, n); - - log.info(": count={}", count); - } - - @Test - public void testB() { - int result = dp(n); - log.info(": result={}", result); - } - - @Test - public void testC() { - int[] list = new int[n + 1]; - int result = dp2(n, list); - log.info(": result={}", result); - } - - private static int count = 0; - - private void helper(int step, int n) { - if (step > n) { - return; - } - if (step == n) { - count++; - } - helper(step + 1, n); - helper(step + 2, n); - - } - - // f(n) = f(n-1) + f(n-2) 具有大量重复计算 - private int dp(int n) { - if (n <= 2) { - return n; - } - return dp(n - 1) + dp(n - 2); - } - - /** - * use cache - */ - private int dp2(int n, int list[]) { - if (n <= 2) { - return n; - } - if (0 == list[n]) { - list[n] = dp2(n - 1, list) + dp2(n - 2, list); - } - - return list[n]; - } - -} diff --git a/algorithms/src/test/java/com/github/kuangcp/a/B.java b/algorithms/src/test/java/com/github/kuangcp/a/B.java deleted file mode 100644 index 975e8535..00000000 --- a/algorithms/src/test/java/com/github/kuangcp/a/B.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.github.kuangcp.a; - -import org.junit.Test; - -/** - * created by https://gitee.com/gin9 - * 746 - * - * @author kuangcp on 18-9-16-下午9:10 - */ -public class B { - private static int minCost = 0; - - /** - * 暴力 - */ - @Test - public void testA() { - - int[] cost = {1, 100, 1, 1, 1, 100}; - - } - - - private static void a(int step, int hp, int[] cost) { - if (step > cost.length) { - return; - } -// if (step == cost.length) { -// minCost = Math.min(cost, minCost); -// } - } -} diff --git a/algorithms/src/test/java/com/github/kuangcp/dynamicprogramming/ClimbStairs.java b/algorithms/src/test/java/com/github/kuangcp/dynamicprogramming/ClimbStairs.java new file mode 100644 index 00000000..e248d395 --- /dev/null +++ b/algorithms/src/test/java/com/github/kuangcp/dynamicprogramming/ClimbStairs.java @@ -0,0 +1,83 @@ +package com.github.kuangcp.dynamicprogramming; + +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; + +/** + * created by https://gitee.com/gin9 70 + * + * https://www.jianshu.com/u/cbba9a1a8f0a + * + * https://leetcode-cn.com/problems/climbing-stairs/description/ + * + * @author kuangcp on 18-9-16-下午8:33 + */ +@Slf4j +public class ClimbStairs { + + // 爬楼梯 + // 动态规划 找规律 找到.最终结果和输入 的关系 以及 状态转换方程 + + /** + * 总台阶数 + */ + private static final int totalStep = 3; + + private static int count = 0; + + @Test + public void testRecursion() { + // 第一次 1 个台阶的方式 + recursionStep(1, totalStep); + // 第一次 2 个台阶的方式 + recursionStep(2, totalStep); + + log.info("count={}", count); + } + + @Test + public void testDP() { + int result = dp(totalStep); + log.info(": result={}", result); + } + + @Test + public void testDPOptimize() { + int[] list = new int[totalStep + 1]; + int result = dp2(totalStep, list); + log.info(": result={}", result); + } + + private void recursionStep(int step, int n) { + if (step > n) { + return; + } + if (step == n) { + count++; + } + recursionStep(step + 1, n); + recursionStep(step + 2, n); + } + + // f(n) = f(n-1) + f(n-2) 具有大量重复计算 + private int dp(int n) { + if (n <= 2) { + return n; + } + return dp(n - 1) + dp(n - 2); + } + + /** + * use cache + */ + private int dp2(int n, int[] list) { + if (n <= 2) { + return n; + } + if (0 == list[n]) { + list[n] = dp2(n - 1, list) + dp2(n - 2, list); + } + + return list[n]; + } +} diff --git a/algorithms/src/test/java/com/github/kuangcp/dynamicprogramming/MinCostClimbingStairs.java b/algorithms/src/test/java/com/github/kuangcp/dynamicprogramming/MinCostClimbingStairs.java new file mode 100644 index 00000000..64b7ff39 --- /dev/null +++ b/algorithms/src/test/java/com/github/kuangcp/dynamicprogramming/MinCostClimbingStairs.java @@ -0,0 +1,96 @@ +package com.github.kuangcp.dynamicprogramming; + +import static org.hamcrest.Matchers.equalTo; +import static org.junit.Assert.assertThat; + +import org.junit.Test; + +/** + * @author https://github.com/kuangcp on 2020-09-03 12:39 + * + * https://leetcode-cn.com/problems/min-cost-climbing-stairs/description/ + */ +public class MinCostClimbingStairs { + + + @Test + public void testRecursion() { + int[] cost = {1, 100, 1, 1, 1, 100, 1, 1, 100, 1}; + int result = recursion(cost.length, cost); + assertThat(result, equalTo(6)); + } + + /** + * 关键: + * 1.可以从0 或者 1位置为起始点! + * 2.梯顶不是最后一个数 而是最后一个数后一个位置! + * dp[i] = min(dp[i-2] + cost[i-2], dp[i-1] + cost[i-1]) (dp[i]代表“到达”第i层所需最小花费) + * 自底向上(bottom-up) + */ + public static int recursion(int totalStep, int[] cost) { + if (0 == totalStep || 1 == totalStep) { + return 0; + } + + return Math.min(recursion(totalStep - 2, cost) + cost[totalStep - 2], + recursion(totalStep - 1, cost) + cost[totalStep - 1]); + } + + /** + * 关键: + * 1.可以从0 或者 1位置为起始点! + * 2.梯顶不是最后一个数 而是最后一个数后一个位置! + * dp[i] = min(dp[i-2] + cost[i-2], dp[i-1] + cost[i-1]) (dp[i]代表“到达”第i层所需最小花费) + * 自底向上(bottom-up) + */ + public static int dpRecursion(int totalStep, int[] cost, int[] dp) { + if (0 == totalStep || 1 == totalStep) { + return 0; + } + + if (0 == dp[totalStep]) { + dp[totalStep] = Math.min(dpRecursion(totalStep - 2, cost, dp) + cost[totalStep - 2], + dpRecursion(totalStep - 1, cost, dp) + cost[totalStep - 1]); + } + return dp[totalStep]; + } + + + /** + * 关键: + * 1.可以从0 或者 1位置为起始点! + * 2.梯顶不是最后一个数 而是最后一个数后一个位置! + * dp[i] = min(dp[i-2] + cost[i-2], dp[i-1] + cost[i-1]) (dp[i]代表“到达”第i层所需最小花费) + * 自底向上(bottom-up) + * 改为循环 + */ + public static int dpLoop(int[] cost) { + int[] dp = new int[cost.length + 1]; + for (int i = 2; i < cost.length + 1; ++i) { + dp[i] = Math.min(dp[i - 2] + cost[i - 2], dp[i - 1] + cost[i - 1]); + } + return dp[cost.length]; + } + + /** + * 关键: + * 1.可以从0 或者 1位置为起始点! + * 2.梯顶不是最后一个数 而是最后一个数后一个位置! + * dp[i] = cost[i] + min(dp[i- 1], dp[i-2]) (dp[i]代表“经过”第i层所需最小花费) + * 自底向上(bottom-up) + * 改为循环 + * 优化内存使用(滚动数组---只使用每一轮计算所需的缓存,通常是上一轮或者多轮的结果) + * 分析可得 只需要两个int变量交替使用即可达到要求 + */ + public static int dpLoopLessMemory(int[] cost) { + int temp1 = cost[0]; + int temp2 = cost[1]; + int res = 0; + for (int i = 2; i < cost.length; ++i) { + res = cost[i] + Math.min(temp1, temp2); + temp1 = temp2; + temp2 = res; + } + return Math.min(temp1, temp2); + } +} diff --git a/concurrency/Readme.md b/concurrency/Readme.md index c14a4c91..823110fa 100644 --- a/concurrency/Readme.md +++ b/concurrency/Readme.md @@ -1,2 +1,4 @@ # Java中的并发 > [笔记](https://github.com/Kuangcp/Note/blob/master/Java/AdvancedLearning/Concurrency.md) + +应尽量避免线程的阻塞和等待,因为这是在浪费CPU \ No newline at end of file From 792cd850d620897bfb4bb868092824860554a9ee Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Tue, 15 Sep 2020 22:28:00 +0800 Subject: [PATCH 168/476] +) class loader --- .../java/jvm/load/ShowClassLoaderTest.java | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 class/src/test/java/jvm/load/ShowClassLoaderTest.java diff --git a/class/src/test/java/jvm/load/ShowClassLoaderTest.java b/class/src/test/java/jvm/load/ShowClassLoaderTest.java new file mode 100644 index 00000000..25485c77 --- /dev/null +++ b/class/src/test/java/jvm/load/ShowClassLoaderTest.java @@ -0,0 +1,25 @@ +package jvm.load; + +import java.util.ArrayList; +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; + +/** + * @author https://github.com/kuangcp on 2020-09-12 19:48 + */ +@Slf4j +public class ShowClassLoaderTest { + + /** + * 这里分别对应三种不同类型的类加载器:AppClassLoader、ExtClassLoader 和 BootstrapClassLoader(显示为 null)。 + */ + @Test + public void test() throws Exception { + log.info("Classloader of this class:{}", ShowClassLoaderTest.class.getClassLoader()); + + log.info("Classloader of Logging:{}", + com.sun.java.accessibility.util.EventID.class.getClassLoader()); + + log.info("Classloader of ArrayList:{}", ArrayList.class.getClassLoader()); + } +} From 220719afef52b5896350c039738ea188a884975d Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Sun, 20 Sep 2020 16:02:15 +0800 Subject: [PATCH 169/476] x) concurrency for jedis --- .../src/main/java/redis/migration/Main.java | 156 ++++++++++-------- .../src/main/java/redis/migration/Readme.md | 3 +- .../main/java/redis/migration/RedisPools.java | 47 +----- .../test/java/redis/migration/MainTest.java | 8 +- 4 files changed, 97 insertions(+), 117 deletions(-) diff --git a/concurrency/src/main/java/redis/migration/Main.java b/concurrency/src/main/java/redis/migration/Main.java index 4ef68796..8eaba9d2 100644 --- a/concurrency/src/main/java/redis/migration/Main.java +++ b/concurrency/src/main/java/redis/migration/Main.java @@ -2,15 +2,19 @@ import static java.util.stream.Collectors.groupingBy; -import java.util.HashSet; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.Set; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import java.util.stream.Collectors; import lombok.extern.slf4j.Slf4j; import redis.clients.jedis.Jedis; +import redis.clients.jedis.JedisPool; import redis.clients.jedis.Tuple; /** @@ -19,13 +23,14 @@ @Slf4j public class Main { - private RedisPools origin = new RedisPools(); - private RedisPools target = new RedisPools(); + private final RedisPools origin = new RedisPools(); + private final RedisPools target = new RedisPools(); - private RedisPoolProperty originProperty; - private RedisPoolProperty targetProperty; - private Integer originDatabase; - private Integer targetDatabase; + private final RedisPoolProperty originProperty; + private final RedisPoolProperty targetProperty; + + private final Integer originDatabase; + private final Integer targetDatabase; Main(RedisPoolProperty originProperty, RedisPoolProperty targetProperty, Integer originDatabase, Integer targetDatabase) { @@ -35,49 +40,49 @@ public class Main { this.targetDatabase = targetDatabase; } - void transferAllKey() { - boolean init = init(); + /** + * Jedis对象 并不是并发安全的,所以应该使用JedisPool + */ + void transferAllKey() throws InterruptedException { + boolean init = initRedisPool(); if (!init) { return; } - if (!origin.isAvailable() || !target.isAvailable()) { - log.error("conn has invalid "); - return; - } - Optional originOpt = origin.getJedis(); - Optional targetOpt = target.getJedis(); + JedisPool originPool = origin.getJedisPool(); + JedisPool targetPool = target.getJedisPool(); - if (!originOpt.isPresent() || !targetOpt.isPresent()) { - log.warn("connect failed: "); - return; + try (final Jedis resource = originPool.getResource()) { + if (!RedisPools.isAvailable(resource)) { + log.error("conn has invalid "); + return; + } } - Jedis originRedis = originOpt.get(); - Jedis targetRedis = targetOpt.get(); - originRedis.select(originDatabase); - targetRedis.select(targetDatabase); - - Set keys = getKeys(origin, originProperty, originDatabase); -// keys.parallelStream().forEach(k -> System.out.println(System.currentTimeMillis() + " "+k)); - - // TODO 如果使用 parallelStream 就会 WRONGTYPE Operation against a key holding the wrong kind of value - // 不使用则不会 + try (final Jedis resource = targetPool.getResource()) { + if (!RedisPools.isAvailable(resource)) { + log.error("conn has invalid "); + return; + } + } + Set keys = getKeys(origin, originDatabase); + ExecutorService pool = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); - // 手动多线程 + final CountDownLatch latch = new CountDownLatch(keys.size()); Map> collect = keys.stream().collect(groupingBy(String::length)); for (List value : collect.values()) { - new Thread(() -> value.forEach(k -> transferOneKey(k, originRedis, targetRedis))).start(); + for (String k : value) { + pool.submit(() -> { + transferOneKey(k, originPool, targetPool); + latch.countDown(); + }); + } } - -// GetRunTime getRunTime = GetRunTime.GET_RUN_TIME; -// getRunTime.startCount(); -// keys.forEach(key -> transferOneKey(key, originRedis, targetRedis)); -// getRunTime.endCount("all"); + latch.await(); } - private boolean init() { + private boolean initRedisPool() { if (Objects.isNull(originProperty) || Objects.isNull(targetProperty)) { log.warn("init property error: "); return false; @@ -89,35 +94,41 @@ private boolean init() { return true; } - private void transferOneKey(String key, Jedis originJedis, Jedis targetJedis) { - String type = originJedis.type(key); - Optional dataType = RedisDataType.of(type); - - log.info("prepared : key={} type={}", key, type); - - if (!dataType.isPresent()) { - log.warn("unsupported data type: type={}", type); - return; - } - - switch (dataType.get()) { - case SET: - transferSet(key, originJedis, targetJedis); - break; - case HASH: - transferHash(key, originJedis, targetJedis); - break; - case LIST: - transferList(key, originJedis, targetJedis); - break; - case ZSET: - transferZSet(key, originJedis, targetJedis); - break; - case String: - targetJedis.set(key, originJedis.get(key)); - break; + private void transferOneKey(String key, JedisPool originPool, JedisPool targetPool) { + try (Jedis originJedis = originPool.getResource()) { + originJedis.select(originDatabase); + String type = originJedis.type(key); + Optional dataType = RedisDataType.of(type); + + if (!dataType.isPresent()) { + log.warn("unsupported data type: key={} type={}", key, type); + return; + } + + log.info("prepared : key={} type={}", key, type); + + try (final Jedis targetJedis = targetPool.getResource()) { + targetJedis.select(targetDatabase); + switch (dataType.get()) { + case SET: + transferSet(key, originJedis, targetJedis); + break; + case HASH: + transferHash(key, originJedis, targetJedis); + break; + case LIST: + transferList(key, originJedis, targetJedis); + break; + case ZSET: + transferZSet(key, originJedis, targetJedis); + break; + case String: + targetJedis.set(key, originJedis.get(key)); + break; + } + log.info("transferOneKey : key={} stamp={}", key, System.currentTimeMillis()); + } } - log.info("transferOneKey : key={} stamp={}", key, System.currentTimeMillis()); } private void transferZSet(String key, Jedis originJedis, Jedis targetJedis) { @@ -149,15 +160,14 @@ private void transferSet(String key, Jedis originJedis, Jedis targetJedis) { /** * get keys */ - private Set getKeys(RedisPools pool, RedisPoolProperty property, int database) { - Optional jedisOpt = pool.getJedis(property); - if (!jedisOpt.isPresent()) { - return new HashSet<>(0); + private Set getKeys(RedisPools pool, int database) { + final JedisPool jedisPool = pool.getJedisPool(); + try (final Jedis jedis = jedisPool.getResource()) { + if (Objects.isNull(jedis)) { + return Collections.emptySet(); + } + jedis.select(database); + return jedis.keys("*"); } - - Jedis jedis = jedisOpt.get(); - jedis.select(database); - return jedis.keys("*"); } - } diff --git a/concurrency/src/main/java/redis/migration/Readme.md b/concurrency/src/main/java/redis/migration/Readme.md index 45434c2c..4893319c 100644 --- a/concurrency/src/main/java/redis/migration/Readme.md +++ b/concurrency/src/main/java/redis/migration/Readme.md @@ -1,4 +1,3 @@ # Redis 数据迁移 -- [ ] 利用多线程快速迁移 -- [ ] 使用线程池 +- [ ] scan diff --git a/concurrency/src/main/java/redis/migration/RedisPools.java b/concurrency/src/main/java/redis/migration/RedisPools.java index c0737f8a..0ec05437 100644 --- a/concurrency/src/main/java/redis/migration/RedisPools.java +++ b/concurrency/src/main/java/redis/migration/RedisPools.java @@ -2,7 +2,6 @@ import java.util.List; import java.util.Objects; -import java.util.Optional; import lombok.Data; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; @@ -13,8 +12,7 @@ import redis.clients.jedis.JedisPoolConfig; /** - * Created by https://github.com/kuangcp on 17-6-9 上午9:56 - * 一个连接redis的连接池实例, TODO 需要检查性能 + * Created by https://github.com/kuangcp on 17-6-9 上午9:56 一个连接redis的连接池实例, TODO 需要检查性能 */ @Slf4j @Data @@ -55,7 +53,7 @@ protected void initPool() { property.getTimeout()); } } catch (Exception e) { - logger.error("create pool error: {}", e); + logger.error("create pool error", e); } } @@ -69,50 +67,23 @@ protected void initPool(RedisPoolProperty property) { initPool(); } - public Optional getJedis(RedisPoolProperty property) { - initPool(property); - return getJedis(); - } - - /** - * 同步获取Jedis实例 - * - * @return Jedis - * jedis.close直接放在finally里,用完了自动关闭 - */ - public Optional getJedis() { + public JedisPool getJedisPool() { if (jedisPool == null) { logger.info("pool is empty re init"); initPool(); } - if (Objects.isNull(jedisPool)) { - return Optional.empty(); - } - Jedis jedis = null; - try { - jedis = jedisPool.getResource(); - logger.trace("use pool={} get conn={}", jedisPool, jedis); - } catch (Exception e) { - logger.error("pool init error {}", e); - } finally { - if (jedis != null) { - jedis.close(); - } - } - return Optional.ofNullable(jedis); + return jedisPool; } /** * 测试当前连接池是否可用 */ - public boolean isAvailable() { - Optional jedisOpt = getJedis(); - if (jedisOpt.isPresent()) { - Jedis jedis = jedisOpt.get(); - String pingResult = jedis.ping(); - return "pong".equalsIgnoreCase(pingResult); + public static boolean isAvailable(Jedis jedis) { + if (Objects.isNull(jedis)) { + return false; } - return false; + String pingResult = jedis.ping(); + return "pong".equalsIgnoreCase(pingResult); } /** diff --git a/concurrency/src/test/java/redis/migration/MainTest.java b/concurrency/src/test/java/redis/migration/MainTest.java index cce67d5b..4e88368c 100644 --- a/concurrency/src/test/java/redis/migration/MainTest.java +++ b/concurrency/src/test/java/redis/migration/MainTest.java @@ -10,18 +10,18 @@ public class MainTest { @Test - public void testTransferAllKey() { + public void testTransferAllKey() throws InterruptedException { RedisPoolProperty origin = new RedisPoolProperty(); origin.setHost("127.0.0.1"); - origin.setPort(6666); + origin.setPort(6667); origin.setTimeout(100); RedisPoolProperty target = new RedisPoolProperty(); target.setHost("127.0.0.1"); - target.setPort(6666); + target.setPort(6667); target.setTimeout(100); - Main main = new Main(origin, target, 2, 4); + Main main = new Main(origin, target, 4, 2); main.transferAllKey(); } } From 2de1fb60ca9624809848432717bf7d3dcd9c8589 Mon Sep 17 00:00:00 2001 From: kcp Date: Thu, 24 Sep 2020 14:33:50 +0800 Subject: [PATCH 170/476] +) weak reference test --- .../com/github/kuangcp/reference/Apple.java | 25 ++++++++++ .../kuangcp/reference/WeakReferenceTest.java | 48 +++++++++++++++++++ 2 files changed, 73 insertions(+) create mode 100644 class/src/test/java/com/github/kuangcp/reference/Apple.java create mode 100644 class/src/test/java/com/github/kuangcp/reference/WeakReferenceTest.java diff --git a/class/src/test/java/com/github/kuangcp/reference/Apple.java b/class/src/test/java/com/github/kuangcp/reference/Apple.java new file mode 100644 index 00000000..02a980ce --- /dev/null +++ b/class/src/test/java/com/github/kuangcp/reference/Apple.java @@ -0,0 +1,25 @@ +package com.github.kuangcp.reference; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; + +/** + * @author https://github.com/kuangcp on 2020-09-24 12:47 + */ +@Data +@Slf4j +@AllArgsConstructor +class Apple { + + private String name; + + /** + * 覆盖finalize,在回收的时候会执行。 + */ + @Override + protected void finalize() throws Throwable { + super.finalize(); + log.info("Apply: finalize={}", name); + } +} diff --git a/class/src/test/java/com/github/kuangcp/reference/WeakReferenceTest.java b/class/src/test/java/com/github/kuangcp/reference/WeakReferenceTest.java new file mode 100644 index 00000000..6e604b93 --- /dev/null +++ b/class/src/test/java/com/github/kuangcp/reference/WeakReferenceTest.java @@ -0,0 +1,48 @@ +package com.github.kuangcp.reference; + +import java.lang.ref.WeakReference; +import lombok.extern.slf4j.Slf4j; +import org.junit.Assert; +import org.junit.Test; + +/** + * @author https://github.com/kuangcp on 2020-09-24 12:35 + * @see java.lang.ThreadLocal.ThreadLocalMap.Entry + */ +@Slf4j +public class WeakReferenceTest { + + @Test + public void testClear() throws InterruptedException { + WeakReference weakReference = new WeakReference<>(new Apple("红富士")); + //通过WeakReference的get()方法获取Apple + log.info("Apple={}", weakReference.get()); + + System.gc(); + + //休眠一下,在运行的时候加上虚拟机参数-XX:+PrintGCDetails,输出gc信息,确定gc发生了。 + Thread.sleep(2000); + + //如果为空,代表被回收了 + Assert.assertNull(weakReference.get()); + log.info("apple has clear"); + } + + @Test + public void testHold() throws InterruptedException { + Apple apple = new Apple("红富士"); + WeakReference weakReference = new WeakReference<>(apple); + + //通过WeakReference的get()方法获取Apple + log.info("Apple={}", weakReference.get()); + + System.gc(); + + //休眠一下,在运行的时候加上虚拟机参数-XX:+PrintGCDetails,输出gc信息,确定gc发生了。 + Thread.sleep(2000); + + Assert.assertNotNull(weakReference.get()); + log.info("apple hasn't clear"); + } +} + From 99d407525e2dd7ab6c6b4f9ae7683e0cb200b8b0 Mon Sep 17 00:00:00 2001 From: kcp Date: Thu, 24 Sep 2020 14:37:50 +0800 Subject: [PATCH 171/476] *) optimize --- .../com/github/kuangcp/loader/InstantiationSortTest.java | 6 ++---- .../test/java/com/github/kuangcp/read/ClassScannerTest.java | 4 +--- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/class/src/test/java/com/github/kuangcp/loader/InstantiationSortTest.java b/class/src/test/java/com/github/kuangcp/loader/InstantiationSortTest.java index 59fdf550..a28d7231 100644 --- a/class/src/test/java/com/github/kuangcp/loader/InstantiationSortTest.java +++ b/class/src/test/java/com/github/kuangcp/loader/InstantiationSortTest.java @@ -7,8 +7,7 @@ * 类属性和对象属性实例化的顺序 * * 17:21:08.112 INFO [main] c.g.k.i.InstantiationSortTest:21 object init block - * 17:21:08.117 INFO [main] c.g.k.i.InstantiationSortTest:25 constructor - * 17:21:08.118 INFO [main] c.g.k.i.InstantiationSortTest:26 a=1 b=2 + * 17:21:08.117 INFO [main] c.g.k.i.InstantiationSortTest:25 constructor a=1 b=2 * 17:21:08.120 INFO [main] c.g.k.i.InstantiationSortTest:17 static init block * 17:21:08.120 INFO [main] c.g.k.i.InstantiationSortTest:30 static method */ @@ -29,8 +28,7 @@ public class InstantiationSortTest { } InstantiationSortTest() { - log.info("constructor"); - log.info("a={} b={}", a, b); + log.info("constructor a={} b={}", a, b); } static void staticMethod() { diff --git a/class/src/test/java/com/github/kuangcp/read/ClassScannerTest.java b/class/src/test/java/com/github/kuangcp/read/ClassScannerTest.java index 0fce27a3..35a600b0 100644 --- a/class/src/test/java/com/github/kuangcp/read/ClassScannerTest.java +++ b/class/src/test/java/com/github/kuangcp/read/ClassScannerTest.java @@ -1,7 +1,6 @@ package com.github.kuangcp.read; import com.github.kuangcp.io.ResourceTool; -import com.github.kuangcp.time.GetRunTime; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -36,8 +35,7 @@ public void testGetPackageAllClasses() { if (clazz != BaseFather.class && BaseFather.class.isAssignableFrom(clazz)) { // 继承自 BaseFather String rawName = clazz.getSimpleName(); String name = lowerCaseFirstLetter(rawName); - System.out.println(String.format - (" public final static %s %s = new %s();", rawName, name, rawName)); + System.out.printf(" public final static %s %s = new %s();%n", rawName, name, rawName); } } } From 72d4506cf21b067e2e3a1ac80132f19ce62f4cdc Mon Sep 17 00:00:00 2001 From: kcp Date: Thu, 24 Sep 2020 15:09:10 +0800 Subject: [PATCH 172/476] *) format --- .../github/kuangcp/inherit/SameFieldTest.java | 25 +++- ...nstanceofTest.java => InstanceOfTest.java} | 2 +- .../syntax/expression/ShortCircuitTest.java | 10 +- class/src/test/java/time/Readme.md | 124 +++++++++--------- 4 files changed, 88 insertions(+), 73 deletions(-) rename class/src/test/java/syntax/{InstanceofTest.java => InstanceOfTest.java} (97%) diff --git a/class/src/test/java/com/github/kuangcp/inherit/SameFieldTest.java b/class/src/test/java/com/github/kuangcp/inherit/SameFieldTest.java index f04d91b4..487082dc 100644 --- a/class/src/test/java/com/github/kuangcp/inherit/SameFieldTest.java +++ b/class/src/test/java/com/github/kuangcp/inherit/SameFieldTest.java @@ -3,6 +3,8 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.IsEqual.equalTo; +import lombok.extern.slf4j.Slf4j; +import org.hamcrest.core.IsNot; import org.junit.Test; /** @@ -16,14 +18,26 @@ */ public class SameFieldTest { + @Slf4j public static class A { - int age = 1; + int size = 1; + + public int getSize() { + log.info("invoke"); + return size; + } } + @Slf4j public static class B extends A { - int age = 3; + int size = 3; + + public int getSize() { + log.info("invoke"); + return size; + } } @Test @@ -34,7 +48,10 @@ public void testSame() { System.out.println(a.getClass()); System.out.println(b.getClass()); - // TODO ? - assertThat(a.age + b.age, equalTo(4)); + assertThat(a.size + b.size, equalTo(4)); + + assertThat(a.size, IsNot.not(a.getSize())); + + assertThat(a.getSize() + b.getSize(), equalTo(6)); } } diff --git a/class/src/test/java/syntax/InstanceofTest.java b/class/src/test/java/syntax/InstanceOfTest.java similarity index 97% rename from class/src/test/java/syntax/InstanceofTest.java rename to class/src/test/java/syntax/InstanceOfTest.java index 34974eea..c72f24e5 100644 --- a/class/src/test/java/syntax/InstanceofTest.java +++ b/class/src/test/java/syntax/InstanceOfTest.java @@ -10,7 +10,7 @@ * @author https://github.com/Kuangcp * @date 2019-05-10 08:18 */ -public class InstanceofTest { +public class InstanceOfTest { @Test public void test() { diff --git a/class/src/test/java/syntax/expression/ShortCircuitTest.java b/class/src/test/java/syntax/expression/ShortCircuitTest.java index fba4b0ab..004ec5e8 100644 --- a/class/src/test/java/syntax/expression/ShortCircuitTest.java +++ b/class/src/test/java/syntax/expression/ShortCircuitTest.java @@ -23,12 +23,12 @@ private boolean addValue() { @Test public void testAnd() { // first satisfied, invoke second - if (flag == 0 && addValue()) { + if (true && addValue()) { assertThat(flag, equalTo(1)); } // first expression is not satisfied conditions, then second expression not invoke - if (flag == 2 && addValue()) { + if (false && addValue()) { assertThat(flag, equalTo(1)); } } @@ -36,15 +36,13 @@ public void testAnd() { @Test public void testOr() { // first expression is satisfied conditions, then second expression not invoke - if (flag == 0 || addValue()) { + if (true || addValue()) { assertThat(flag, equalTo(0)); } // first not satisfied, invoke second - if (flag == 2 || addValue()) { + if (false || addValue()) { assertThat(flag, equalTo(1)); } } - - // TODO 位运算 } diff --git a/class/src/test/java/time/Readme.md b/class/src/test/java/time/Readme.md index ac66c5ff..f01516f2 100644 --- a/class/src/test/java/time/Readme.md +++ b/class/src/test/java/time/Readme.md @@ -4,71 +4,71 @@ ``` ├── chrono/ -│   ├── AbstractChronology -│   ├── ChronoLocalDate -│   ├── ChronoLocalDateImpl -│   ├── ChronoLocalDateTime -│   ├── ChronoLocalDateTimeImpl -│   ├── Chronology -│   ├── ChronoPeriod -│   ├── ChronoPeriodImpl -│   ├── ChronoZonedDateTime -│   ├── ChronoZonedDateTimeImpl -│   ├── Era -│   ├── HijrahChronology 伊斯兰教日历 -│   ├── HijrahDate -│   ├── HijrahEra -│   ├── IsoChronology -│   ├── IsoEra -│   ├── JapaneseChronology 日本 -│   ├── JapaneseDate -│   ├── JapaneseEra -│   ├── MinguoChronology 民国 -│   ├── MinguoDate -│   ├── MinguoEra -│   ├── Ser -│   ├── ThaiBuddhistChronology 泰国佛教日历 -│   ├── ThaiBuddhistDate -│   └── ThaiBuddhistEra +│ ├── AbstractChronology +│ ├── ChronoLocalDate +│ ├── ChronoLocalDateImpl +│ ├── ChronoLocalDateTime +│ ├── ChronoLocalDateTimeImpl +│ ├── Chronology +│ ├── ChronoPeriod +│ ├── ChronoPeriodImpl +│ ├── ChronoZonedDateTime +│ ├── ChronoZonedDateTimeImpl +│ ├── Era +│ ├── HijrahChronology 伊斯兰教日历 +│ ├── HijrahDate +│ ├── HijrahEra +│ ├── IsoChronology +│ ├── IsoEra +│ ├── JapaneseChronology 日本 +│ ├── JapaneseDate +│ ├── JapaneseEra +│ ├── MinguoChronology 民国 +│ ├── MinguoDate +│ ├── MinguoEra +│ ├── Ser +│ ├── ThaiBuddhistChronology 泰国佛教日历 +│ ├── ThaiBuddhistDate +│ └── ThaiBuddhistEra ├── format/ -│   ├── DateTimeFormatterBuilder -│   ├── DateTimeFormatter -│   ├── DateTimeParseContext -│   ├── DateTimeParseException -│   ├── DateTimePrintContext -│   ├── DateTimeTextProvider -│   ├── DecimalStyle -│   ├── FormatStyle -│   ├── Parsed -│   ├── ResolverStyle -│   ├── SignStyle -│   ├── TextStyle -│   └── ZoneName +│ ├── DateTimeFormatterBuilder +│ ├── DateTimeFormatter +│ ├── DateTimeParseContext +│ ├── DateTimeParseException +│ ├── DateTimePrintContext +│ ├── DateTimeTextProvider +│ ├── DecimalStyle +│ ├── FormatStyle +│ ├── Parsed +│ ├── ResolverStyle +│ ├── SignStyle +│ ├── TextStyle +│ └── ZoneName ├── temporal/ -│   ├── ChronoField -│   ├── ChronoUnit -│   ├── IsoFields -│   ├── JulianFields -│   ├── TemporalAccessor -│   ├── TemporalAdjuster -│   ├── TemporalAdjusters -│   ├── TemporalAmount -│   ├── Temporal -│   ├── TemporalField -│   ├── TemporalQueries -│   ├── TemporalQuery -│   ├── TemporalUnit -│   ├── UnsupportedTemporalTypeException -│   ├── ValueRange -│   └── WeekFields +│ ├── ChronoField +│ ├── ChronoUnit +│ ├── IsoFields +│ ├── JulianFields +│ ├── TemporalAccessor +│ ├── TemporalAdjuster +│ ├── TemporalAdjusters +│ ├── TemporalAmount +│ ├── Temporal +│ ├── TemporalField +│ ├── TemporalQueries +│ ├── TemporalQuery +│ ├── TemporalUnit +│ ├── UnsupportedTemporalTypeException +│ ├── ValueRange +│ └── WeekFields ├── zone/ -│   ├── Ser -│   ├── TzdbZoneRulesProvider -│   ├── ZoneOffsetTransition -│   ├── ZoneOffsetTransitionRule -│   ├── ZoneRules -│   ├── ZoneRulesException -│   └── ZoneRulesProvider +│ ├── Ser +│ ├── TzdbZoneRulesProvider +│ ├── ZoneOffsetTransition +│ ├── ZoneOffsetTransitionRule +│ ├── ZoneRules +│ ├── ZoneRulesException +│ └── ZoneRulesProvider ├── Clock ├── DateTimeException ├── DayOfWeek From d43a97f458862f8ac5f9adf1b46ff98310518465 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Mon, 2 Nov 2020 23:04:39 +0800 Subject: [PATCH 173/476] *) update string --- class/src/test/java/Readme.md | 1 + .../test/java/com/github/kuangcp/Readme.md | 1 + .../test/java/syntax/string/StringTest.java | 86 +++++-------------- 3 files changed, 22 insertions(+), 66 deletions(-) create mode 100644 class/src/test/java/Readme.md create mode 100644 class/src/test/java/com/github/kuangcp/Readme.md diff --git a/class/src/test/java/Readme.md b/class/src/test/java/Readme.md new file mode 100644 index 00000000..fc1bc968 --- /dev/null +++ b/class/src/test/java/Readme.md @@ -0,0 +1 @@ +# Java基础体系 \ No newline at end of file diff --git a/class/src/test/java/com/github/kuangcp/Readme.md b/class/src/test/java/com/github/kuangcp/Readme.md new file mode 100644 index 00000000..2bbd3657 --- /dev/null +++ b/class/src/test/java/com/github/kuangcp/Readme.md @@ -0,0 +1 @@ +# 类和对象 \ No newline at end of file diff --git a/class/src/test/java/syntax/string/StringTest.java b/class/src/test/java/syntax/string/StringTest.java index 6455d33c..322630f7 100644 --- a/class/src/test/java/syntax/string/StringTest.java +++ b/class/src/test/java/syntax/string/StringTest.java @@ -1,11 +1,5 @@ package syntax.string; -import static org.hamcrest.core.IsEqual.equalTo; -import static org.junit.Assert.assertThat; - -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; import lombok.extern.slf4j.Slf4j; import org.junit.Assert; import org.junit.Test; @@ -18,87 +12,47 @@ @Slf4j public class StringTest { - // Forced type conversion - @Test - public void testConvert() { - List list = new ArrayList<>(); - list.add(1); - Object name = list.get(0); - log.debug("name: {}", name); - log.debug("is null : {}", name == null); - log.debug("is String : {}", name instanceof String); - log.debug("is int : {}", name instanceof Integer); - - list.add(null); - list.add(null); - - log.debug("list.size {}", list.size()); - - if (list.get(0) instanceof String) { - String nullToString = (String) list.get(0); - log.debug("string: {}", nullToString); - } else { - log.debug("not String : {}", list.get(0)); - } - } - - @Test - public void testUUID() { - String uuid = UUID.randomUUID().toString(); - log.debug("uuid: ={}", uuid); - } - + /** + * 字符串常量池 和 堆 + */ @Test - public void testConstantPool() { + public void testConstantPoolAndHeap() { assert new String("1") != new String("1"); assert "1" != new String("1"); - assert "11" != new String("1") + new String("1"); - - // 常量池 - assert "1" == "1"; - - // 左边在常量池 右边在堆, 即使 HashCode 一致 + // 左边会被编译器替换为 "11" assert "1" + "1" != new String("1") + new String("1"); - + assert "11" != new String("1") + new String("1"); assert ("1" + "1").hashCode() == (new String("1") + new String("1")).hashCode(); - // 以下三种 右边表达式的结果都是等价的, 都在堆创建了一个新的对象 - String a = new String("1") + new String("1"); -// String a = new String("1" + "1"); -// String a = new String("1" ) + "1"; - - // https://docs.oracle.com/javase/8/docs/api/java/lang/String.html#intern-- - // When the intern method is invoked, - // if the pool already contains a string equal to this String object as determined by the equals(Object) method, then the string from the pool is returned. - // Otherwise, this String object is added to the pool and a reference to this String object is returned. - String b = a.intern(); - String c = "11"; + assert new String("1") + new String("1") != new String("1") + new String("1"); - assert a != b; - assert b == c; - assert a != c; + assert "1" == "1"; } + /** + * https://docs.oracle.com/javase/8/docs/api/java/lang/String.html#intern-- + */ @Test public void testIntern() { - // 如果在 1.6上运行第一个断言就会通不过 - // intern() 会把首次遇到的字符串复制到常量池中, 并返回常量池中该字符串的引用 + // 如果在 1.6上运行第一个断言就会通不过 intern() 会把首次遇到的字符串复制到常量池中, 并返回常量池中该字符串的引用 - // 在 1.7及以上: 不会复制实例只是在常量池中记录首次出现的实例的引用, + // 在 1.7及以上: 不会复制实例,只是在常量池中记录首次出现的实例的引用, // 所以intern() 返回的引用和StringBuilder创建的实例是一样的 - String a = new StringBuilder("Ja").append("va").toString(); - assertThat(a.intern(), equalTo(a)); + String java = new StringBuilder("Ja").append("va").toString(); + assert java.intern() == java; // 这个断言通过是因为, Java字符串已经不是首次出现了, intern() 返回的是常量池里的引用, 和 StringBuilder实例不一致 // https://www.zhihu.com/question/51102308 java字符串也是一致的结果 - String b = new StringBuilder("Ja").append("va").toString(); - Assert.assertFalse(b.intern() == b); + String second = new StringBuilder("Ja").append("va").toString(); + Assert.assertNotSame(second.intern(), second); } + //TODO replace replaceAll replaceFirst + @Test - public void testReplaceSpecialChar(){ + public void testReplaceSpecialChar() { "".replaceAll("[\\ud800\\udc00-\\udbff\\udfff\\ud800-\\udfff]", "*"); } } From cd7ae0837721772003749b538d416e5e446bc523 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Mon, 2 Nov 2020 23:15:48 +0800 Subject: [PATCH 174/476] +) assert --- .../com/github/kuangcp/reflects/ReflectTargetObjectTest.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/class/src/test/java/com/github/kuangcp/reflects/ReflectTargetObjectTest.java b/class/src/test/java/com/github/kuangcp/reflects/ReflectTargetObjectTest.java index 8b314393..411c6971 100644 --- a/class/src/test/java/com/github/kuangcp/reflects/ReflectTargetObjectTest.java +++ b/class/src/test/java/com/github/kuangcp/reflects/ReflectTargetObjectTest.java @@ -244,7 +244,11 @@ public void testModifyStaticFinalBoolean() throws Exception { removeFinalModifier(Boolean.class.getField("FALSE"), true); log.info("Everything is true? {}", false); + assertThat(false, equalTo(true)); + assert Boolean.FALSE == true; + + assert false != true; } /** From 94ff1dee5a0133dca3d88fa8d7531b74e7ec7092 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Mon, 2 Nov 2020 23:23:03 +0800 Subject: [PATCH 175/476] +) simple + --- class/src/test/java/syntax/string/StringTest.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/class/src/test/java/syntax/string/StringTest.java b/class/src/test/java/syntax/string/StringTest.java index 322630f7..95bd5e10 100644 --- a/class/src/test/java/syntax/string/StringTest.java +++ b/class/src/test/java/syntax/string/StringTest.java @@ -49,6 +49,14 @@ public void testIntern() { Assert.assertNotSame(second.intern(), second); } + @Test + public void testConcat() throws Exception { + String foo = "foo"; + // javap -v 查看编译器生成的字节码,能看到这里使用了 StringBuilder 进行替换 + String result = foo + "bar"; + //TODO complete + } + //TODO replace replaceAll replaceFirst @Test From 8acded5f30d642748cb4a38d373c788d31812bd3 Mon Sep 17 00:00:00 2001 From: kcp Date: Wed, 4 Nov 2020 11:04:57 +0800 Subject: [PATCH 176/476] *) merge --- class/src/main/java/jvm/oom/DirectMemoryOOM.java | 15 +++++++++------ .../main/java/jvm/oom/RunTimeConstantPoolOOM.java | 9 +++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/class/src/main/java/jvm/oom/DirectMemoryOOM.java b/class/src/main/java/jvm/oom/DirectMemoryOOM.java index 6db0c16b..e0dd5569 100644 --- a/class/src/main/java/jvm/oom/DirectMemoryOOM.java +++ b/class/src/main/java/jvm/oom/DirectMemoryOOM.java @@ -1,5 +1,8 @@ package jvm.oom; +import java.lang.reflect.Field; +import sun.misc.Unsafe; + /** * -Xmx20M -XX:MaxDirectMemorySize=10M * @@ -13,11 +16,11 @@ public class DirectMemoryOOM { private static final int memoryBlock = 1024 * 1024; public static void main(String[] args) throws IllegalAccessException { -// Field field = Unsafe.class.getDeclaredFields()[0]; -// field.setAccessible(true); -// Unsafe unsafe = (Unsafe) field.get(null); -// while (true) { -// unsafe.allocateMemory(memoryBlock); -// } + Field field = Unsafe.class.getDeclaredFields()[0]; + field.setAccessible(true); + Unsafe unsafe = (Unsafe) field.get(null); + while (true) { + unsafe.allocateMemory(memoryBlock); + } } } diff --git a/class/src/main/java/jvm/oom/RunTimeConstantPoolOOM.java b/class/src/main/java/jvm/oom/RunTimeConstantPoolOOM.java index 5a4344ba..c851e947 100644 --- a/class/src/main/java/jvm/oom/RunTimeConstantPoolOOM.java +++ b/class/src/main/java/jvm/oom/RunTimeConstantPoolOOM.java @@ -1,9 +1,6 @@ package jvm.oom; -import java.util.ArrayList; -import java.util.List; import java.util.Optional; -import java.util.concurrent.TimeUnit; import java.util.stream.IntStream; /** @@ -20,7 +17,6 @@ * @author kuangcp on 4/4/19-12:20 AM */ public class RunTimeConstantPoolOOM { - private static List data = new ArrayList<>(); public static void main(String[] args) throws InterruptedException { int i = 0; @@ -32,12 +28,13 @@ public static void main(String[] args) throws InterruptedException { while (true) { // 只要出现了显式的 + 字符串拼接, 就会创建新的字符串, intern() 方法调用与否结果都是一样的 // data.add((result.get() + i++).intern()); - data.add((result.get() + i++)); +// data.add((result.get() + i++)); + result.get().intern(); // 如果不加, 瞬间 Old Gen 爆满然后 OOM : heap space // 如果加上, 就会发现 Eden 一直涨, 然后被回收掉, 并且部分转移到了 Old // 还能通过 visualvm 查看 内存的慢慢涨上去, 直到满了后 OOM, 内存被一下子释放掉 - TimeUnit.MILLISECONDS.sleep(20); +// TimeUnit.MILLISECONDS.sleep(5); } } } From 5ed38bd668f609426fc096a43bf5fa8ce142db6c Mon Sep 17 00:00:00 2001 From: kcp Date: Tue, 24 Nov 2020 15:29:04 +0800 Subject: [PATCH 177/476] +) test native memory --- class/build.gradle | 3 -- .../kuangcp/reflects/ReflectTargetObject.java | 2 +- .../main/java/jvm/oom/DirectMemoryOOM.java | 33 ++++++++++++++++--- .../java/jvm/oom/DirectMemoryOOMTest.java | 19 +++++++++++ 4 files changed, 49 insertions(+), 8 deletions(-) create mode 100644 class/src/test/java/jvm/oom/DirectMemoryOOMTest.java diff --git a/class/build.gradle b/class/build.gradle index 83ebfa23..b6cfd307 100644 --- a/class/build.gradle +++ b/class/build.gradle @@ -1,6 +1,3 @@ -sourceCompatibility = 1.8 -targetCompatibility = 1.8 - dependencies { implementation libs['fastjson'] implementation libs['gson'] diff --git a/class/src/main/java/com/github/kuangcp/reflects/ReflectTargetObject.java b/class/src/main/java/com/github/kuangcp/reflects/ReflectTargetObject.java index 59a1bb71..524c0908 100644 --- a/class/src/main/java/com/github/kuangcp/reflects/ReflectTargetObject.java +++ b/class/src/main/java/com/github/kuangcp/reflects/ReflectTargetObject.java @@ -12,7 +12,7 @@ @Data @AllArgsConstructor @NoArgsConstructor -class ReflectTargetObject { +public class ReflectTargetObject { public static final String staticFinalString = "staticFinal"; public static final Integer staticFinalInteger = 1; diff --git a/class/src/main/java/jvm/oom/DirectMemoryOOM.java b/class/src/main/java/jvm/oom/DirectMemoryOOM.java index e0dd5569..cb515c5a 100644 --- a/class/src/main/java/jvm/oom/DirectMemoryOOM.java +++ b/class/src/main/java/jvm/oom/DirectMemoryOOM.java @@ -1,26 +1,51 @@ package jvm.oom; import java.lang.reflect.Field; +import java.nio.ByteBuffer; import sun.misc.Unsafe; /** - * -Xmx20M -XX:MaxDirectMemorySize=10M + * -XX:NativeMemoryTracking=detail + * + * 查看内存分布jcmd pid VM.native_memory detail * - * TODO 瞬间内存占满 似乎该参数在8中失效了 * TODO Java11 sun包成为内置包, 需要找到替代方式获取直接内存 * + * https://www.jianshu.com/p/2d8bc4d4c181 + * + * https://tech.meituan.com/2019/01/03/spring-boot-native-memory-leak.html + * * @author kuangcp on 4/4/19-12:29 AM */ public class DirectMemoryOOM { - private static final int memoryBlock = 1024 * 1024; + private static final long memoryBlock = 1024 * 1024L; - public static void main(String[] args) throws IllegalAccessException { + // -XX:MaxDirectMemorySize参数只对由DirectByteBuffer分配的内存有效,对Unsafe直接分配的内存无效 + + /** + * TODO 为什么 这里分配的是虚拟内存 + */ + static void byUnsafe() throws IllegalAccessException, InterruptedException { Field field = Unsafe.class.getDeclaredFields()[0]; field.setAccessible(true); + Unsafe unsafe = (Unsafe) field.get(null); while (true) { + Thread.sleep(50); unsafe.allocateMemory(memoryBlock); } } + + /** + * TODO 为什么会进行回收 -XX:MaxDirectMemorySize + */ + static void byBuffer() throws InterruptedException { + while (true) { + Thread.sleep(10); + ByteBuffer buf = ByteBuffer.allocateDirect(1024 * 1024); + buf.putLong(2L); + buf.flip(); + } + } } diff --git a/class/src/test/java/jvm/oom/DirectMemoryOOMTest.java b/class/src/test/java/jvm/oom/DirectMemoryOOMTest.java new file mode 100644 index 00000000..0d5fe3c4 --- /dev/null +++ b/class/src/test/java/jvm/oom/DirectMemoryOOMTest.java @@ -0,0 +1,19 @@ +package jvm.oom; + +import org.junit.Test; + +/** + * @author https://github.com/kuangcp on 2020-11-24 14:45 + */ +public class DirectMemoryOOMTest { + + @Test + public void testUnsafe() throws InterruptedException, IllegalAccessException { + DirectMemoryOOM.byUnsafe(); + } + + @Test + public void testBuffer() throws InterruptedException { + DirectMemoryOOM.byBuffer(); + } +} \ No newline at end of file From 4745e2ec02be2b6b5d8a0f207480088db046eec4 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Wed, 7 Apr 2021 05:08:20 +0800 Subject: [PATCH 178/476] +) AES --- .../kuangcp/bloom/filter/BloomFilter.java | 4 +- .../organization/structure/NodeMgr.java | 2 +- .../dynamicprogramming/ClimbStairs.java | 14 +- class/src/main/java/security/AES.java | 147 ++++++++++++++++++ class/src/test/java/security/AESTest.java | 69 ++++++++ 5 files changed, 227 insertions(+), 9 deletions(-) create mode 100644 class/src/main/java/security/AES.java create mode 100644 class/src/test/java/security/AESTest.java diff --git a/algorithms/src/main/java/com/github/kuangcp/bloom/filter/BloomFilter.java b/algorithms/src/main/java/com/github/kuangcp/bloom/filter/BloomFilter.java index a2eac920..10407445 100644 --- a/algorithms/src/main/java/com/github/kuangcp/bloom/filter/BloomFilter.java +++ b/algorithms/src/main/java/com/github/kuangcp/bloom/filter/BloomFilter.java @@ -14,10 +14,10 @@ @Slf4j public class BloomFilter { - private static List> functions = new ArrayList<>(); + private static final List> functions = new ArrayList<>(); private static final short LEN_OF_BYTE = 7; - private byte[] cache = new byte[2 << 28]; + private final byte[] cache = new byte[2 << 28]; static { functions.add(HashFunctions.hashByObjects); diff --git a/algorithms/src/main/java/com/github/kuangcp/organization/structure/NodeMgr.java b/algorithms/src/main/java/com/github/kuangcp/organization/structure/NodeMgr.java index 73196d72..45fa657e 100644 --- a/algorithms/src/main/java/com/github/kuangcp/organization/structure/NodeMgr.java +++ b/algorithms/src/main/java/com/github/kuangcp/organization/structure/NodeMgr.java @@ -22,7 +22,7 @@ public class NodeMgr implements NodeAction { /** * 存放任意多棵树 */ - private Map nodes = new ConcurrentHashMap<>(); + private final Map nodes = new ConcurrentHashMap<>(); // TODO 列出整个树 public String tree(String parentId) { diff --git a/algorithms/src/test/java/com/github/kuangcp/dynamicprogramming/ClimbStairs.java b/algorithms/src/test/java/com/github/kuangcp/dynamicprogramming/ClimbStairs.java index e248d395..18651ea7 100644 --- a/algorithms/src/test/java/com/github/kuangcp/dynamicprogramming/ClimbStairs.java +++ b/algorithms/src/test/java/com/github/kuangcp/dynamicprogramming/ClimbStairs.java @@ -21,7 +21,7 @@ public class ClimbStairs { /** * 总台阶数 */ - private static final int totalStep = 3; + private static final int totalStep = 5; private static int count = 0; @@ -38,21 +38,23 @@ public void testRecursion() { @Test public void testDP() { int result = dp(totalStep); - log.info(": result={}", result); + log.info("result={}", result); } @Test public void testDPOptimize() { int[] list = new int[totalStep + 1]; - int result = dp2(totalStep, list); - log.info(": result={}", result); + int result = dpWithCache(totalStep, list); + log.info("result={}", result); } private void recursionStep(int step, int n) { + log.info("step:{} n={}", step, n ); if (step > n) { return; } if (step == n) { + log.info("add"); count++; } recursionStep(step + 1, n); @@ -70,12 +72,12 @@ private int dp(int n) { /** * use cache */ - private int dp2(int n, int[] list) { + private int dpWithCache(int n, int[] list) { if (n <= 2) { return n; } if (0 == list[n]) { - list[n] = dp2(n - 1, list) + dp2(n - 2, list); + list[n] = dpWithCache(n - 1, list) + dpWithCache(n - 2, list); } return list[n]; diff --git a/class/src/main/java/security/AES.java b/class/src/main/java/security/AES.java new file mode 100644 index 00000000..3487ead2 --- /dev/null +++ b/class/src/main/java/security/AES.java @@ -0,0 +1,147 @@ +package security; + +import java.math.BigInteger; +import java.nio.charset.StandardCharsets; +import java.util.Base64; +import javax.crypto.Cipher; +import javax.crypto.KeyGenerator; +import javax.crypto.spec.SecretKeySpec; +import org.apache.commons.lang3.StringUtils; +import sun.misc.BASE64Decoder; + +public class AES { + + //密钥 (需要前端和后端保持一致) + private static final String KEY = "qwertyuiqwertyui"; + //算法 + private static final String ALGORITHMSTR = "AES/CTR/PKCS5Padding"; + + /** + * aes解密 + * + * @param encrypt 内容 + */ + public static String aesDecrypt(String encrypt) { + try { + return aesDecrypt(encrypt, KEY); + } catch (Exception e) { + e.printStackTrace(); + return ""; + } + } + + /** + * aes加密 + */ + public static String aesEncrypt(String content) { + try { + return aesEncrypt(content, KEY); + } catch (Exception e) { + e.printStackTrace(); + return ""; + } + } + + /** + * 将byte[]转为各种进制的字符串 + * + * @param bytes byte[] + * @param radix 可以转换进制的范围,从Character.MIN_RADIX到Character.MAX_RADIX,超出范围后变为10进制 + * @return 转换后的字符串 + */ + public static String binary(byte[] bytes, int radix) { + return new BigInteger(1, bytes).toString(radix);// 这里的1代表正数 + } + + /** + * base 64 encode + * + * @param bytes 待编码的byte[] + * @return 编码后的base 64 code + */ + public static String base64Encode(byte[] bytes) { + return Base64.getEncoder().encodeToString(bytes); + } + + /** + * base 64 decode + * + * @param base64Code 待解码的base 64 code + * @return 解码后的byte[] + */ + public static byte[] base64Decode(String base64Code) throws Exception { + return StringUtils.isEmpty(base64Code) ? null : new BASE64Decoder().decodeBuffer(base64Code); + } + + + /** + * AES加密 + * + * @param content 待加密的内容 + * @param encryptKey 加密密钥 + * @return 加密后的byte[] + */ + public static byte[] aesEncryptToBytes(String content, String encryptKey) throws Exception { + KeyGenerator kgen = KeyGenerator.getInstance("AES"); + kgen.init(128); + Cipher cipher = Cipher.getInstance(ALGORITHMSTR); + cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(encryptKey.getBytes(), "AES")); + + return cipher.doFinal(content.getBytes(StandardCharsets.UTF_8)); + } + + + /** + * AES加密为base 64 code + * + * @param content 待加密的内容 + * @param encryptKey 加密密钥 + * @return 加密后的base 64 code + */ + public static String aesEncrypt(String content, String encryptKey) throws Exception { + return base64Encode(aesEncryptToBytes(content, encryptKey)); + } + + /** + * AES解密 + * + * @param encryptBytes 待解密的byte[] + * @param decryptKey 解密密钥 + * @return 解密后的String + */ + public static String aesDecryptByBytes(byte[] encryptBytes, String decryptKey) throws Exception { + KeyGenerator kgen = KeyGenerator.getInstance("AES"); + kgen.init(128); + + Cipher cipher = Cipher.getInstance(ALGORITHMSTR); + cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(decryptKey.getBytes(), "AES")); + byte[] decryptBytes = cipher.doFinal(encryptBytes); + return new String(decryptBytes); + } + + + /** + * 将base 64 code AES解密 + * + * @param encryptStr 待解密的base 64 code + * @param decryptKey 解密密钥 + * @return 解密后的string + */ + public static String aesDecrypt(String encryptStr, String decryptKey) throws Exception { + return StringUtils.isEmpty(encryptStr) ? null + : aesDecryptByBytes(base64Decode(encryptStr), decryptKey); + } + + /** + * 测试 + */ + public static void main(String[] args) throws Exception { + String content = "123"; + System.out.println("加密前:" + content); + System.out.println("加密密钥和解密密钥:" + KEY); + String encrypt = aesEncrypt(content, KEY); + System.out.println("加密后:" + encrypt); + String decrypt = aesDecrypt(encrypt, KEY); + System.out.println("解密后:" + decrypt); + } +} \ No newline at end of file diff --git a/class/src/test/java/security/AESTest.java b/class/src/test/java/security/AESTest.java new file mode 100644 index 00000000..565f4a0f --- /dev/null +++ b/class/src/test/java/security/AESTest.java @@ -0,0 +1,69 @@ +//package security; +// +//import static org.hamcrest.Matchers.equalTo; +//import static org.junit.Assert.assertThat; +// +//import java.nio.charset.StandardCharsets; +//import java.security.Key; +//import java.security.Provider; +//import java.security.Security; +//import javax.crypto.Cipher; +//import javax.crypto.spec.IvParameterSpec; +//import javax.crypto.spec.SecretKeySpec; +//import org.apache.commons.lang3.StringUtils; +//import org.apache.kafka.common.utils.ByteUtils; +//import org.junit.Test; +// +///** +// * @author https://github.com/kuangcp on 2020-11-01 21:35 +// */ +//public class AESTest { +// +// @Test +// public void testEncrypt() throws Exception { +// final Provider[] providers = Security.getProviders(); +// for (Provider provider : providers) { +// System.out.println(provider); +// } +// String key = "qwertyuiqwertyuo"; +// final String text = "123456"; +// String result = AES.aesEncrypt(text, key); +// System.out.println(result); +// String origin = AES.aesDecrypt(result, "qwertyuiqwertoki"); +// assertThat(origin, equalTo(text)); +// } +// +// /** +// * DES解密:CTR操作模式 * * @param cipherText:密文 * @param key:密钥 * @param iv:初始计数器 * +// * +// * @return 原文 +// */ +// public static String decryptCTR(String cipherText, String key, String counter) { +// try { +// // 获取解密密钥 +// byte[] keyBytes = getKey(key); +// Key keySpec = new SecretKeySpec(keyBytes, ALGORITHM); +// // 获取初始矢量 +// byte[] ivBytes = counter.getBytes(StandardCharsets.UTF_8); +// IvParameterSpec ivSpec = new IvParameterSpec(ivBytes); +// // 获取Cipher实例 +// Cipher cipher = Cipher.getInstance("DES/CTR/PKCS5Padding"); +// // 初始化Cipher实例,设置执行模式,解密密钥和初始计数器 +// cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec); +// // 解密 +// +// byte[] cipherTextBytes = hex2byte(cipherText); +// byte[] plainTextBytes = cipher.doFinal(cipherTextBytes); +// // 返回明文 +// return new String(plainTextBytes, StandardCharsets.UTF_8); +// } catch (Exception e) { +// System.out.println("CTR解密异常"); +// e.printStackTrace(); +// } +// return null; +// } +// +// @Test +// public void testdd() throws Exception { +// +// } \ No newline at end of file From a38ae6313fec8813b14a82384e251b60d9089ee5 Mon Sep 17 00:00:00 2001 From: kcp Date: Thu, 29 Apr 2021 16:26:27 +0800 Subject: [PATCH 179/476] +) wait and while --- .../test/java/thread/NotifyAndWaitTest.java | 148 +++++++++++++++++- 1 file changed, 147 insertions(+), 1 deletion(-) diff --git a/concurrency/src/test/java/thread/NotifyAndWaitTest.java b/concurrency/src/test/java/thread/NotifyAndWaitTest.java index 260f0ede..f8219a8d 100644 --- a/concurrency/src/test/java/thread/NotifyAndWaitTest.java +++ b/concurrency/src/test/java/thread/NotifyAndWaitTest.java @@ -1,6 +1,8 @@ package thread; +import java.util.Collections; import java.util.HashSet; +import java.util.LinkedList; import java.util.List; import java.util.Optional; import java.util.Set; @@ -74,7 +76,6 @@ public void testMultiLoop() throws InterruptedException { }); } - for (NotifyAndWait ele : set) { pool.submit(() -> { try { @@ -88,4 +89,149 @@ public void testMultiLoop() throws InterruptedException { Thread.sleep(300000); } + + /** + * https://www.cnblogs.com/x_wukong/p/4009709.html + */ + static class Printer implements Runnable { + + private final String name; + private final Object prev; + private final Object self; + + private Printer(String name, Object prev, Object self) { + this.name = name; + this.prev = prev; + this.self = self; + } + + @Override + public void run() { + int count = 10; + while (count > 0) { + synchronized (prev) { + synchronized (self) { + System.out.print(name); + count--; + + self.notify(); + } + try { + prev.wait(); + } catch (InterruptedException e) { + log.error("", e); + } + } + } + } + } + + + @Test + public void testSortedPrint() { + Object aLock = new Object(); + Object bLock = new Object(); + Object cLock = new Object(); + + Printer pa = new Printer("A", cLock, aLock); + Printer pb = new Printer("B", aLock, bLock); + Printer pc = new Printer("C", bLock, cLock); + + new Thread(pa).start(); + new Thread(pb).start(); + new Thread(pc).start(); + } + + /** + * TODO + * + * https://www.zhihu.com/question/439926072 + * https://blog.csdn.net/qq_35181209/article/details/77362297 + */ + static class SpuriousWakeup extends Object { + + private final List list; + + public SpuriousWakeup() { + list = Collections.synchronizedList(new LinkedList<>()); + } + + public String removeItem() throws InterruptedException { + print("in removeItem() - entering"); + + synchronized (list) { + if (list.isEmpty()) { //这里用if语句会发生危险 + print("in removeItem() - about to wait()"); + list.wait(); + print("in removeItem() - done with wait()"); + } + + //删除元素 + String item = list.remove(0); + + print("in removeItem() - leaving"); + return item; + } + } + + public void addItem(String item) { + print("in addItem() - entering"); + synchronized (list) { + //添加元素 + list.add(item); + print("in addItem() - just added: '" + item + "'"); + + //添加后,通知所有线程 + list.notifyAll(); + print("in addItem() - just notified"); + } + print("in addItem() - leaving"); + } + } + + private static void print(String msg) { + String name = Thread.currentThread().getName(); + System.out.println(name + ": " + msg); + } + + @Test + public void test() { + final SpuriousWakeup en = new SpuriousWakeup(); + + Runnable runA = () -> { + try { + String item = en.removeItem(); + print("in run() - returned: '" + item + "'"); + } catch (InterruptedException ix) { + print("interrupted!"); + } catch (Exception x) { + print("threw an Exception!!!\n" + x); + } + }; + + Runnable runB = () -> en.addItem("Hello!"); + try { + //启动第一个删除元素的线程 + Thread threadA1 = new Thread(runA, "threadA1"); + threadA1.start(); + + Thread.sleep(500); + + //启动第二个删除元素的线程 + Thread threadA2 = new Thread(runA, "threadA2"); + threadA2.start(); + + Thread.sleep(500); + //启动增加元素的线程 + Thread threadB = new Thread(runB, "threadB"); + threadB.start(); + + Thread.sleep(10000); // wait 10 seconds + + threadA1.interrupt(); + threadA2.interrupt(); + } catch (InterruptedException e) { + log.error("", e); + } + } } From a87926427be72efe6c6bd2d8bed5e9fdc71a377f Mon Sep 17 00:00:00 2001 From: kcp Date: Fri, 30 Apr 2021 14:00:47 +0800 Subject: [PATCH 180/476] +) wait and while --- .../{ => waitnotify}/NotifyAndWait.java | 2 +- .../java/thread/waitnotify/SortedPrint.java | 40 +++++ .../thread/waitnotify/SpuriousWakeup.java | 86 ++++++++++ .../test/java/thread/NotifyAndWaitTest.java | 155 ++++-------------- 4 files changed, 159 insertions(+), 124 deletions(-) rename concurrency/src/main/java/thread/{ => waitnotify}/NotifyAndWait.java (97%) create mode 100644 concurrency/src/main/java/thread/waitnotify/SortedPrint.java create mode 100644 concurrency/src/main/java/thread/waitnotify/SpuriousWakeup.java diff --git a/concurrency/src/main/java/thread/NotifyAndWait.java b/concurrency/src/main/java/thread/waitnotify/NotifyAndWait.java similarity index 97% rename from concurrency/src/main/java/thread/NotifyAndWait.java rename to concurrency/src/main/java/thread/waitnotify/NotifyAndWait.java index ffb1946d..1b5bbbc8 100644 --- a/concurrency/src/main/java/thread/NotifyAndWait.java +++ b/concurrency/src/main/java/thread/waitnotify/NotifyAndWait.java @@ -1,4 +1,4 @@ -package thread; +package thread.waitnotify; import java.util.Optional; import java.util.UUID; diff --git a/concurrency/src/main/java/thread/waitnotify/SortedPrint.java b/concurrency/src/main/java/thread/waitnotify/SortedPrint.java new file mode 100644 index 00000000..50f7827d --- /dev/null +++ b/concurrency/src/main/java/thread/waitnotify/SortedPrint.java @@ -0,0 +1,40 @@ +package thread.waitnotify; + +import lombok.extern.slf4j.Slf4j; + +/** + * https://www.cnblogs.com/x_wukong/p/4009709.html + */ +@Slf4j +public class SortedPrint implements Runnable { + + private final String name; + private final Object prev; + private final Object self; + + @Override + public void run() { + int count = 10; + while (count > 0) { + synchronized (prev) { + synchronized (self) { + System.out.print(name); + count--; + + self.notify(); + } + try { + prev.wait(); + } catch (InterruptedException e) { + log.error("", e); + } + } + } + } + + public SortedPrint(String name, Object prev, Object self) { + this.name = name; + this.prev = prev; + this.self = self; + } +} \ No newline at end of file diff --git a/concurrency/src/main/java/thread/waitnotify/SpuriousWakeup.java b/concurrency/src/main/java/thread/waitnotify/SpuriousWakeup.java new file mode 100644 index 00000000..178e956e --- /dev/null +++ b/concurrency/src/main/java/thread/waitnotify/SpuriousWakeup.java @@ -0,0 +1,86 @@ +package thread.waitnotify; + +/** + * @author kuangcp + */ + +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import lombok.extern.slf4j.Slf4j; + +/** + * https://www.zhihu.com/question/439926072 + * https://blog.csdn.net/qq_35181209/article/details/77362297 + */ +@Slf4j +public class SpuriousWakeup { + + private final List list; + + public SpuriousWakeup() { + list = Collections.synchronizedList(new LinkedList<>()); + } + + public String removeItemWithWhile() { + try { + log.info("in removeItem() - entering"); + + synchronized (list) { + // 当不满足业务判断,等待满足条件,使用if只会判断一次, 应该使用while确保 wait() 方法后的代码满足 业务判断 + while (list.isEmpty()) { + log.info("in removeItem() - about to wait()"); + list.wait(); + log.info("in removeItem() - done with wait() {}", list.isEmpty()); + } + + //删除元素 + String item = list.remove(0); + + log.info("in removeItem() - leaving"); + return item; + } + } catch (Exception e) { + log.error("", e); + } + return null; + } + + public String removeItem() { + try { + log.info("in removeItem() - entering"); + + synchronized (list) { + // 当不满足业务判断,等待满足条件,使用if只会判断一次, 应该使用while确保 wait() 方法后的代码满足 业务判断 + if (list.isEmpty()) { + log.info("in removeItem() - about to wait()"); + list.wait(); + log.info("in removeItem() - done with wait()"); + } + + //删除元素 + String item = list.remove(0); + + log.info("in removeItem() - leaving"); + return item; + } + } catch (Exception e) { + log.error("", e); + } + return null; + } + + public void addItem(String item) { + log.info("in addItem() - entering"); + synchronized (list) { + //添加元素 + list.add(item); + log.info("in addItem() - just added: '" + item + "'"); + + //添加后,通知所有线程 + list.notifyAll(); + log.info("in addItem() - just notified"); + } + log.info("in addItem() - leaving"); + } +} \ No newline at end of file diff --git a/concurrency/src/test/java/thread/NotifyAndWaitTest.java b/concurrency/src/test/java/thread/NotifyAndWaitTest.java index f8219a8d..2dd07d1c 100644 --- a/concurrency/src/test/java/thread/NotifyAndWaitTest.java +++ b/concurrency/src/test/java/thread/NotifyAndWaitTest.java @@ -1,17 +1,19 @@ package thread; -import java.util.Collections; import java.util.HashSet; -import java.util.LinkedList; import java.util.List; import java.util.Optional; import java.util.Set; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.stream.IntStream; import lombok.extern.slf4j.Slf4j; import org.junit.Test; +import thread.waitnotify.NotifyAndWait; +import thread.waitnotify.SortedPrint; +import thread.waitnotify.SpuriousWakeup; /** * @author https://github.com/kuangcp on 2020-05-09 11:01 @@ -90,148 +92,55 @@ public void testMultiLoop() throws InterruptedException { Thread.sleep(300000); } - /** - * https://www.cnblogs.com/x_wukong/p/4009709.html - */ - static class Printer implements Runnable { - - private final String name; - private final Object prev; - private final Object self; - - private Printer(String name, Object prev, Object self) { - this.name = name; - this.prev = prev; - this.self = self; - } - - @Override - public void run() { - int count = 10; - while (count > 0) { - synchronized (prev) { - synchronized (self) { - System.out.print(name); - count--; - - self.notify(); - } - try { - prev.wait(); - } catch (InterruptedException e) { - log.error("", e); - } - } - } - } - } - - @Test public void testSortedPrint() { Object aLock = new Object(); Object bLock = new Object(); Object cLock = new Object(); - Printer pa = new Printer("A", cLock, aLock); - Printer pb = new Printer("B", aLock, bLock); - Printer pc = new Printer("C", bLock, cLock); + SortedPrint pa = new SortedPrint("A", cLock, aLock); + SortedPrint pb = new SortedPrint("B", aLock, bLock); + SortedPrint pc = new SortedPrint("C", bLock, cLock); new Thread(pa).start(); new Thread(pb).start(); new Thread(pc).start(); } - /** - * TODO - * - * https://www.zhihu.com/question/439926072 - * https://blog.csdn.net/qq_35181209/article/details/77362297 - */ - static class SpuriousWakeup extends Object { - - private final List list; - - public SpuriousWakeup() { - list = Collections.synchronizedList(new LinkedList<>()); - } - - public String removeItem() throws InterruptedException { - print("in removeItem() - entering"); + private void testLogic(Supplier rmFunc, Runnable addFunc) throws InterruptedException { + Runnable remove = () -> { + String item = rmFunc.get(); + log.info("in run() - returned: '" + item + "'"); + }; - synchronized (list) { - if (list.isEmpty()) { //这里用if语句会发生危险 - print("in removeItem() - about to wait()"); - list.wait(); - print("in removeItem() - done with wait()"); - } + //启动第一个删除元素的线程 + Thread threadA1 = new Thread(remove, "threadA1"); + threadA1.start(); + Thread.sleep(500); - //删除元素 - String item = list.remove(0); + //启动第二个删除元素的线程 + Thread threadA2 = new Thread(remove, "threadA2"); + threadA2.start(); + Thread.sleep(500); - print("in removeItem() - leaving"); - return item; - } - } + System.out.println(); - public void addItem(String item) { - print("in addItem() - entering"); - synchronized (list) { - //添加元素 - list.add(item); - print("in addItem() - just added: '" + item + "'"); - - //添加后,通知所有线程 - list.notifyAll(); - print("in addItem() - just notified"); - } - print("in addItem() - leaving"); - } + //启动增加元素的线程 + Thread threadB = new Thread(addFunc, "threadB"); + threadB.start(); + Thread.sleep(4000); + log.info("complete sleep"); } - private static void print(String msg) { - String name = Thread.currentThread().getName(); - System.out.println(name + ": " + msg); + @Test + public void test() throws InterruptedException { + final SpuriousWakeup en = new SpuriousWakeup(); + testLogic(en::removeItem, () -> en.addItem("Hello!")); } @Test - public void test() { + public void testWithWhile() throws InterruptedException { final SpuriousWakeup en = new SpuriousWakeup(); - - Runnable runA = () -> { - try { - String item = en.removeItem(); - print("in run() - returned: '" + item + "'"); - } catch (InterruptedException ix) { - print("interrupted!"); - } catch (Exception x) { - print("threw an Exception!!!\n" + x); - } - }; - - Runnable runB = () -> en.addItem("Hello!"); - try { - //启动第一个删除元素的线程 - Thread threadA1 = new Thread(runA, "threadA1"); - threadA1.start(); - - Thread.sleep(500); - - //启动第二个删除元素的线程 - Thread threadA2 = new Thread(runA, "threadA2"); - threadA2.start(); - - Thread.sleep(500); - //启动增加元素的线程 - Thread threadB = new Thread(runB, "threadB"); - threadB.start(); - - Thread.sleep(10000); // wait 10 seconds - - threadA1.interrupt(); - threadA2.interrupt(); - } catch (InterruptedException e) { - log.error("", e); - } + testLogic(en::removeItemWithWhile, () -> en.addItem("Hello!")); } } From 5f4ee40ff563c5de4278bf19a48c86f775676649 Mon Sep 17 00:00:00 2001 From: kcp Date: Fri, 30 Apr 2021 14:18:39 +0800 Subject: [PATCH 181/476] x) avoid circule dep --- algorithms/build.gradle | 1 + build.gradle | 2 -- class/build.gradle | 2 ++ collection/build.gradle | 1 + common-config/build.gradle | 5 ----- concurrency/build.gradle | 1 + flink/build.gradle | 1 + generic/build.gradle | 1 + guava/build.gradle | 1 + hadoop/build.gradle | 1 + io/build.gradle | 1 + java8/build.gradle | 1 + kafka/build.gradle | 1 + mybatis/build.gradle | 4 +--- network/build.gradle | 1 + pattern/build.gradle | 1 + question/build.gradle | 1 + spring/build.gradle | 1 + test/build.gradle | 3 +++ 19 files changed, 20 insertions(+), 10 deletions(-) diff --git a/algorithms/build.gradle b/algorithms/build.gradle index 6652e1a3..467b4ba2 100644 --- a/algorithms/build.gradle +++ b/algorithms/build.gradle @@ -1,4 +1,5 @@ dependencies { implementation(group: 'com.github.jsonzou', name: 'jmockdata', version: '4.1.2') implementation(libs['common-codec']) + implementation project(":common-config") } \ No newline at end of file diff --git a/build.gradle b/build.gradle index be70d4e1..ce4fdfc8 100644 --- a/build.gradle +++ b/build.gradle @@ -43,7 +43,6 @@ subprojects { // All sub-modules add the following dependencies dependencies { - annotationProcessor libs['lombok'] compileOnly libs['lombok'] testAnnotationProcessor libs['lombok'] @@ -55,6 +54,5 @@ subprojects { testImplementation libs['junit'] testImplementation libs['hamcrest-core'] testImplementation libs['hamcrest-lib'] - implementation project(":common-config") } } diff --git a/class/build.gradle b/class/build.gradle index b6cfd307..bbd0f798 100644 --- a/class/build.gradle +++ b/class/build.gradle @@ -1,4 +1,6 @@ dependencies { + implementation project(":common-config") + implementation libs['fastjson'] implementation libs['gson'] implementation libs['jackson_core'] diff --git a/collection/build.gradle b/collection/build.gradle index 4a3eac68..3eecfa65 100644 --- a/collection/build.gradle +++ b/collection/build.gradle @@ -2,6 +2,7 @@ plugins { id 'java' } dependencies { + implementation project(":common-config") implementation libs['kcp-tool'] // implementation libs['testng'] } diff --git a/common-config/build.gradle b/common-config/build.gradle index cdd97ca6..1e83699c 100644 --- a/common-config/build.gradle +++ b/common-config/build.gradle @@ -1,6 +1 @@ -configurations { - // void dependency loop - compile.exclude module: 'common-config' -} - version = '1.0.0' \ No newline at end of file diff --git a/concurrency/build.gradle b/concurrency/build.gradle index e2d123d6..d0390ff5 100644 --- a/concurrency/build.gradle +++ b/concurrency/build.gradle @@ -4,6 +4,7 @@ plugins { } dependencies { + implementation project(":common-config") implementation libs['jedis'] implementation libs['groovy'] implementation libs['common-lang'] diff --git a/flink/build.gradle b/flink/build.gradle index b09cd8ac..114821ab 100644 --- a/flink/build.gradle +++ b/flink/build.gradle @@ -4,6 +4,7 @@ apply plugin: 'java' apply plugin: 'application' dependencies { + implementation project(":common-config") implementation libs['flink-java'] implementation libs['flink-clients'] implementation libs['flink-streaming-java'] diff --git a/generic/build.gradle b/generic/build.gradle index dcfdff37..c9a5e53a 100644 --- a/generic/build.gradle +++ b/generic/build.gradle @@ -1,3 +1,4 @@ dependencies{ + implementation project(":common-config") implementation rootProject.libs['common-math'] } \ No newline at end of file diff --git a/guava/build.gradle b/guava/build.gradle index 087dbcb9..f8e03238 100644 --- a/guava/build.gradle +++ b/guava/build.gradle @@ -1,3 +1,4 @@ dependencies { + implementation project(":common-config") implementation libs['guava'] } \ No newline at end of file diff --git a/hadoop/build.gradle b/hadoop/build.gradle index 955047b9..b20508c7 100644 --- a/hadoop/build.gradle +++ b/hadoop/build.gradle @@ -1,4 +1,5 @@ dependencies { + implementation project(":common-config") implementation (libs["hadoop-common"]) { exclude group: 'org.slf4j' } diff --git a/io/build.gradle b/io/build.gradle index cebfb678..00553fed 100644 --- a/io/build.gradle +++ b/io/build.gradle @@ -1,3 +1,4 @@ dependencies { + implementation project(":common-config") implementation libs['common-lang'] } diff --git a/java8/build.gradle b/java8/build.gradle index 043b5905..be7f0c27 100644 --- a/java8/build.gradle +++ b/java8/build.gradle @@ -1,3 +1,4 @@ dependencies { + implementation project(":common-config") implementation(rootProject.libs['kcp-tool']) } diff --git a/kafka/build.gradle b/kafka/build.gradle index 94317943..b6fd107d 100644 --- a/kafka/build.gradle +++ b/kafka/build.gradle @@ -1,4 +1,5 @@ dependencies { + implementation project(":common-config") implementation libs['kcp-tool'] implementation libs['kafka-clients'] implementation libs['jackson_core'] diff --git a/mybatis/build.gradle b/mybatis/build.gradle index 0ef557c1..83e23256 100644 --- a/mybatis/build.gradle +++ b/mybatis/build.gradle @@ -9,10 +9,8 @@ repositories { mavenCentral() } -sourceCompatibility = 1.8 -targetCompatibility = 1.8 - dependencies { + implementation project(":common-config") implementation("org.springframework.boot:spring-boot-starter-web") testImplementation("org.springframework.boot:spring-boot-starter-test") implementation("com.baomidou:mybatis-plus-boot-starter:3.1.2") diff --git a/network/build.gradle b/network/build.gradle index 51d739a2..195dc793 100644 --- a/network/build.gradle +++ b/network/build.gradle @@ -1,3 +1,4 @@ dependencies { + implementation project(":common-config") implementation rootProject.libs['ok-http'] } \ No newline at end of file diff --git a/pattern/build.gradle b/pattern/build.gradle index 07cd24b9..c0a49352 100644 --- a/pattern/build.gradle +++ b/pattern/build.gradle @@ -1,4 +1,5 @@ dependencies { + implementation project(":common-config") implementation rootProject.libs['guava'] } diff --git a/question/build.gradle b/question/build.gradle index 1d1159c3..838c6995 100644 --- a/question/build.gradle +++ b/question/build.gradle @@ -1,4 +1,5 @@ dependencies { + implementation project(":common-config") implementation libs['testng'] implementation libs["cglib-3.2.4"] diff --git a/spring/build.gradle b/spring/build.gradle index 35b0e488..e04756b2 100644 --- a/spring/build.gradle +++ b/spring/build.gradle @@ -1,4 +1,5 @@ dependencies { + implementation project(":common-config") implementation libs['MySQL'] implementation libs['cglib'] diff --git a/test/build.gradle b/test/build.gradle index e69de29b..e64af1b9 100644 --- a/test/build.gradle +++ b/test/build.gradle @@ -0,0 +1,3 @@ +dependencies { + implementation project(":common-config") +} \ No newline at end of file From 0fee1555f1c85f1c6ee6c744c9887733f7c45711 Mon Sep 17 00:00:00 2001 From: kcp Date: Fri, 7 May 2021 21:21:08 +0800 Subject: [PATCH 182/476] +) validator --- settings.gradle | 1 + web/build.gradle | 8 ++ .../kuangcp/validation/ActiveState.java | 12 +++ .../kuangcp/validation/BeanValidator.java | 96 +++++++++++++++++++ .../github/kuangcp/validation/Contain.java | 31 ++++++ .../validation/EnumContainValidator.java | 75 +++++++++++++++ .../github/kuangcp/validation/StrState.java | 9 ++ .../github/kuangcp/validation/TestParam.java | 17 ++++ .../kuangcp/validation/BeanValidatorTest.java | 51 ++++++++++ 9 files changed, 300 insertions(+) create mode 100644 web/build.gradle create mode 100644 web/src/main/java/com/github/kuangcp/validation/ActiveState.java create mode 100644 web/src/main/java/com/github/kuangcp/validation/BeanValidator.java create mode 100644 web/src/main/java/com/github/kuangcp/validation/Contain.java create mode 100644 web/src/main/java/com/github/kuangcp/validation/EnumContainValidator.java create mode 100644 web/src/main/java/com/github/kuangcp/validation/StrState.java create mode 100644 web/src/main/java/com/github/kuangcp/validation/TestParam.java create mode 100644 web/src/test/java/com/github/kuangcp/validation/BeanValidatorTest.java diff --git a/settings.gradle b/settings.gradle index 21b1b7fa..6b946958 100644 --- a/settings.gradle +++ b/settings.gradle @@ -20,4 +20,5 @@ include( , 'hadoop' , 'flink' , 'mybatis' + , 'web' ) diff --git a/web/build.gradle b/web/build.gradle new file mode 100644 index 00000000..5724ebc6 --- /dev/null +++ b/web/build.gradle @@ -0,0 +1,8 @@ +dependencies { + implementation project(":common-config") + + implementation group: 'javax.validation', name: 'validation-api', version: '2.0.1.Final' + implementation group: 'org.hibernate.validator', name: 'hibernate-validator', version: '6.2.0.Final' + implementation group: 'javax.el', name: 'javax.el-api', version: '3.0.0' + implementation group: 'org.glassfish.web', name: 'javax.el', version: '2.2.6' +} \ No newline at end of file diff --git a/web/src/main/java/com/github/kuangcp/validation/ActiveState.java b/web/src/main/java/com/github/kuangcp/validation/ActiveState.java new file mode 100644 index 00000000..88502acc --- /dev/null +++ b/web/src/main/java/com/github/kuangcp/validation/ActiveState.java @@ -0,0 +1,12 @@ +package com.github.kuangcp.validation; + +/** + * @author kuangcp + */ +public interface ActiveState { + + int ACTIVE = 1; + + int NONE = 0; + +} diff --git a/web/src/main/java/com/github/kuangcp/validation/BeanValidator.java b/web/src/main/java/com/github/kuangcp/validation/BeanValidator.java new file mode 100644 index 00000000..c9fd58f5 --- /dev/null +++ b/web/src/main/java/com/github/kuangcp/validation/BeanValidator.java @@ -0,0 +1,96 @@ +package com.github.kuangcp.validation; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import javax.validation.ConstraintViolation; +import javax.validation.Validation; +import javax.validation.Validator; +import javax.validation.ValidatorFactory; +import lombok.extern.slf4j.Slf4j; + +/** + * 基于注解的校验, @NotBlank @NotEmpty.... + * 其他注解请参考{@link org.hibernate.validator.constraints} 和 {@link javax.validation.constraints} + */ +@Slf4j +public abstract class BeanValidator { + + private static final ValidatorFactory validatorFactory = Validation + .buildDefaultValidatorFactory(); + + /** + * 校验多个字段 + * + * @param t 校验的对象 + * @param groups Class, 必须要传,如果没有就传入一个new Class[0] + * @param 校验的对象泛型 + * @return key: 字段,value:错误信息 + */ + public static Map validate(T t, Class... groups) { + Validator validator = validatorFactory.getValidator(); + Set> violationSet = validator.validate(t, groups); + if (violationSet.isEmpty()) { + return Collections.emptyMap(); + } else { + Map errors = new HashMap<>(); + for (ConstraintViolation violation : violationSet) { + errors.put(violation.getPropertyPath().toString(), violation.getMessage()); + } + return errors; + } + } + + /** + * 校验多个对象 + * + * @param collection 集合 + * @return key: 字段,value:错误信息 + */ + public static Map validateList(Collection collection) { + Iterator iterator = collection.iterator(); + Map errors; + do { + if (!iterator.hasNext()) { + return Collections.emptyMap(); + } + Object object = iterator.next(); + // new Class[0] 必须要传, 否则在determineGroupValidationOrder方法中,会抛出异常 + errors = validate(object, new Class[0]); + } while (errors.isEmpty()); + return errors; + } + + /** + * 综合validateList 和 validate 方法, 任何校验只需要使用这个方法就可以 + * + * @param first 第一个对象 + * @param objects 对象数组 + * @return key: 字段,value:错误信息 + */ + public static Map validateObject(Object first, Object... objects) { + if (objects != null && objects.length > 0) { + return validateList(Arrays.asList(first, objects)); + } else { + // new Class[0] 必须要传, 否则在determineGroupValidationOrder方法中,会抛出异常 + return validate(first, new Class[0]); + } + } + + /** + * 再次封装校验,对于异常直接抛出 + * + * @param param 参数 + */ + public static void check(Object param) throws IllegalArgumentException { + Map validateMap = BeanValidator.validateObject(param); + if (Objects.nonNull(validateMap) && !validateMap.isEmpty()) { + throw new RuntimeException(validateMap.values().iterator().next()); + } + } +} diff --git a/web/src/main/java/com/github/kuangcp/validation/Contain.java b/web/src/main/java/com/github/kuangcp/validation/Contain.java new file mode 100644 index 00000000..8f5023d3 --- /dev/null +++ b/web/src/main/java/com/github/kuangcp/validation/Contain.java @@ -0,0 +1,31 @@ +package com.github.kuangcp.validation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import javax.validation.Constraint; +import javax.validation.Payload; + +@Target({ElementType.FIELD}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Constraint(validatedBy = {EnumContainValidator.class}) +@interface Contain { + + Class value(); + + Class[] groups() default {}; + + Class[] payload() default {}; + + String message() default ""; + + @Target({ElementType.FIELD}) + @Retention(RetentionPolicy.RUNTIME) + @Documented + @interface List { + Contain[] value(); + } +} diff --git a/web/src/main/java/com/github/kuangcp/validation/EnumContainValidator.java b/web/src/main/java/com/github/kuangcp/validation/EnumContainValidator.java new file mode 100644 index 00000000..1239d60c --- /dev/null +++ b/web/src/main/java/com/github/kuangcp/validation/EnumContainValidator.java @@ -0,0 +1,75 @@ +package com.github.kuangcp.validation; + +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.Arrays; +import java.util.Objects; +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class EnumContainValidator implements ConstraintValidator { + + private Class code; + + @Override + public void initialize(Contain constraintAnnotation) { + this.code = constraintAnnotation.value(); + } + + @Override + public boolean isValid(Object judgeValue, ConstraintValidatorContext constraintValidatorContext) { + try { + + return Arrays.stream(code.getFields()) + .filter(v -> Modifier.isFinal(v.getModifiers()) && Modifier.isStatic(v.getModifiers())) + .anyMatch(v -> Objects.equals(getStaticFieldValue(v), judgeValue)); + } catch (Exception e) { + log.error("", e); + } + + return false; + } + + public static Object getStaticFieldValue(Field field) { + return getFieldValue(null, field); + } + + public static Object getFieldValue(Object obj, Field field) { + if (null == field) { + return null; + } + if (obj instanceof Class) { + // 静态字段获取时对象为null + obj = null; + } + + setAccessible(field); + Object result; + try { + result = field.get(obj); + } catch (IllegalAccessException e) { + throw new RuntimeException("IllegalAccess for" + field.getDeclaringClass() + field.getName(), + e); + } + return result; + } + + + /** + * 设置方法为可访问(私有方法可以被外部调用) + * + * @param AccessibleObject的子类,比如Class、Method、Field等 + * @param accessibleObject 可设置访问权限的对象,比如Class、Method、Field等 + * @return 被设置可访问的对象 + * @since 4.6.8 + */ + public static T setAccessible(T accessibleObject) { + if (null != accessibleObject && !accessibleObject.isAccessible()) { + accessibleObject.setAccessible(true); + } + return accessibleObject; + } +} diff --git a/web/src/main/java/com/github/kuangcp/validation/StrState.java b/web/src/main/java/com/github/kuangcp/validation/StrState.java new file mode 100644 index 00000000..df159b3a --- /dev/null +++ b/web/src/main/java/com/github/kuangcp/validation/StrState.java @@ -0,0 +1,9 @@ +package com.github.kuangcp.validation; + +public interface StrState { + + String nu = null; + + String d = "2"; + +} diff --git a/web/src/main/java/com/github/kuangcp/validation/TestParam.java b/web/src/main/java/com/github/kuangcp/validation/TestParam.java new file mode 100644 index 00000000..e1de7a21 --- /dev/null +++ b/web/src/main/java/com/github/kuangcp/validation/TestParam.java @@ -0,0 +1,17 @@ +package com.github.kuangcp.validation; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@NoArgsConstructor +@AllArgsConstructor +@Data +class TestParam { + + @Contain(value = ActiveState.class, message = "状态不合法") + private Integer state; + + @Contain(value = StrState.class, message = "状态str不合法") + private String str; +} diff --git a/web/src/test/java/com/github/kuangcp/validation/BeanValidatorTest.java b/web/src/test/java/com/github/kuangcp/validation/BeanValidatorTest.java new file mode 100644 index 00000000..def6af51 --- /dev/null +++ b/web/src/test/java/com/github/kuangcp/validation/BeanValidatorTest.java @@ -0,0 +1,51 @@ +package com.github.kuangcp.validation; + +import lombok.extern.slf4j.Slf4j; +import org.junit.Assert; +import org.junit.Test; + +@Slf4j +public class BeanValidatorTest { + + @Test + public void testValid() { + TestParam d = new TestParam(); + d.setState(ActiveState.ACTIVE); + BeanValidator.check(d); + } + + @Test + public void testInvalid() { + TestParam d = new TestParam(); + d.setState(-1); + try { + BeanValidator.check(d); + } catch (Exception e) { + Assert.assertEquals(e.getMessage(), "状态不合法"); + return; + } + Assert.fail(); + } + + @Test + public void testValidStr() { + TestParam d = new TestParam(); + d.setStr("2"); + d.setState(ActiveState.NONE); + BeanValidator.check(d); + } + + @Test + public void testInvalidStr() { + TestParam d = new TestParam(); + d.setStr("2s"); + d.setState(3); + try { + BeanValidator.check(d); + } catch (Exception e) { + Assert.assertEquals(e.getMessage(), "状态str不合法"); + return; + } + Assert.fail(); + } +} From f84c1bc13a97692a3396d428028bde79bc1bde3e Mon Sep 17 00:00:00 2001 From: kcp Date: Fri, 7 May 2021 21:23:12 +0800 Subject: [PATCH 183/476] +) readme --- web/src/main/java/com/github/kuangcp/validation/Readme.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 web/src/main/java/com/github/kuangcp/validation/Readme.md diff --git a/web/src/main/java/com/github/kuangcp/validation/Readme.md b/web/src/main/java/com/github/kuangcp/validation/Readme.md new file mode 100644 index 00000000..58b4fa0c --- /dev/null +++ b/web/src/main/java/com/github/kuangcp/validation/Readme.md @@ -0,0 +1,3 @@ + +https://docs.jboss.org/hibernate/stable/validator/reference/en-US/html_single/#section-provider-specific-settings + From 559f3b823331a8b3e7c87ec1b3e9a5db430b2708 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Sat, 8 May 2021 06:22:43 +0800 Subject: [PATCH 184/476] +) add error msg constant --- .../github/kuangcp/validation/Contain.java | 2 +- .../validation/EnumContainValidator.java | 10 +++--- .../kuangcp/validation/ErrorMsgConstant.java | 11 +++++++ .../github/kuangcp/validation/StrState.java | 4 +-- .../github/kuangcp/validation/TestParam.java | 4 +-- .../kuangcp/validation/BeanValidatorTest.java | 32 +++++++++---------- 6 files changed, 36 insertions(+), 27 deletions(-) create mode 100644 web/src/main/java/com/github/kuangcp/validation/ErrorMsgConstant.java diff --git a/web/src/main/java/com/github/kuangcp/validation/Contain.java b/web/src/main/java/com/github/kuangcp/validation/Contain.java index 8f5023d3..b274a07d 100644 --- a/web/src/main/java/com/github/kuangcp/validation/Contain.java +++ b/web/src/main/java/com/github/kuangcp/validation/Contain.java @@ -14,7 +14,7 @@ @Constraint(validatedBy = {EnumContainValidator.class}) @interface Contain { - Class value(); + Class value(); Class[] groups() default {}; diff --git a/web/src/main/java/com/github/kuangcp/validation/EnumContainValidator.java b/web/src/main/java/com/github/kuangcp/validation/EnumContainValidator.java index 1239d60c..7ef09fb2 100644 --- a/web/src/main/java/com/github/kuangcp/validation/EnumContainValidator.java +++ b/web/src/main/java/com/github/kuangcp/validation/EnumContainValidator.java @@ -12,18 +12,17 @@ @Slf4j public class EnumContainValidator implements ConstraintValidator { - private Class code; + private Class enumClass; @Override public void initialize(Contain constraintAnnotation) { - this.code = constraintAnnotation.value(); + this.enumClass = constraintAnnotation.value(); } @Override public boolean isValid(Object judgeValue, ConstraintValidatorContext constraintValidatorContext) { try { - - return Arrays.stream(code.getFields()) + return Arrays.stream(enumClass.getFields()) .filter(v -> Modifier.isFinal(v.getModifiers()) && Modifier.isStatic(v.getModifiers())) .anyMatch(v -> Objects.equals(getStaticFieldValue(v), judgeValue)); } catch (Exception e) { @@ -51,13 +50,12 @@ public static Object getFieldValue(Object obj, Field field) { try { result = field.get(obj); } catch (IllegalAccessException e) { - throw new RuntimeException("IllegalAccess for" + field.getDeclaringClass() + field.getName(), + throw new RuntimeException("IllegalAccess for " + field.getDeclaringClass() + field.getName(), e); } return result; } - /** * 设置方法为可访问(私有方法可以被外部调用) * diff --git a/web/src/main/java/com/github/kuangcp/validation/ErrorMsgConstant.java b/web/src/main/java/com/github/kuangcp/validation/ErrorMsgConstant.java new file mode 100644 index 00000000..a4ec6f21 --- /dev/null +++ b/web/src/main/java/com/github/kuangcp/validation/ErrorMsgConstant.java @@ -0,0 +1,11 @@ +package com.github.kuangcp.validation; + +/** + * @author https://github.com/kuangcp on 2021-05-08 06:16 + */ +public interface ErrorMsgConstant { + + String STATE_ERROR = "状态不合法"; + + String STR_STATE_ERROR = "string状态不合法"; +} diff --git a/web/src/main/java/com/github/kuangcp/validation/StrState.java b/web/src/main/java/com/github/kuangcp/validation/StrState.java index df159b3a..c81dfb7e 100644 --- a/web/src/main/java/com/github/kuangcp/validation/StrState.java +++ b/web/src/main/java/com/github/kuangcp/validation/StrState.java @@ -2,8 +2,8 @@ public interface StrState { - String nu = null; + String NONE = null; - String d = "2"; + String SECOND = "2"; } diff --git a/web/src/main/java/com/github/kuangcp/validation/TestParam.java b/web/src/main/java/com/github/kuangcp/validation/TestParam.java index e1de7a21..5ee0fcb9 100644 --- a/web/src/main/java/com/github/kuangcp/validation/TestParam.java +++ b/web/src/main/java/com/github/kuangcp/validation/TestParam.java @@ -9,9 +9,9 @@ @Data class TestParam { - @Contain(value = ActiveState.class, message = "状态不合法") + @Contain(value = ActiveState.class, message = ErrorMsgConstant.STATE_ERROR) private Integer state; - @Contain(value = StrState.class, message = "状态str不合法") + @Contain(value = StrState.class, message = ErrorMsgConstant.STR_STATE_ERROR) private String str; } diff --git a/web/src/test/java/com/github/kuangcp/validation/BeanValidatorTest.java b/web/src/test/java/com/github/kuangcp/validation/BeanValidatorTest.java index def6af51..9603e225 100644 --- a/web/src/test/java/com/github/kuangcp/validation/BeanValidatorTest.java +++ b/web/src/test/java/com/github/kuangcp/validation/BeanValidatorTest.java @@ -9,19 +9,19 @@ public class BeanValidatorTest { @Test public void testValid() { - TestParam d = new TestParam(); - d.setState(ActiveState.ACTIVE); - BeanValidator.check(d); + TestParam param = new TestParam(); + param.setState(ActiveState.ACTIVE); + BeanValidator.check(param); } @Test public void testInvalid() { - TestParam d = new TestParam(); - d.setState(-1); + TestParam param = new TestParam(); + param.setState(-1); try { - BeanValidator.check(d); + BeanValidator.check(param); } catch (Exception e) { - Assert.assertEquals(e.getMessage(), "状态不合法"); + Assert.assertEquals(e.getMessage(), ErrorMsgConstant.STATE_ERROR); return; } Assert.fail(); @@ -29,21 +29,21 @@ public void testInvalid() { @Test public void testValidStr() { - TestParam d = new TestParam(); - d.setStr("2"); - d.setState(ActiveState.NONE); - BeanValidator.check(d); + TestParam param = new TestParam(); + param.setStr(StrState.SECOND); + param.setState(ActiveState.NONE); + BeanValidator.check(param); } @Test public void testInvalidStr() { - TestParam d = new TestParam(); - d.setStr("2s"); - d.setState(3); + TestParam param = new TestParam(); + param.setStr("2s"); + param.setState(3); try { - BeanValidator.check(d); + BeanValidator.check(param); } catch (Exception e) { - Assert.assertEquals(e.getMessage(), "状态str不合法"); + Assert.assertEquals(e.getMessage(), ErrorMsgConstant.STR_STATE_ERROR); return; } Assert.fail(); From 1e4ea463ba266e036f512d641226d86d223a9af4 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Sat, 8 May 2021 06:26:56 +0800 Subject: [PATCH 185/476] *) test assert fail --- .../com/github/kuangcp/validation/BeanValidatorTest.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/web/src/test/java/com/github/kuangcp/validation/BeanValidatorTest.java b/web/src/test/java/com/github/kuangcp/validation/BeanValidatorTest.java index 9603e225..9ccc3f90 100644 --- a/web/src/test/java/com/github/kuangcp/validation/BeanValidatorTest.java +++ b/web/src/test/java/com/github/kuangcp/validation/BeanValidatorTest.java @@ -20,11 +20,10 @@ public void testInvalid() { param.setState(-1); try { BeanValidator.check(param); + Assert.fail(); } catch (Exception e) { Assert.assertEquals(e.getMessage(), ErrorMsgConstant.STATE_ERROR); - return; } - Assert.fail(); } @Test @@ -42,10 +41,9 @@ public void testInvalidStr() { param.setState(3); try { BeanValidator.check(param); + Assert.fail(); } catch (Exception e) { Assert.assertEquals(e.getMessage(), ErrorMsgConstant.STR_STATE_ERROR); - return; } - Assert.fail(); } } From d86535e2af45a5cbb1d8983277714b96d2564521 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Sat, 8 May 2021 08:16:59 +0800 Subject: [PATCH 186/476] *) rename class --- .../multicast/{Two.java => Consumer.java} | 9 +++------ .../kuangcp/multicast/MulticastConstant.java | 11 +++++++++++ .../multicast/{One.java => Producer.java} | 12 +++++------- .../com/github/kuangcp/nio/EchoNIOServer.java | 10 +++++----- .../java/com/github/kuangcp/nio/NIOClient.java | 17 +++++++++-------- .../java/com/github/kuangcp/nio/NIOServer.java | 4 ++-- 6 files changed, 35 insertions(+), 28 deletions(-) rename network/src/main/java/com/github/kuangcp/multicast/{Two.java => Consumer.java} (75%) create mode 100644 network/src/main/java/com/github/kuangcp/multicast/MulticastConstant.java rename network/src/main/java/com/github/kuangcp/multicast/{One.java => Producer.java} (70%) diff --git a/network/src/main/java/com/github/kuangcp/multicast/Two.java b/network/src/main/java/com/github/kuangcp/multicast/Consumer.java similarity index 75% rename from network/src/main/java/com/github/kuangcp/multicast/Two.java rename to network/src/main/java/com/github/kuangcp/multicast/Consumer.java index fdda6d98..9e9e6491 100644 --- a/network/src/main/java/com/github/kuangcp/multicast/Two.java +++ b/network/src/main/java/com/github/kuangcp/multicast/Consumer.java @@ -9,15 +9,12 @@ * @author https://github.com/kuangcp on 2020-01-15 20:34 */ @Slf4j -public class Two { - - private static int port = 8000; - private static String address = "228.0.0.4"; +public class Consumer { public static void main(String[] args) { try { - InetAddress group = InetAddress.getByName(address); - MulticastSocket multicastSocket = new MulticastSocket(port); + InetAddress group = InetAddress.getByName(MulticastConstant.ipAddress); + MulticastSocket multicastSocket = new MulticastSocket(MulticastConstant.port); multicastSocket.joinGroup(group); byte[] buffer = new byte[1024]; diff --git a/network/src/main/java/com/github/kuangcp/multicast/MulticastConstant.java b/network/src/main/java/com/github/kuangcp/multicast/MulticastConstant.java new file mode 100644 index 00000000..25db3650 --- /dev/null +++ b/network/src/main/java/com/github/kuangcp/multicast/MulticastConstant.java @@ -0,0 +1,11 @@ +package com.github.kuangcp.multicast; + +/** + * @author https://github.com/kuangcp on 2021-05-08 08:13 + */ +public interface MulticastConstant { + + int port = 8006; + + String ipAddress = "228.0.0.4"; +} diff --git a/network/src/main/java/com/github/kuangcp/multicast/One.java b/network/src/main/java/com/github/kuangcp/multicast/Producer.java similarity index 70% rename from network/src/main/java/com/github/kuangcp/multicast/One.java rename to network/src/main/java/com/github/kuangcp/multicast/Producer.java index f2bed0cc..bb28ab17 100644 --- a/network/src/main/java/com/github/kuangcp/multicast/One.java +++ b/network/src/main/java/com/github/kuangcp/multicast/Producer.java @@ -9,19 +9,17 @@ * @author https://github.com/kuangcp on 2020-01-15 20:30 */ @Slf4j -public class One { - - private static int port = 8000; - private static String address = "228.0.0.4"; +public class Producer { public static void main(String[] args) { try { - InetAddress group = InetAddress.getByName(address); - MulticastSocket multicastSocket = new MulticastSocket(port); + InetAddress group = InetAddress.getByName(MulticastConstant.ipAddress); + MulticastSocket multicastSocket = new MulticastSocket(MulticastConstant.port); multicastSocket.joinGroup(group); while (true) { byte[] buffer = "One".getBytes(); - DatagramPacket packet = new DatagramPacket(buffer, buffer.length, group, port); + DatagramPacket packet = new DatagramPacket(buffer, buffer.length, group, + MulticastConstant.port); multicastSocket.send(packet); Thread.sleep(1000); } diff --git a/network/src/main/java/com/github/kuangcp/nio/EchoNIOServer.java b/network/src/main/java/com/github/kuangcp/nio/EchoNIOServer.java index 0a56fc81..597f65ad 100644 --- a/network/src/main/java/com/github/kuangcp/nio/EchoNIOServer.java +++ b/network/src/main/java/com/github/kuangcp/nio/EchoNIOServer.java @@ -21,6 +21,7 @@ public class EchoNIOServer { public static void main(String[] args) throws IOException { new EchoNIOServer().start(); } + public void start() throws IOException { Selector selector = Selector.open(); //通过OPEN方法来打开一个未绑定的ServerSocketChannel 实例 @@ -78,9 +79,9 @@ private void handleInput(Selector selector, SelectionKey key) throws IOException String body = new String(bytes, StandardCharsets.UTF_8); System.out.println("The time server receive order : " + body); if ("STOP".equalsIgnoreCase(body)) { - stop(); + this.stop(); } - doWrite(sc); + this.doWrite(sc, body); } else if (readBytes < 0) { // 对端链路关闭 key.cancel(); @@ -92,9 +93,8 @@ private void handleInput(Selector selector, SelectionKey key) throws IOException } - - private void doWrite(SocketChannel channel) throws IOException { - byte[] bytes = "response".getBytes(); + private void doWrite(SocketChannel channel, String body) throws IOException { + byte[] bytes = (body + "?").getBytes(); ByteBuffer writeBuffer = ByteBuffer.allocate(bytes.length); writeBuffer.put(bytes); writeBuffer.flip(); diff --git a/network/src/main/java/com/github/kuangcp/nio/NIOClient.java b/network/src/main/java/com/github/kuangcp/nio/NIOClient.java index 5a24915f..ffb35b34 100644 --- a/network/src/main/java/com/github/kuangcp/nio/NIOClient.java +++ b/network/src/main/java/com/github/kuangcp/nio/NIOClient.java @@ -36,16 +36,17 @@ private void init() throws IOException { try { while (scan.hasNextLine()) { String line = scan.nextLine(); - channel.write(NIOServer.charset.encode(line)); - if ("exit".equalsIgnoreCase(line)) { - stop(); + log.info("exit client"); + this.stop(); return; } + + channel.write(NIOServer.charset.encode(line)); } } catch (IOException e) { log.error(e.getMessage(), e); - stop(); + this.stop(); } } @@ -59,8 +60,8 @@ class ClientThread extends Thread { public void run() { try { while (!stop) { - selector.select(); - + // 如果不设置超时时间会一直阻塞等待,导致客户端无法退出 + selector.select(5000); for (SelectionKey sk : selector.selectedKeys()) { selector.selectedKeys().remove(sk); if (sk.isReadable()) { @@ -69,7 +70,7 @@ public void run() { } } } catch (Exception e) { - e.printStackTrace(); + log.error("", e); } } } @@ -86,7 +87,7 @@ private void readContent(SelectionKey sk) throws IOException { if (content.length() == 0) { return; } - log.info("msg={}", content.toString()); + log.info("server: {}", content); sk.interestOps(SelectionKey.OP_READ); } } diff --git a/network/src/main/java/com/github/kuangcp/nio/NIOServer.java b/network/src/main/java/com/github/kuangcp/nio/NIOServer.java index bb231e07..76b59966 100644 --- a/network/src/main/java/com/github/kuangcp/nio/NIOServer.java +++ b/network/src/main/java/com/github/kuangcp/nio/NIOServer.java @@ -70,7 +70,7 @@ private void start() throws Exception { } // 连接建立 else if (sk.isConnectable()) { - System.out.println("close"); + log.info("close"); } // 读就绪 else if (sk.isReadable()) { @@ -78,7 +78,7 @@ else if (sk.isReadable()) { } // 写就绪 else if (sk.isWritable()) { - + log.info("writeable"); } } } From 7874fefb004ee8b0281c198cf762d97516bfbc95 Mon Sep 17 00:00:00 2001 From: kcp Date: Sat, 8 May 2021 15:52:43 +0800 Subject: [PATCH 187/476] +) compare with stringbuffer --- .../src/main/java/jmh/StringBuilderBenchmark.java | 15 ++++++++++++--- .../test/java/jmh/StringBuilderBenchmarkTest.java | 4 +++- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/class/src/main/java/jmh/StringBuilderBenchmark.java b/class/src/main/java/jmh/StringBuilderBenchmark.java index 3b822bd6..c846e629 100644 --- a/class/src/main/java/jmh/StringBuilderBenchmark.java +++ b/class/src/main/java/jmh/StringBuilderBenchmark.java @@ -15,9 +15,9 @@ */ @BenchmarkMode(Mode.Throughput) @Warmup(iterations = 3) -@Measurement(iterations = 10, time = 1) -@Threads(4) -@Fork(2) +@Measurement(iterations = 5, time = 2) +@Threads(3) +@Fork(3) @OutputTimeUnit(TimeUnit.MILLISECONDS) public class StringBuilderBenchmark { @@ -37,4 +37,13 @@ public void testStringBuilderAdd() { } sb.toString(); } + + @Benchmark + public void testStringBufferAdd() { + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < 10; i++) { + sb.append(i); + } + sb.toString(); + } } \ No newline at end of file diff --git a/class/src/test/java/jmh/StringBuilderBenchmarkTest.java b/class/src/test/java/jmh/StringBuilderBenchmarkTest.java index 1614fe66..b496bd96 100644 --- a/class/src/test/java/jmh/StringBuilderBenchmarkTest.java +++ b/class/src/test/java/jmh/StringBuilderBenchmarkTest.java @@ -13,9 +13,11 @@ public class StringBuilderBenchmarkTest { @Test public void test() throws RunnerException { + String output = "/tmp/StringBuilderBenchmark-" + System.currentTimeMillis() + ".log"; + System.out.println(output); Options options = new OptionsBuilder() .include(StringBuilderBenchmark.class.getSimpleName()) - .output("/tmp/Benchmark.log").build(); + .output(output).build(); new Runner(options).run(); } } From b7eb784c8854e25cc833bcbd23b56659ad9b6434 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Mon, 10 May 2021 08:06:36 +0800 Subject: [PATCH 188/476] *) upgrade --- dependency.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dependency.gradle b/dependency.gradle index bfa20b1d..38f03f8b 100644 --- a/dependency.gradle +++ b/dependency.gradle @@ -36,8 +36,8 @@ ext { // test, junit has removed hamcrest since 4.11 , "hamcrest-core" : "org.hamcrest:hamcrest-core:$ver.hamcrest" , "hamcrest-lib" : "org.hamcrest:hamcrest-library:$ver.hamcrest" - , "jmh" : "org.openjdk.jmh:jmh-core:1.21" - , "jmh-generator-annprocess": "org.openjdk.jmh:jmh-generator-annprocess:1.21" + , "jmh" : "org.openjdk.jmh:jmh-core:1.30" + , "jmh-generator-annprocess": "org.openjdk.jmh:jmh-generator-annprocess:1.30" , "junit" : "junit:junit:$ver.junit" , "mockito-core" : "org.mockito:mockito-core:2.21.0" From 7a93c1f37f4920e8459cb654a762b1fe2e553bb7 Mon Sep 17 00:00:00 2001 From: kcp Date: Mon, 10 May 2021 09:34:19 +0800 Subject: [PATCH 189/476] +) boxing test --- class/src/main/java/jmh/BoxingBenchmark.java | 39 +++++++++++++++++++ .../test/java/jmh/BoxingBenchmarkTest.java | 24 ++++++++++++ 2 files changed, 63 insertions(+) create mode 100644 class/src/main/java/jmh/BoxingBenchmark.java create mode 100644 class/src/test/java/jmh/BoxingBenchmarkTest.java diff --git a/class/src/main/java/jmh/BoxingBenchmark.java b/class/src/main/java/jmh/BoxingBenchmark.java new file mode 100644 index 00000000..ffe65ca9 --- /dev/null +++ b/class/src/main/java/jmh/BoxingBenchmark.java @@ -0,0 +1,39 @@ +package jmh; + +import java.util.concurrent.TimeUnit; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Threads; +import org.openjdk.jmh.annotations.Warmup; + +/** + * @author kuangcp + */ +@BenchmarkMode(Mode.Throughput) +@Warmup(iterations = 3) +@Measurement(iterations = 5, time = 2) +@Threads(3) +@Fork(3) +@OutputTimeUnit(TimeUnit.MILLISECONDS) +public class BoxingBenchmark { + + @Benchmark + public void testBoxing() { + Integer v = 4; + for (int i = 0; i < 100; i++) { + v += 4; + } + } + + @Benchmark + public void testNoBoxing() { + int v = 4; + for (int i = 0; i < 100; i++) { + v += 4; + } + } +} diff --git a/class/src/test/java/jmh/BoxingBenchmarkTest.java b/class/src/test/java/jmh/BoxingBenchmarkTest.java new file mode 100644 index 00000000..1a39d382 --- /dev/null +++ b/class/src/test/java/jmh/BoxingBenchmarkTest.java @@ -0,0 +1,24 @@ +package jmh; + +import org.junit.Test; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +/** + * @author kuangcp + */ +public class BoxingBenchmarkTest { + + @Test + public void testBoxingAndUnBoxing() throws RunnerException { + String output = "/tmp/StringBuilderBenchmark-" + System.currentTimeMillis() + ".log"; + System.out.println(output); + Options options = new OptionsBuilder() + .include(BoxingBenchmark.class.getSimpleName()) + .output(output).build(); + new Runner(options).run(); + } + +} From b26c200a6fc352ffed391f789992f8a0e0005b2e Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Mon, 17 May 2021 00:29:59 +0800 Subject: [PATCH 190/476] +) gc test case --- .../java/jvm/gc/cms/TriggerFullGCForCMS.java | 44 ++++++++++++++++++ .../java/jvm/gc/g1/StringDeduplication.java | 21 +++++++++ class/src/main/java/jvm/load/Readme.md | 2 +- .../InstantiationAndConstructorTest.java | 6 +-- .../java/jmh/StringBuilderBenchmarkTest.java | 45 ++++++++++++++++++- 5 files changed, 112 insertions(+), 6 deletions(-) create mode 100644 class/src/main/java/jvm/gc/cms/TriggerFullGCForCMS.java create mode 100644 class/src/main/java/jvm/gc/g1/StringDeduplication.java diff --git a/class/src/main/java/jvm/gc/cms/TriggerFullGCForCMS.java b/class/src/main/java/jvm/gc/cms/TriggerFullGCForCMS.java new file mode 100644 index 00000000..7160f848 --- /dev/null +++ b/class/src/main/java/jvm/gc/cms/TriggerFullGCForCMS.java @@ -0,0 +1,44 @@ +package jvm.gc.cms; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +/** + * @author https://github.com/kuangcp on 2021-05-15 18:53 + * + * 问题:内存溢出后会死循环创建 dump 直到填满硬盘,CMSScavengeBeforeRemark 并不能在 CMSGC 的时候触发 FullGC + * + * -Xms300m -Xmx300m -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:CMSFullGCsBeforeCompaction=1 + * -XX:CMSInitiatingOccupancyFraction=75 -XX:+CMSScavengeBeforeRemark -XX:+HeapDumpBeforeFullGC + * -XX:+HeapDumpOnOutOfMemoryError -XX:+CMSClassUnloadingEnabled -XX:+DisableExplicitGC -verbose:gc + * -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:HeapDumpPath=/home/kcp/test/java-heapdump.hprof + * -Xloggc:/home/kcp/test/gc.log + */ +public class TriggerFullGCForCMS { + + final static Runnable eatMem = () -> { + List temp = new ArrayList<>(); + for (int i = 0; i < 60; i++) { + try { + TimeUnit.MILLISECONDS.sleep(30); + } catch (InterruptedException e) { + e.printStackTrace(); + } + temp.add(new byte[1024 * 1024]); + System.out.println(Thread.currentThread().getName() + " " + i); + } + }; + + public static void main(String[] args) throws InterruptedException { + final ExecutorService pool = Executors.newFixedThreadPool(4); + List live = new ArrayList<>(); + while (true) { + pool.execute(eatMem); + live.add(new byte[1024 * 1024]); + TimeUnit.SECONDS.sleep(1); + } + } +} diff --git a/class/src/main/java/jvm/gc/g1/StringDeduplication.java b/class/src/main/java/jvm/gc/g1/StringDeduplication.java new file mode 100644 index 00000000..d3da530f --- /dev/null +++ b/class/src/main/java/jvm/gc/g1/StringDeduplication.java @@ -0,0 +1,21 @@ +package jvm.gc.g1; + +import java.util.concurrent.TimeUnit; + +/** + * @author https://github.com/kuangcp on 2021-05-16 23:36 + * + * -Xms15m -Xmx15m -XX:+PrintGCDetails -XX:+UseG1GC -XX:+PrintStringTableStatistics -XX:+UseStringDeduplication -XX:+PrintStringDeduplicationStatistics + */ +public class StringDeduplication { + + // TODO 如何设计用例 体现去重特性 + public static void main(String[] args) throws InterruptedException { + for (int j = 0; j < 100; j++) { + for (int i = 0; i < 1000; i++) { + String.valueOf(i).intern(); + } + TimeUnit.MILLISECONDS.sleep(30); + } + } +} diff --git a/class/src/main/java/jvm/load/Readme.md b/class/src/main/java/jvm/load/Readme.md index 8705d541..17df3354 100644 --- a/class/src/main/java/jvm/load/Readme.md +++ b/class/src/main/java/jvm/load/Readme.md @@ -1 +1 @@ -Load and Unload class +# Load and Unload class diff --git a/class/src/test/java/com/github/kuangcp/instantiation/InstantiationAndConstructorTest.java b/class/src/test/java/com/github/kuangcp/instantiation/InstantiationAndConstructorTest.java index e63b672d..cc1fcfd4 100644 --- a/class/src/test/java/com/github/kuangcp/instantiation/InstantiationAndConstructorTest.java +++ b/class/src/test/java/com/github/kuangcp/instantiation/InstantiationAndConstructorTest.java @@ -25,12 +25,12 @@ * 1. 反序列化 * 1. Unsafe.allocateInstance * - * clone 以及 serialize 创建对象时 不会调用构造器 + * clone 以及 serialize,Unsafe allocateInstance 三种方式 创建对象时 不会调用构造器 */ @Slf4j public class InstantiationAndConstructorTest { - // 直接调用构造器 + // 显式调用构造器 @Test public void testInitByNew() { InstantiationAndConstructor instance = new InstantiationAndConstructor("name"); @@ -44,7 +44,7 @@ public void testInitByReflectEmpty() throws IllegalAccessException, Instantiatio InstantiationAndConstructor.class.newInstance(); } - // 反射实例化对象方式 获取指定构造器 + // 反射实例化对象方式 获取并调用指定构造器 @Test public void testInitByReflect() throws ReflectiveOperationException { Constructor constructor = diff --git a/class/src/test/java/jmh/StringBuilderBenchmarkTest.java b/class/src/test/java/jmh/StringBuilderBenchmarkTest.java index b496bd96..5fae127c 100644 --- a/class/src/test/java/jmh/StringBuilderBenchmarkTest.java +++ b/class/src/test/java/jmh/StringBuilderBenchmarkTest.java @@ -1,6 +1,15 @@ package jmh; +import java.util.concurrent.TimeUnit; import org.junit.Test; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Threads; +import org.openjdk.jmh.annotations.Warmup; import org.openjdk.jmh.runner.Runner; import org.openjdk.jmh.runner.RunnerException; import org.openjdk.jmh.runner.options.Options; @@ -9,6 +18,12 @@ /** * @author kuangcp on 2019-04-21 11:39 PM */ +@BenchmarkMode(Mode.Throughput) +@Warmup(iterations = 1) +@Measurement(iterations = 1, time = 1) +@Threads(3) +@Fork(3) +@OutputTimeUnit(TimeUnit.MILLISECONDS) public class StringBuilderBenchmarkTest { @Test @@ -16,8 +31,34 @@ public void test() throws RunnerException { String output = "/tmp/StringBuilderBenchmark-" + System.currentTimeMillis() + ".log"; System.out.println(output); Options options = new OptionsBuilder() - .include(StringBuilderBenchmark.class.getSimpleName()) + .include(StringBuilderBenchmarkTest.class.getSimpleName()) .output(output).build(); new Runner(options).run(); } -} + + @Benchmark + public void testStringAdd() { + String a = ""; + for (int i = 0; i < 10; i++) { + a += i; + } + } + + @Benchmark + public void testStringBuilderAdd() { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < 10; i++) { + sb.append(i); + } + sb.toString(); + } + + @Benchmark + public void testStringBufferAdd() { + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < 10; i++) { + sb.append(i); + } + sb.toString(); + } +} \ No newline at end of file From 48c466736ef9d329168d9346118f9b6b087e0586 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Tue, 18 May 2021 08:44:25 +0800 Subject: [PATCH 191/476] +) netty websocket server demo --- .../netty/websocket/ChannelSupervise.java | 37 +++++ .../NioWebSocketChannelInitializer.java | 22 +++ .../netty/websocket/NioWebSocketHandler.java | 138 ++++++++++++++++++ .../netty/websocket/NioWebSocketServer.java | 41 ++++++ 4 files changed, 238 insertions(+) create mode 100644 netty/src/main/java/netty/websocket/ChannelSupervise.java create mode 100644 netty/src/main/java/netty/websocket/NioWebSocketChannelInitializer.java create mode 100644 netty/src/main/java/netty/websocket/NioWebSocketHandler.java create mode 100644 netty/src/main/java/netty/websocket/NioWebSocketServer.java diff --git a/netty/src/main/java/netty/websocket/ChannelSupervise.java b/netty/src/main/java/netty/websocket/ChannelSupervise.java new file mode 100644 index 00000000..de9f056f --- /dev/null +++ b/netty/src/main/java/netty/websocket/ChannelSupervise.java @@ -0,0 +1,37 @@ +package netty.websocket; + +import io.netty.channel.Channel; +import io.netty.channel.ChannelId; +import io.netty.channel.group.ChannelGroup; +import io.netty.channel.group.DefaultChannelGroup; +import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; +import io.netty.util.concurrent.GlobalEventExecutor; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +/** + * @author https://github.com/kuangcp on 2021-05-18 08:34 + */ +public class ChannelSupervise { + + private static final ChannelGroup GlobalGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE); + private static final ConcurrentMap ChannelMap = new ConcurrentHashMap<>(); + + public static void addChannel(Channel channel) { + GlobalGroup.add(channel); + ChannelMap.put(channel.id().asShortText(), channel.id()); + } + + public static void removeChannel(Channel channel) { + GlobalGroup.remove(channel); + ChannelMap.remove(channel.id().asShortText()); + } + + public static Channel findChannel(String id) { + return GlobalGroup.find(ChannelMap.get(id)); + } + + public static void send2All(TextWebSocketFrame tws) { + GlobalGroup.writeAndFlush(tws); + } +} diff --git a/netty/src/main/java/netty/websocket/NioWebSocketChannelInitializer.java b/netty/src/main/java/netty/websocket/NioWebSocketChannelInitializer.java new file mode 100644 index 00000000..8057e8c4 --- /dev/null +++ b/netty/src/main/java/netty/websocket/NioWebSocketChannelInitializer.java @@ -0,0 +1,22 @@ +package netty.websocket; + +import io.netty.channel.ChannelInitializer; +import io.netty.channel.socket.SocketChannel; +import io.netty.handler.codec.http.HttpObjectAggregator; +import io.netty.handler.codec.http.HttpServerCodec; +import io.netty.handler.logging.LoggingHandler; +import io.netty.handler.stream.ChunkedWriteHandler; + +/** + * @author https://github.com/kuangcp on 2021-05-18 08:33 + */ +public class NioWebSocketChannelInitializer extends ChannelInitializer { + @Override + protected void initChannel(SocketChannel ch) { + ch.pipeline().addLast("logging",new LoggingHandler("DEBUG"));//设置log监听器,并且日志级别为debug,方便观察运行流程 + ch.pipeline().addLast("http-codec",new HttpServerCodec());//设置解码器 + ch.pipeline().addLast("aggregator",new HttpObjectAggregator(65536));//聚合器,使用websocket会用到 + ch.pipeline().addLast("http-chunked",new ChunkedWriteHandler());//用于大数据的分区传输 + ch.pipeline().addLast("handler",new NioWebSocketHandler());//自定义的业务handler + } +} diff --git a/netty/src/main/java/netty/websocket/NioWebSocketHandler.java b/netty/src/main/java/netty/websocket/NioWebSocketHandler.java new file mode 100644 index 00000000..787dc6d0 --- /dev/null +++ b/netty/src/main/java/netty/websocket/NioWebSocketHandler.java @@ -0,0 +1,138 @@ +package netty.websocket; + + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelFutureListener; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.SimpleChannelInboundHandler; +import io.netty.handler.codec.http.DefaultFullHttpResponse; +import io.netty.handler.codec.http.FullHttpRequest; +import io.netty.handler.codec.http.HttpResponseStatus; +import io.netty.handler.codec.http.HttpUtil; +import io.netty.handler.codec.http.HttpVersion; +import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame; +import io.netty.handler.codec.http.websocketx.PingWebSocketFrame; +import io.netty.handler.codec.http.websocketx.PongWebSocketFrame; +import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; +import io.netty.handler.codec.http.websocketx.WebSocketFrame; +import io.netty.handler.codec.http.websocketx.WebSocketServerHandshaker; +import io.netty.handler.codec.http.websocketx.WebSocketServerHandshakerFactory; +import io.netty.util.CharsetUtil; +import java.util.Date; +import lombok.extern.slf4j.Slf4j; + +/** + * @author https://github.com/kuangcp on 2021-05-18 08:33 + */ +@Slf4j +public class NioWebSocketHandler extends SimpleChannelInboundHandler { + + private WebSocketServerHandshaker handshaker; + + @Override + protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception { + log.debug("收到消息:" + msg); + if (msg instanceof FullHttpRequest) { + //以http请求形式接入,但是走的是websocket + handleHttpRequest(ctx, (FullHttpRequest) msg); + } else if (msg instanceof WebSocketFrame) { + //处理websocket客户端的消息 + handlerWebSocketFrame(ctx, (WebSocketFrame) msg); + } + } + + @Override + public void channelActive(ChannelHandlerContext ctx) throws Exception { + //添加连接 + log.debug("客户端加入连接:" + ctx.channel()); + ChannelSupervise.addChannel(ctx.channel()); + } + + @Override + public void channelInactive(ChannelHandlerContext ctx) throws Exception { + //断开连接 + log.debug("客户端断开连接:" + ctx.channel()); + ChannelSupervise.removeChannel(ctx.channel()); + } + + @Override + public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { + ctx.flush(); + } + + private void handlerWebSocketFrame(ChannelHandlerContext ctx, WebSocketFrame frame) { + // 判断是否关闭链路的指令 + if (frame instanceof CloseWebSocketFrame) { + handshaker.close(ctx.channel(), (CloseWebSocketFrame) frame.retain()); + return; + } + // 判断是否ping消息 + if (frame instanceof PingWebSocketFrame) { + ctx.channel().write( + new PongWebSocketFrame(frame.content().retain())); + return; + } + // 本例程仅支持文本消息,不支持二进制消息 + if (!(frame instanceof TextWebSocketFrame)) { + log.debug("本例程仅支持文本消息,不支持二进制消息"); + throw new UnsupportedOperationException(String.format( + "%s frame types not supported", frame.getClass().getName())); + } + // 返回应答消息 + String request = ((TextWebSocketFrame) frame).text(); + log.debug("服务端收到:" + request); + TextWebSocketFrame tws = new TextWebSocketFrame(new Date().toString() + + ctx.channel().id() + ":" + request); + + // 群发至所有连接 +// ChannelSupervise.send2All(tws); + + // 返回【谁发的发给谁】 + ctx.channel().writeAndFlush(tws); + } + + /** + * 唯一的一次http请求,用于创建websocket + */ + private void handleHttpRequest(ChannelHandlerContext ctx, + FullHttpRequest req) { + //要求Upgrade为websocket,过滤掉get/Post + if (!req.decoderResult().isSuccess() + || (!"websocket".equals(req.headers().get("Upgrade")))) { + //若不是websocket方式,则创建BAD_REQUEST的req,返回给客户端 + sendHttpResponse(ctx, req, new DefaultFullHttpResponse( + HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST)); + return; + } + WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory( + "ws://localhost:7094/ws", null, false); + handshaker = wsFactory.newHandshaker(req); + if (handshaker == null) { + WebSocketServerHandshakerFactory + .sendUnsupportedVersionResponse(ctx.channel()); + } else { + handshaker.handshake(ctx.channel(), req); + } + } + + /** + * 拒绝不合法的请求,并返回错误信息 + */ + private static void sendHttpResponse(ChannelHandlerContext ctx, + FullHttpRequest req, DefaultFullHttpResponse res) { + // 返回应答给客户端 + if (res.status().code() != 200) { + ByteBuf buf = Unpooled.copiedBuffer(res.status().toString(), + CharsetUtil.UTF_8); + res.content().writeBytes(buf); + buf.release(); + } + ChannelFuture f = ctx.channel().writeAndFlush(res); + // 如果是非Keep-Alive,关闭连接 + if (!HttpUtil.isKeepAlive(req) || res.status().code() != 200) { + f.addListener(ChannelFutureListener.CLOSE); + } + } +} diff --git a/netty/src/main/java/netty/websocket/NioWebSocketServer.java b/netty/src/main/java/netty/websocket/NioWebSocketServer.java new file mode 100644 index 00000000..3a8665a4 --- /dev/null +++ b/netty/src/main/java/netty/websocket/NioWebSocketServer.java @@ -0,0 +1,41 @@ +package netty.websocket; + +import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.Channel; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.nio.NioServerSocketChannel; +import lombok.extern.slf4j.Slf4j; + +/** + * @author https://github.com/kuangcp on 2021-05-18 08:32 + */ +@Slf4j +public class NioWebSocketServer { + + private void init() { + log.info("正在启动websocket服务器"); + NioEventLoopGroup boss = new NioEventLoopGroup(); + NioEventLoopGroup work = new NioEventLoopGroup(); + try { + ServerBootstrap bootstrap = new ServerBootstrap(); + bootstrap.group(boss, work); + bootstrap.channel(NioServerSocketChannel.class); + bootstrap.childHandler(new NioWebSocketChannelInitializer()); + Channel channel = bootstrap.bind(7094).sync().channel(); + log.info("webSocket服务器启动成功:" + channel); + channel.closeFuture().sync(); + } catch (InterruptedException e) { + e.printStackTrace(); + log.info("运行出错:" + e); + } finally { + boss.shutdownGracefully(); + work.shutdownGracefully(); + log.info("websocket服务器已关闭"); + } + } + + public static void main(String[] args) { + new NioWebSocketServer().init(); + } +} + From cad9a98df57060501b72e49219465ac5f7571b76 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Tue, 18 May 2021 08:51:02 +0800 Subject: [PATCH 192/476] *) log --- .../netty/websocket/NioWebSocketHandler.java | 19 +++++++++---------- .../netty/websocket/NioWebSocketServer.java | 9 ++++----- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/netty/src/main/java/netty/websocket/NioWebSocketHandler.java b/netty/src/main/java/netty/websocket/NioWebSocketHandler.java index 787dc6d0..a54e1f83 100644 --- a/netty/src/main/java/netty/websocket/NioWebSocketHandler.java +++ b/netty/src/main/java/netty/websocket/NioWebSocketHandler.java @@ -45,14 +45,12 @@ protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Except @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { - //添加连接 log.debug("客户端加入连接:" + ctx.channel()); ChannelSupervise.addChannel(ctx.channel()); } @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { - //断开连接 log.debug("客户端断开连接:" + ctx.channel()); ChannelSupervise.removeChannel(ctx.channel()); } @@ -68,23 +66,26 @@ private void handlerWebSocketFrame(ChannelHandlerContext ctx, WebSocketFrame fra handshaker.close(ctx.channel(), (CloseWebSocketFrame) frame.retain()); return; } + // 判断是否ping消息 if (frame instanceof PingWebSocketFrame) { ctx.channel().write( new PongWebSocketFrame(frame.content().retain())); return; } + // 本例程仅支持文本消息,不支持二进制消息 if (!(frame instanceof TextWebSocketFrame)) { log.debug("本例程仅支持文本消息,不支持二进制消息"); throw new UnsupportedOperationException(String.format( "%s frame types not supported", frame.getClass().getName())); } + // 返回应答消息 String request = ((TextWebSocketFrame) frame).text(); log.debug("服务端收到:" + request); - TextWebSocketFrame tws = new TextWebSocketFrame(new Date().toString() - + ctx.channel().id() + ":" + request); + TextWebSocketFrame tws = new TextWebSocketFrame( + new Date().toString() + ctx.channel().id() + ":" + request); // 群发至所有连接 // ChannelSupervise.send2All(tws); @@ -96,8 +97,7 @@ private void handlerWebSocketFrame(ChannelHandlerContext ctx, WebSocketFrame fra /** * 唯一的一次http请求,用于创建websocket */ - private void handleHttpRequest(ChannelHandlerContext ctx, - FullHttpRequest req) { + private void handleHttpRequest(ChannelHandlerContext ctx, FullHttpRequest req) { //要求Upgrade为websocket,过滤掉get/Post if (!req.decoderResult().isSuccess() || (!"websocket".equals(req.headers().get("Upgrade")))) { @@ -106,12 +106,12 @@ private void handleHttpRequest(ChannelHandlerContext ctx, HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST)); return; } + WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory( "ws://localhost:7094/ws", null, false); handshaker = wsFactory.newHandshaker(req); if (handshaker == null) { - WebSocketServerHandshakerFactory - .sendUnsupportedVersionResponse(ctx.channel()); + WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel()); } else { handshaker.handshake(ctx.channel(), req); } @@ -124,8 +124,7 @@ private static void sendHttpResponse(ChannelHandlerContext ctx, FullHttpRequest req, DefaultFullHttpResponse res) { // 返回应答给客户端 if (res.status().code() != 200) { - ByteBuf buf = Unpooled.copiedBuffer(res.status().toString(), - CharsetUtil.UTF_8); + ByteBuf buf = Unpooled.copiedBuffer(res.status().toString(), CharsetUtil.UTF_8); res.content().writeBytes(buf); buf.release(); } diff --git a/netty/src/main/java/netty/websocket/NioWebSocketServer.java b/netty/src/main/java/netty/websocket/NioWebSocketServer.java index 3a8665a4..779b05fa 100644 --- a/netty/src/main/java/netty/websocket/NioWebSocketServer.java +++ b/netty/src/main/java/netty/websocket/NioWebSocketServer.java @@ -13,7 +13,7 @@ public class NioWebSocketServer { private void init() { - log.info("正在启动websocket服务器"); + log.info("正在启动WebSocket服务器"); NioEventLoopGroup boss = new NioEventLoopGroup(); NioEventLoopGroup work = new NioEventLoopGroup(); try { @@ -22,15 +22,14 @@ private void init() { bootstrap.channel(NioServerSocketChannel.class); bootstrap.childHandler(new NioWebSocketChannelInitializer()); Channel channel = bootstrap.bind(7094).sync().channel(); - log.info("webSocket服务器启动成功:" + channel); + log.info("WebSocket服务器启动成功:" + channel); channel.closeFuture().sync(); } catch (InterruptedException e) { - e.printStackTrace(); - log.info("运行出错:" + e); + log.info("", e); } finally { boss.shutdownGracefully(); work.shutdownGracefully(); - log.info("websocket服务器已关闭"); + log.info("WebSocket服务器已关闭"); } } From 73679e0254cb50ec26756eeeacc0b16f2ddca9dd Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Tue, 18 May 2021 08:57:58 +0800 Subject: [PATCH 193/476] +) active count --- .../netty/websocket/ChannelSupervise.java | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/netty/src/main/java/netty/websocket/ChannelSupervise.java b/netty/src/main/java/netty/websocket/ChannelSupervise.java index de9f056f..1a5b6eed 100644 --- a/netty/src/main/java/netty/websocket/ChannelSupervise.java +++ b/netty/src/main/java/netty/websocket/ChannelSupervise.java @@ -8,30 +8,39 @@ import io.netty.util.concurrent.GlobalEventExecutor; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +import lombok.extern.slf4j.Slf4j; /** * @author https://github.com/kuangcp on 2021-05-18 08:34 */ +@Slf4j public class ChannelSupervise { - private static final ChannelGroup GlobalGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE); - private static final ConcurrentMap ChannelMap = new ConcurrentHashMap<>(); + private static final ChannelGroup GLOBAL_GROUP = new DefaultChannelGroup( + GlobalEventExecutor.INSTANCE); + private static final ConcurrentMap CHANNEL_MAP = new ConcurrentHashMap<>(); public static void addChannel(Channel channel) { - GlobalGroup.add(channel); - ChannelMap.put(channel.id().asShortText(), channel.id()); + GLOBAL_GROUP.add(channel); + CHANNEL_MAP.put(channel.id().asShortText(), channel.id()); + printState(); } public static void removeChannel(Channel channel) { - GlobalGroup.remove(channel); - ChannelMap.remove(channel.id().asShortText()); + GLOBAL_GROUP.remove(channel); + CHANNEL_MAP.remove(channel.id().asShortText()); + printState(); } public static Channel findChannel(String id) { - return GlobalGroup.find(ChannelMap.get(id)); + return GLOBAL_GROUP.find(CHANNEL_MAP.get(id)); } public static void send2All(TextWebSocketFrame tws) { - GlobalGroup.writeAndFlush(tws); + GLOBAL_GROUP.writeAndFlush(tws); + } + + private static void printState() { + log.info("channelSize={} global={}", CHANNEL_MAP.size(), GLOBAL_GROUP.size()); } } From 2fdd80a89fdc88efa68839656f9f6245fd842bea Mon Sep 17 00:00:00 2001 From: kcp Date: Tue, 18 May 2021 21:47:57 +0800 Subject: [PATCH 194/476] +) xor for encrypt --- .../java/syntax/bit/BitOperatorsTest.java | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/class/src/test/java/syntax/bit/BitOperatorsTest.java b/class/src/test/java/syntax/bit/BitOperatorsTest.java index 9fa7d231..b4ed3b69 100644 --- a/class/src/test/java/syntax/bit/BitOperatorsTest.java +++ b/class/src/test/java/syntax/bit/BitOperatorsTest.java @@ -5,8 +5,12 @@ import com.github.kuangcp.time.GetRunTime; import com.github.kuangcp.util.ShowBinary; +import java.nio.charset.StandardCharsets; import java.util.stream.IntStream; +import java.util.stream.Stream; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.junit.Assert; import org.junit.Test; /** @@ -133,6 +137,35 @@ public void testCompareSpeed() { runTime.endCountOneLine("log and pow"); } + @Test + public void testXORByByte() { + String originMsg = "原始中文内容123Abc"; + + byte[] data = originMsg.getBytes(StandardCharsets.UTF_8); + log.info("data={}", data); + + byte[] one = transfer(data); + log.info("data={}", one); + + byte[] two = transfer(one); + log.info("data={}", two); + + String oneStr = StringUtils.toEncodedString(one, StandardCharsets.UTF_8); + String twoStr = StringUtils.toEncodedString(two, StandardCharsets.UTF_8); + assertThat(twoStr, equalTo(originMsg)); + Assert.assertNotEquals(oneStr, originMsg); + } + + private byte[] transfer(byte[] data) { + byte[] key = {3, 56, 12, 22, 35, 87, 123, 83, 111, 34, 23, 56, 34, 56}; + int maxKey = key.length; + byte[] result = new byte[data.length]; + for (int i = 0; i < data.length; i++) { + result[i] = (byte) (data[i] ^ key[i % maxKey]); + } + return result; + } + /** * 等价于 num => (int)Math.pow(2, Math.floor(Math.log(i) / Math.log(2)) + 1)) * 最大使得形成 16位1 + 1 From 6a30f662d99699e04a438c0d747884e5f89ca2ba Mon Sep 17 00:00:00 2001 From: kcp Date: Tue, 18 May 2021 21:52:53 +0800 Subject: [PATCH 195/476] +) Readme --- netty/src/main/java/netty/websocket/Readme.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 netty/src/main/java/netty/websocket/Readme.md diff --git a/netty/src/main/java/netty/websocket/Readme.md b/netty/src/main/java/netty/websocket/Readme.md new file mode 100644 index 00000000..25bb730b --- /dev/null +++ b/netty/src/main/java/netty/websocket/Readme.md @@ -0,0 +1,3 @@ +Netty 实现的 websocket + +TODO 对比 Tomcat,Spring实现 进行性能测试(固定 JVM 参数 相同硬件,测试最大连接数,失败数,GC情况,延迟) From 9f7134b9de27b1f49b8dd78279dc4502c2ed4935 Mon Sep 17 00:00:00 2001 From: kcp Date: Thu, 20 May 2021 10:58:18 +0800 Subject: [PATCH 196/476] +) limit class and param --- .../github/kuangcp/validation/EnumContainValidator.java | 9 +++++++++ .../com/github/kuangcp/validation/BeanValidatorTest.java | 2 ++ 2 files changed, 11 insertions(+) diff --git a/web/src/main/java/com/github/kuangcp/validation/EnumContainValidator.java b/web/src/main/java/com/github/kuangcp/validation/EnumContainValidator.java index 7ef09fb2..6dd61837 100644 --- a/web/src/main/java/com/github/kuangcp/validation/EnumContainValidator.java +++ b/web/src/main/java/com/github/kuangcp/validation/EnumContainValidator.java @@ -21,7 +21,16 @@ public void initialize(Contain constraintAnnotation) { @Override public boolean isValid(Object judgeValue, ConstraintValidatorContext constraintValidatorContext) { + if (Objects.isNull(judgeValue)) { + return false; + } try { + if (!enumClass.isInterface()) { + log.warn("只能使用接口对入参进行约束: class:{}", enumClass.getSimpleName()); + return false; + } + + // TODO 实际使用需要缓存反射的字段 return Arrays.stream(enumClass.getFields()) .filter(v -> Modifier.isFinal(v.getModifiers()) && Modifier.isStatic(v.getModifiers())) .anyMatch(v -> Objects.equals(getStaticFieldValue(v), judgeValue)); diff --git a/web/src/test/java/com/github/kuangcp/validation/BeanValidatorTest.java b/web/src/test/java/com/github/kuangcp/validation/BeanValidatorTest.java index 9ccc3f90..0fda64b0 100644 --- a/web/src/test/java/com/github/kuangcp/validation/BeanValidatorTest.java +++ b/web/src/test/java/com/github/kuangcp/validation/BeanValidatorTest.java @@ -11,6 +11,7 @@ public class BeanValidatorTest { public void testValid() { TestParam param = new TestParam(); param.setState(ActiveState.ACTIVE); + param.setStr(StrState.SECOND); BeanValidator.check(param); } @@ -18,6 +19,7 @@ public void testValid() { public void testInvalid() { TestParam param = new TestParam(); param.setState(-1); + param.setStr(StrState.SECOND); try { BeanValidator.check(param); Assert.fail(); From 0c8a120cabaadef1099265b8278ff1f8e24fedb1 Mon Sep 17 00:00:00 2001 From: kcp Date: Fri, 11 Jun 2021 21:19:17 +0800 Subject: [PATCH 197/476] +) detail optimize --- .../kuangcp/reference/StackReferenceTest.java | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 class/src/test/java/com/github/kuangcp/reference/StackReferenceTest.java diff --git a/class/src/test/java/com/github/kuangcp/reference/StackReferenceTest.java b/class/src/test/java/com/github/kuangcp/reference/StackReferenceTest.java new file mode 100644 index 00000000..9691bb31 --- /dev/null +++ b/class/src/test/java/com/github/kuangcp/reference/StackReferenceTest.java @@ -0,0 +1,32 @@ +package com.github.kuangcp.reference; + +import org.apache.commons.lang3.time.StopWatch; +import org.junit.Test; + +/** + * @author kuangcp + */ +public class StackReferenceTest { + + @Test + public void testAvoidStackReference() { + StopWatch started = StopWatch.createStarted(); + for (int i = 0; i < 1000000; i++) { + Apple apple = new Apple("who"); + apple.toString(); // avoid jvm delete + } + started.stop(); + System.out.println(started); + + // 减少栈对象 + Apple apple; + started = StopWatch.createStarted(); + for (int i = 0; i < 1000000; i++) { + apple = new Apple("who"); + apple.toString(); + } + started.stop(); + System.out.println(started); + } + +} From 7a37b320ef842ec06ed0c06f67f74bf8f634fb4d Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Thu, 24 Jun 2021 01:21:28 +0800 Subject: [PATCH 198/476] -) remove dep --- build.gradle | 1 - netty/src/main/resources/logback.xml | 17 +++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 netty/src/main/resources/logback.xml diff --git a/build.gradle b/build.gradle index ce4fdfc8..f2fe6e60 100644 --- a/build.gradle +++ b/build.gradle @@ -49,7 +49,6 @@ subprojects { testCompileOnly libs['lombok'] implementation libs['logback-classic'] - implementation libs['kcp-tool'] testImplementation libs['junit'] testImplementation libs['hamcrest-core'] diff --git a/netty/src/main/resources/logback.xml b/netty/src/main/resources/logback.xml new file mode 100644 index 00000000..d5538e4b --- /dev/null +++ b/netty/src/main/resources/logback.xml @@ -0,0 +1,17 @@ + + + + + + + %d{HH:mm:ss.SSS} %highlight(%-5level) [%thread] %logger{30} %highlight(%M):%yellow(%-3L) %msg%n + + + INFO + + + + + + + \ No newline at end of file From 715806b78961eba27859b4a101880da09ae1f581 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Sun, 11 Jul 2021 20:30:17 +0800 Subject: [PATCH 199/476] +) manual sharding table --- .../java/com/github/kuangcp/Application.java | 2 +- .../kuangcp/customer/domain/Customer.java | 19 --- .../kuangcp/sharding/manual/AuthUtil.java | 24 +++ .../manual/ConsistentHashingAlgorithm.java | 94 ++++++++++++ .../manual/ShardingAlgorithmEnum.java | 46 ++++++ .../manual/ShardingAlgorithmType.java | 9 ++ .../sharding/manual/ShardingTable.java | 17 +++ .../sharding/manual/SharedInterceptor.java | 137 ++++++++++++++++++ .../customer/dao/CustomerDao.java | 7 +- .../simple/customer/domain/Customer.java | 22 +++ .../{ => simple}/order/dao/OrderDao.java | 4 +- .../{ => simple}/order/domain/Order.java | 2 +- .../{ => simple}/order/dto/OrderDTO.java | 2 +- .../order/service/OrderService.java | 4 +- .../order/service/impl/OrderServiceImpl.java | 23 +-- mybatis/src/main/resources/logback.xml | 79 ++++++++++ .../kuangcp/customer/dao/CustomerDaoTest.java | 36 ----- .../ConsistentHashingAlgorithmTest.java | 57 ++++++++ .../simple/customer/dao/CustomerDaoTest.java | 44 ++++++ .../{ => simple}/order/dao/OrderDaoTest.java | 14 +- .../order/service/OrderServiceTest.java | 12 +- .../{ => simple}/customer/domain/Person.java | 2 +- 22 files changed, 564 insertions(+), 92 deletions(-) delete mode 100644 mybatis/src/main/java/com/github/kuangcp/customer/domain/Customer.java create mode 100644 mybatis/src/main/java/com/github/kuangcp/sharding/manual/AuthUtil.java create mode 100644 mybatis/src/main/java/com/github/kuangcp/sharding/manual/ConsistentHashingAlgorithm.java create mode 100644 mybatis/src/main/java/com/github/kuangcp/sharding/manual/ShardingAlgorithmEnum.java create mode 100644 mybatis/src/main/java/com/github/kuangcp/sharding/manual/ShardingAlgorithmType.java create mode 100644 mybatis/src/main/java/com/github/kuangcp/sharding/manual/ShardingTable.java create mode 100644 mybatis/src/main/java/com/github/kuangcp/sharding/manual/SharedInterceptor.java rename mybatis/src/main/java/com/github/kuangcp/{ => simple}/customer/dao/CustomerDao.java (79%) create mode 100644 mybatis/src/main/java/com/github/kuangcp/simple/customer/domain/Customer.java rename mybatis/src/main/java/com/github/kuangcp/{ => simple}/order/dao/OrderDao.java (64%) rename mybatis/src/main/java/com/github/kuangcp/{ => simple}/order/domain/Order.java (92%) rename mybatis/src/main/java/com/github/kuangcp/{ => simple}/order/dto/OrderDTO.java (92%) rename mybatis/src/main/java/com/github/kuangcp/{ => simple}/order/service/OrderService.java (60%) rename mybatis/src/main/java/com/github/kuangcp/{ => simple}/order/service/impl/OrderServiceImpl.java (84%) create mode 100644 mybatis/src/main/resources/logback.xml delete mode 100644 mybatis/src/test/java/com/github/kuangcp/customer/dao/CustomerDaoTest.java create mode 100644 mybatis/src/test/java/com/github/kuangcp/sharding/manual/ConsistentHashingAlgorithmTest.java create mode 100644 mybatis/src/test/java/com/github/kuangcp/simple/customer/dao/CustomerDaoTest.java rename mybatis/src/test/java/com/github/kuangcp/{ => simple}/order/dao/OrderDaoTest.java (87%) rename mybatis/src/test/java/com/github/kuangcp/{ => simple}/order/service/OrderServiceTest.java (63%) rename test/src/main/java/com/github/kuangcp/{ => simple}/customer/domain/Person.java (74%) diff --git a/mybatis/src/main/java/com/github/kuangcp/Application.java b/mybatis/src/main/java/com/github/kuangcp/Application.java index c5675c5c..1c97c1d3 100644 --- a/mybatis/src/main/java/com/github/kuangcp/Application.java +++ b/mybatis/src/main/java/com/github/kuangcp/Application.java @@ -4,7 +4,7 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -@MapperScan("com.github.kuangcp.*.dao") +@MapperScan("com.github.kuangcp.**.dao") @SpringBootApplication() public class Application { diff --git a/mybatis/src/main/java/com/github/kuangcp/customer/domain/Customer.java b/mybatis/src/main/java/com/github/kuangcp/customer/domain/Customer.java deleted file mode 100644 index a2f4530f..00000000 --- a/mybatis/src/main/java/com/github/kuangcp/customer/domain/Customer.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.github.kuangcp.customer.domain; - -import com.baomidou.mybatisplus.annotation.TableName; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -@TableName("customer") -public class Customer { - - private Long id; - private String name; - private Integer nation; -} diff --git a/mybatis/src/main/java/com/github/kuangcp/sharding/manual/AuthUtil.java b/mybatis/src/main/java/com/github/kuangcp/sharding/manual/AuthUtil.java new file mode 100644 index 00000000..9cfa1288 --- /dev/null +++ b/mybatis/src/main/java/com/github/kuangcp/sharding/manual/AuthUtil.java @@ -0,0 +1,24 @@ +package com.github.kuangcp.sharding.manual; + +import org.springframework.stereotype.Component; + +/** + * @author https://github.com/kuangcp on 2021-07-11 18:53 + */ +@Component +public class AuthUtil { + + private final ThreadLocal orgIdLocal = new ThreadLocal<>(); + + public void completeAuth(Long orgId) { + orgIdLocal.set(orgId); + } + + public Long getAuthedOrgId() { + return orgIdLocal.get(); + } + + public void clearAuth() { + orgIdLocal.remove(); + } +} diff --git a/mybatis/src/main/java/com/github/kuangcp/sharding/manual/ConsistentHashingAlgorithm.java b/mybatis/src/main/java/com/github/kuangcp/sharding/manual/ConsistentHashingAlgorithm.java new file mode 100644 index 00000000..e988184b --- /dev/null +++ b/mybatis/src/main/java/com/github/kuangcp/sharding/manual/ConsistentHashingAlgorithm.java @@ -0,0 +1,94 @@ +package com.github.kuangcp.sharding.manual; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.*; + +/** + * @author https://github.com/kuangcp on 2021-07-11 19:13 + */ +public class ConsistentHashingAlgorithm { + private MessageDigest md5; + private final int numberOfReplicas = 300; + + private final SortedMap circle = new TreeMap<>(); + private static final Map cache = new HashMap<>(); + + public ConsistentHashingAlgorithm(int totalNode) { + for (int i = 1; i <= totalNode; i++) { + add("_" + i); + } + } + + public static ConsistentHashingAlgorithm useWithCache(int totalNode) { + ConsistentHashingAlgorithm result = cache.get(totalNode); + if (Objects.nonNull(result)) { + return result; + } + ConsistentHashingAlgorithm newOne = new ConsistentHashingAlgorithm(totalNode); + cache.put(totalNode, newOne); + return newOne; + } + + /** + * 添加虚拟节点 + * numberOfReplicas为虚拟节点的数量 + * 例如初始化hash环的时候传入,我们使用300个虚拟节点 + */ + public void add(String node) { + for (int i = 0; i < numberOfReplicas; i++) { + circle.put(this.hash(node.toString() + "_" + i), node); + } + } + + /** + * 移除节点 + */ + public void remove(String node) { + for (int i = 0; i < numberOfReplicas; i++) { + circle.remove(this.hash(node.toString() + i)); + } + } + + public String get(Long key) { + return get(key + ""); + } + + /** + * 获得一个最近的顺时针节点 + * + * @param key 为给定键取Hash,取得顺时针方向上最近的一个虚拟节点对应的实际节点 + */ + public String get(String key) { + if (circle.isEmpty()) { + return null; + } + long hash = this.hash(key); + if (!circle.containsKey(hash)) { + //返回此映射的部分视图,其键大于等于 hash + SortedMap tailMap = circle.tailMap(hash); + hash = tailMap.isEmpty() ? circle.firstKey() : tailMap.firstKey(); + } + return circle.get(hash); + } + + public long hash(String key) { + if (md5 == null) { + try { + md5 = MessageDigest.getInstance("MD5"); + } catch (NoSuchAlgorithmException e) { + throw new IllegalStateException("no md5 algorythm found"); + } + } + + md5.reset(); + md5.update(key.getBytes()); + byte[] bKey = md5.digest(); + + long res = ((long) (bKey[3] & 0xFF) << 24) | + ((long) (bKey[2] & 0xFF) << 16) | + ((long) (bKey[1] & 0xFF) << 8) | + (long) (bKey[0] & 0xFF); + return res & 0xffffffffL; + } +} diff --git a/mybatis/src/main/java/com/github/kuangcp/sharding/manual/ShardingAlgorithmEnum.java b/mybatis/src/main/java/com/github/kuangcp/sharding/manual/ShardingAlgorithmEnum.java new file mode 100644 index 00000000..70aa3726 --- /dev/null +++ b/mybatis/src/main/java/com/github/kuangcp/sharding/manual/ShardingAlgorithmEnum.java @@ -0,0 +1,46 @@ +package com.github.kuangcp.sharding.manual; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Objects; +import java.util.Optional; +import java.util.function.BiFunction; + +/** + * @author https://github.com/kuangcp on 2021-07-11 18:41 + */ +@Getter +@AllArgsConstructor +public enum ShardingAlgorithmEnum { + MOD(ShardingAlgorithmType.MOD, (shardingVal, totalSlice) -> { + long index = shardingVal % totalSlice; + return "_" + index; + }), + + CONSISTENT_HASHING(ShardingAlgorithmType.CONSISTENT_HASHING, + (shardingVal, totalCount) -> ConsistentHashingAlgorithm.useWithCache(totalCount).get(shardingVal)), + ; + + private final int type; + /** + * shardingVal 用于分片的值 + * totalSlice 总分片数 + * suffix 子表后缀 + */ + private final BiFunction func; + + + public static Optional of(Integer type) { + if (Objects.isNull(type)) { + return Optional.empty(); + } + for (ShardingAlgorithmEnum value : values()) { + if (Objects.equals(value.type, type)) { + return Optional.of(value); + } + } + return Optional.empty(); + } + +} diff --git a/mybatis/src/main/java/com/github/kuangcp/sharding/manual/ShardingAlgorithmType.java b/mybatis/src/main/java/com/github/kuangcp/sharding/manual/ShardingAlgorithmType.java new file mode 100644 index 00000000..d90db68f --- /dev/null +++ b/mybatis/src/main/java/com/github/kuangcp/sharding/manual/ShardingAlgorithmType.java @@ -0,0 +1,9 @@ +package com.github.kuangcp.sharding.manual; + +/** + * @author https://github.com/kuangcp on 2021-07-11 18:59 + */ +public interface ShardingAlgorithmType { + int MOD = 1; + int CONSISTENT_HASHING = 2; +} diff --git a/mybatis/src/main/java/com/github/kuangcp/sharding/manual/ShardingTable.java b/mybatis/src/main/java/com/github/kuangcp/sharding/manual/ShardingTable.java new file mode 100644 index 00000000..e2e8e78f --- /dev/null +++ b/mybatis/src/main/java/com/github/kuangcp/sharding/manual/ShardingTable.java @@ -0,0 +1,17 @@ +package com.github.kuangcp.sharding.manual; + +import java.lang.annotation.*; + +/** + * + * @author https://github.com/kuangcp on 2021-07-11 18:19 + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface ShardingTable { + + int algorithm(); + + int tableCount(); +} diff --git a/mybatis/src/main/java/com/github/kuangcp/sharding/manual/SharedInterceptor.java b/mybatis/src/main/java/com/github/kuangcp/sharding/manual/SharedInterceptor.java new file mode 100644 index 00000000..49e954dd --- /dev/null +++ b/mybatis/src/main/java/com/github/kuangcp/sharding/manual/SharedInterceptor.java @@ -0,0 +1,137 @@ +package com.github.kuangcp.sharding.manual; + +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.extern.slf4j.Slf4j; +import org.apache.ibatis.executor.statement.RoutingStatementHandler; +import org.apache.ibatis.executor.statement.StatementHandler; +import org.apache.ibatis.mapping.BoundSql; +import org.apache.ibatis.mapping.MappedStatement; +import org.apache.ibatis.plugin.*; +import org.apache.ibatis.reflection.MetaObject; +import org.apache.ibatis.reflection.SystemMetaObject; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.sql.Connection; +import java.util.Properties; + +/** + * @author https://github.com/kuangcp on 2021-07-11 18:19 + */ +@Slf4j +@Component +@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", + args = {Connection.class, Integer.class})}) +public class SharedInterceptor implements Interceptor { + + @Autowired + private AuthUtil authUtil; + + @Override + public Object intercept(Invocation invocation) throws Throwable { + MetaObject metaObject; + MappedStatement mappedStatement; + Object target = invocation.getTarget(); + + if (!(target instanceof RoutingStatementHandler)) { + return invocation.proceed(); + } + RoutingStatementHandler routingStatementHandler = (RoutingStatementHandler) target; + metaObject = SystemMetaObject.forObject(routingStatementHandler); + StatementHandler statementHandler = (StatementHandler) metaObject.getValue("delegate"); + metaObject = SystemMetaObject.forObject(statementHandler); + + mappedStatement = (MappedStatement) metaObject.getValue("mappedStatement"); + BoundSql boundSql = statementHandler.getBoundSql(); + //获取对应的Mapper类 + Class mapperClass = Class.forName(mappedStatement.getId().substring(0, mappedStatement.getId().lastIndexOf("."))); + //获取对应EO + Class eoClass = getEoClass(mapperClass); + if (eoClass.isAnnotationPresent(ShardingTable.class) && eoClass.isAnnotationPresent(TableName.class)) { + String logicTable = eoClass.getAnnotation(TableName.class).value(); + ShardingTable rdsSharding = eoClass.getAnnotation(ShardingTable.class); + int algorithm = rdsSharding.algorithm(); + int tableTotal = rdsSharding.tableCount(); + + Long orgId = authUtil.getAuthedOrgId(); + + // 统一使用 组织id 分表或者不分表 + String subTableName = ShardingAlgorithmEnum.of(algorithm) + .map(ShardingAlgorithmEnum::getFunc) + .map(v -> v.apply(orgId, tableTotal)) + .map(v -> logicTable + v) + .orElse(logicTable); + + if (StringUtils.isEmpty(subTableName)) { + log.error("Unable to obtain subTableName , exec canceled. caseby: {} splitKey's value is null", logicTable); + } else { + String sql = boundSql.getSql(); + //将表名替换为子表名 + sql = sql.replaceAll(logicTable, subTableName); + + metaObject = SystemMetaObject.forObject(boundSql); + metaObject.setValue("sql", sql); + } + } + + return invocation.proceed(); + } + + + @Override + public Object plugin(Object target) { + // TODO Auto-generated method stub + if (target instanceof StatementHandler) { + return Plugin.wrap(target, this); + } else { + return target; + } + } + + @Override + public void setProperties(Properties properties) { + + } + + /** + * 获取Eo class + * + * @param eoMapper + * @return + */ + private Class getEoClass(Class eoMapper) { + Class entityClass = getGenericClass(eoMapper); + if (entityClass != null) { + String eoName = entityClass.getPackage().getName() + "." + StringUtils.delete(entityClass.getSimpleName(), "Eo") + "ExtEo"; + + try { + Class extClass = Class.forName(eoName); + entityClass = extClass; + } catch (ClassNotFoundException exception) { + } + } + + return entityClass; + } + + /** + * 获取接口的泛型类型,如果不存在则返回null + * + * @param clazz + * @return + */ + private Class getGenericClass(Class clazz) { + Type t = clazz.getGenericSuperclass(); + if (t == null) { + t = clazz.getGenericInterfaces()[0]; + } + if (t instanceof ParameterizedType) { + Type[] p = ((ParameterizedType) t).getActualTypeArguments(); + return ((Class) p[0]); + } + return null; + } +} diff --git a/mybatis/src/main/java/com/github/kuangcp/customer/dao/CustomerDao.java b/mybatis/src/main/java/com/github/kuangcp/simple/customer/dao/CustomerDao.java similarity index 79% rename from mybatis/src/main/java/com/github/kuangcp/customer/dao/CustomerDao.java rename to mybatis/src/main/java/com/github/kuangcp/simple/customer/dao/CustomerDao.java index fed16137..7ae11c76 100644 --- a/mybatis/src/main/java/com/github/kuangcp/customer/dao/CustomerDao.java +++ b/mybatis/src/main/java/com/github/kuangcp/simple/customer/dao/CustomerDao.java @@ -1,12 +1,13 @@ -package com.github.kuangcp.customer.dao; +package com.github.kuangcp.simple.customer.dao; import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import com.github.kuangcp.customer.domain.Customer; -import java.util.List; +import com.github.kuangcp.simple.customer.domain.Customer; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Select; import org.springframework.stereotype.Repository; +import java.util.List; + @Repository public interface CustomerDao extends BaseMapper { diff --git a/mybatis/src/main/java/com/github/kuangcp/simple/customer/domain/Customer.java b/mybatis/src/main/java/com/github/kuangcp/simple/customer/domain/Customer.java new file mode 100644 index 00000000..0affe32e --- /dev/null +++ b/mybatis/src/main/java/com/github/kuangcp/simple/customer/domain/Customer.java @@ -0,0 +1,22 @@ +package com.github.kuangcp.simple.customer.domain; + +import com.baomidou.mybatisplus.annotation.TableName; +import com.github.kuangcp.sharding.manual.ShardingAlgorithmType; +import com.github.kuangcp.sharding.manual.ShardingTable; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@TableName("customer") +@ShardingTable(algorithm = ShardingAlgorithmType.MOD, tableCount = 6) +public class Customer { + + private Long id; + private String name; + private Integer nation; +} diff --git a/mybatis/src/main/java/com/github/kuangcp/order/dao/OrderDao.java b/mybatis/src/main/java/com/github/kuangcp/simple/order/dao/OrderDao.java similarity index 64% rename from mybatis/src/main/java/com/github/kuangcp/order/dao/OrderDao.java rename to mybatis/src/main/java/com/github/kuangcp/simple/order/dao/OrderDao.java index e7db06f6..c5024ff8 100644 --- a/mybatis/src/main/java/com/github/kuangcp/order/dao/OrderDao.java +++ b/mybatis/src/main/java/com/github/kuangcp/simple/order/dao/OrderDao.java @@ -1,7 +1,7 @@ -package com.github.kuangcp.order.dao; +package com.github.kuangcp.simple.order.dao; import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import com.github.kuangcp.order.domain.Order; +import com.github.kuangcp.simple.order.domain.Order; import org.springframework.stereotype.Repository; @Repository diff --git a/mybatis/src/main/java/com/github/kuangcp/order/domain/Order.java b/mybatis/src/main/java/com/github/kuangcp/simple/order/domain/Order.java similarity index 92% rename from mybatis/src/main/java/com/github/kuangcp/order/domain/Order.java rename to mybatis/src/main/java/com/github/kuangcp/simple/order/domain/Order.java index e79add5c..2836c24d 100644 --- a/mybatis/src/main/java/com/github/kuangcp/order/domain/Order.java +++ b/mybatis/src/main/java/com/github/kuangcp/simple/order/domain/Order.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.order.domain; +package com.github.kuangcp.simple.order.domain; import com.baomidou.mybatisplus.annotation.TableName; import java.math.BigDecimal; diff --git a/mybatis/src/main/java/com/github/kuangcp/order/dto/OrderDTO.java b/mybatis/src/main/java/com/github/kuangcp/simple/order/dto/OrderDTO.java similarity index 92% rename from mybatis/src/main/java/com/github/kuangcp/order/dto/OrderDTO.java rename to mybatis/src/main/java/com/github/kuangcp/simple/order/dto/OrderDTO.java index e0c7d8b4..652ba8a6 100644 --- a/mybatis/src/main/java/com/github/kuangcp/order/dto/OrderDTO.java +++ b/mybatis/src/main/java/com/github/kuangcp/simple/order/dto/OrderDTO.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.order.dto; +package com.github.kuangcp.simple.order.dto; import java.math.BigDecimal; import java.time.LocalDateTime; import lombok.AllArgsConstructor; diff --git a/mybatis/src/main/java/com/github/kuangcp/order/service/OrderService.java b/mybatis/src/main/java/com/github/kuangcp/simple/order/service/OrderService.java similarity index 60% rename from mybatis/src/main/java/com/github/kuangcp/order/service/OrderService.java rename to mybatis/src/main/java/com/github/kuangcp/simple/order/service/OrderService.java index f0f445ba..7991fb58 100644 --- a/mybatis/src/main/java/com/github/kuangcp/order/service/OrderService.java +++ b/mybatis/src/main/java/com/github/kuangcp/simple/order/service/OrderService.java @@ -1,6 +1,6 @@ -package com.github.kuangcp.order.service; +package com.github.kuangcp.simple.order.service; -import com.github.kuangcp.order.dto.OrderDTO; +import com.github.kuangcp.simple.order.dto.OrderDTO; import java.util.List; public interface OrderService { diff --git a/mybatis/src/main/java/com/github/kuangcp/order/service/impl/OrderServiceImpl.java b/mybatis/src/main/java/com/github/kuangcp/simple/order/service/impl/OrderServiceImpl.java similarity index 84% rename from mybatis/src/main/java/com/github/kuangcp/order/service/impl/OrderServiceImpl.java rename to mybatis/src/main/java/com/github/kuangcp/simple/order/service/impl/OrderServiceImpl.java index ff72ae37..a9ee630a 100644 --- a/mybatis/src/main/java/com/github/kuangcp/order/service/impl/OrderServiceImpl.java +++ b/mybatis/src/main/java/com/github/kuangcp/simple/order/service/impl/OrderServiceImpl.java @@ -1,20 +1,21 @@ -package com.github.kuangcp.order.service.impl; +package com.github.kuangcp.simple.order.service.impl; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; -import com.github.kuangcp.order.dao.OrderDao; -import com.github.kuangcp.order.domain.Order; -import com.github.kuangcp.order.dto.OrderDTO; -import com.github.kuangcp.order.service.OrderService; -import com.github.kuangcp.customer.dao.CustomerDao; -import com.github.kuangcp.customer.domain.Customer; +import com.github.kuangcp.simple.customer.dao.CustomerDao; +import com.github.kuangcp.simple.customer.domain.Customer; +import com.github.kuangcp.simple.order.dao.OrderDao; +import com.github.kuangcp.simple.order.domain.Order; +import com.github.kuangcp.simple.order.dto.OrderDTO; +import com.github.kuangcp.simple.order.service.OrderService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.function.Function; import java.util.stream.Collectors; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; @Service public class OrderServiceImpl implements OrderService { @@ -27,14 +28,14 @@ public class OrderServiceImpl implements OrderService { @Override public List queryByUserIdWithLoop(Long userId) { - List orders = orderDao.selectList(new QueryWrapper().eq("user_id", 200)); + List orders = orderDao.selectList(new QueryWrapper().eq("user_id", userId)); return orders.stream().map(this::convert).collect(Collectors.toList()); } @Override public List queryByUserId(Long userId) { - List orders = orderDao.selectList(new QueryWrapper().eq("user_id", 200)); + List orders = orderDao.selectList(new QueryWrapper().eq("user_id", userId)); Set idSet = orders.stream().map(Order::getUserId).collect(Collectors.toSet()); Map userMap = customerDao.selectBatchIds(idSet).stream() diff --git a/mybatis/src/main/resources/logback.xml b/mybatis/src/main/resources/logback.xml new file mode 100644 index 00000000..cb8ba4cc --- /dev/null +++ b/mybatis/src/main/resources/logback.xml @@ -0,0 +1,79 @@ + + + + + + + + + %d{HH:mm:ss.SSS} %highlight(%-5level) [%thread] %logger{30} %highlight(%M):%yellow(%-3L) %msg%n + + + INFO + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/mybatis/src/test/java/com/github/kuangcp/customer/dao/CustomerDaoTest.java b/mybatis/src/test/java/com/github/kuangcp/customer/dao/CustomerDaoTest.java deleted file mode 100644 index 4891caa2..00000000 --- a/mybatis/src/test/java/com/github/kuangcp/customer/dao/CustomerDaoTest.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.github.kuangcp.customer.dao; - -import com.github.kuangcp.base.TestStarter; -import com.github.kuangcp.customer.domain.Customer; -import com.github.kuangcp.mock.common.MockUsuallyValue; -import java.util.List; -import java.util.stream.LongStream; -import lombok.extern.slf4j.Slf4j; -import org.junit.Test; -import org.springframework.beans.factory.annotation.Autowired; - -@Slf4j -public class CustomerDaoTest extends TestStarter { - - @Autowired - private CustomerDao customerDao; - - @Test - public void testQuery() { - customerDao.insert(Customer.builder().name("myth").build()); - List customers = customerDao.queryByName("myth"); - - customers.forEach(item -> log.info("item={}", item)); - } - - @Test - public void testBulkInsert() { - - LongStream.rangeClosed(1, 1000) - .mapToObj(i -> Customer.builder().id(i) - .id(i) - .nation(MockUsuallyValue.mock(Integer.class)) - .build()) - .forEach(customerDao::insert); - } -} \ No newline at end of file diff --git a/mybatis/src/test/java/com/github/kuangcp/sharding/manual/ConsistentHashingAlgorithmTest.java b/mybatis/src/test/java/com/github/kuangcp/sharding/manual/ConsistentHashingAlgorithmTest.java new file mode 100644 index 00000000..8b474b9b --- /dev/null +++ b/mybatis/src/test/java/com/github/kuangcp/sharding/manual/ConsistentHashingAlgorithmTest.java @@ -0,0 +1,57 @@ +package com.github.kuangcp.sharding.manual; + +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; + +import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; + +/** + * @author https://github.com/kuangcp on 2021-07-11 19:32 + */ +@Slf4j +public class ConsistentHashingAlgorithmTest { + + private Map userMap = new HashMap<>(); + + /** + * 一致性hash算法优势在于 新增删除节点,原始数据迁移较少 + */ + @Test + public void testCompareMoveData() { + for (ShardingAlgorithmEnum func : ShardingAlgorithmEnum.values()) { + log.info("start: func={}", func); + this.userMap = new HashMap<>(); + for (int i = 2; i < 30; i += 3) { + calculatePerIndex(func, i, (int) Math.pow(2, i) * 20); +// calculatePerIndex(func, i, i * 3); + } + } + } + + private void calculatePerIndex(ShardingAlgorithmEnum func, int totalSlice, int totalData) { + Map newUserMap = new HashMap<>(); + for (int i = 0; i < totalData; i++) { + String finalTable = func.getFunc().apply((long) i, totalSlice); +// System.out.println(i + " " + finalTable); + newUserMap.put(i, finalTable); + } + long changCount = userMap.entrySet().stream().filter(v -> { + String newIndex = newUserMap.get(v.getKey()); + return !Objects.equals(newIndex, v.getValue()); + }).count(); + + this.userMap = newUserMap; + Map>> tableMap = userMap.entrySet() + .stream().collect(Collectors.groupingBy(Map.Entry::getValue)); + + AtomicInteger counter = new AtomicInteger(); + tableMap.entrySet().stream() + .sorted(Map.Entry.comparingByKey()).forEach(e -> { + counter.addAndGet(e.getValue().size()); + System.out.println(e.getKey() + ":" + e.getValue().size()); + }); + log.info("{}: count={} changeCount={}", func.name(), counter.get(), changCount); + } +} \ No newline at end of file diff --git a/mybatis/src/test/java/com/github/kuangcp/simple/customer/dao/CustomerDaoTest.java b/mybatis/src/test/java/com/github/kuangcp/simple/customer/dao/CustomerDaoTest.java new file mode 100644 index 00000000..c70c49f7 --- /dev/null +++ b/mybatis/src/test/java/com/github/kuangcp/simple/customer/dao/CustomerDaoTest.java @@ -0,0 +1,44 @@ +package com.github.kuangcp.simple.customer.dao; + +import com.github.kuangcp.base.TestStarter; +import com.github.kuangcp.sharding.manual.AuthUtil; +import com.github.kuangcp.simple.customer.domain.Customer; +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.List; +import java.util.stream.LongStream; + +@Slf4j +public class CustomerDaoTest extends TestStarter { + + @Autowired + private CustomerDao customerDao; + @Autowired + private AuthUtil authUtil; + + private void login(Long orgId){ + authUtil.clearAuth(); + authUtil.completeAuth(orgId); + } + @Test + public void testQuery() { + login(2L); + customerDao.insert(Customer.builder().id(1L).name("myth").build()); + List customers = customerDao.queryByName("myth"); + + customers.forEach(item -> log.info("item={}", item)); + } + + @Test + public void testBulkInsert() { + + LongStream.rangeClosed(1, 1000) + .mapToObj(i -> Customer.builder().id(i) + .id(i) + .nation(3) + .build()) + .forEach(customerDao::insert); + } +} \ No newline at end of file diff --git a/mybatis/src/test/java/com/github/kuangcp/order/dao/OrderDaoTest.java b/mybatis/src/test/java/com/github/kuangcp/simple/order/dao/OrderDaoTest.java similarity index 87% rename from mybatis/src/test/java/com/github/kuangcp/order/dao/OrderDaoTest.java rename to mybatis/src/test/java/com/github/kuangcp/simple/order/dao/OrderDaoTest.java index ccd09706..282ef092 100644 --- a/mybatis/src/test/java/com/github/kuangcp/order/dao/OrderDaoTest.java +++ b/mybatis/src/test/java/com/github/kuangcp/simple/order/dao/OrderDaoTest.java @@ -1,16 +1,16 @@ -package com.github.kuangcp.order.dao; +package com.github.kuangcp.simple.order.dao; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.github.kuangcp.base.TestStarter; -import com.github.kuangcp.mock.common.MockUsuallyValue; -import com.github.kuangcp.order.domain.Order; +import com.github.kuangcp.simple.order.domain.Order; +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; + import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; import java.util.function.Consumer; -import lombok.extern.slf4j.Slf4j; -import org.junit.Test; -import org.springframework.beans.factory.annotation.Autowired; @Slf4j public class OrderDaoTest extends TestStarter { @@ -35,7 +35,7 @@ public void testQuery() { public void testBulkInsert() throws InterruptedException { Consumer consumer = start -> { - Order temp = Order.builder().num(MockUsuallyValue.mock(Integer.class)) + Order temp = Order.builder().num(44) .createTime(LocalDateTime.now()).build(); for (int i = 0; i < 10000; i++) { long id = (start + i); diff --git a/mybatis/src/test/java/com/github/kuangcp/order/service/OrderServiceTest.java b/mybatis/src/test/java/com/github/kuangcp/simple/order/service/OrderServiceTest.java similarity index 63% rename from mybatis/src/test/java/com/github/kuangcp/order/service/OrderServiceTest.java rename to mybatis/src/test/java/com/github/kuangcp/simple/order/service/OrderServiceTest.java index f90a80ba..15ee2e42 100644 --- a/mybatis/src/test/java/com/github/kuangcp/order/service/OrderServiceTest.java +++ b/mybatis/src/test/java/com/github/kuangcp/simple/order/service/OrderServiceTest.java @@ -1,8 +1,7 @@ -package com.github.kuangcp.order.service; +package com.github.kuangcp.simple.order.service; import com.github.kuangcp.base.TestStarter; -import com.github.kuangcp.order.dto.OrderDTO; -import com.github.kuangcp.time.GetRunTime; +import com.github.kuangcp.simple.order.dto.OrderDTO; import java.util.List; import lombok.extern.slf4j.Slf4j; import org.junit.Test; @@ -16,12 +15,9 @@ public class OrderServiceTest extends TestStarter { @Test public void testCompareQueryId() { - GetRunTime run = new GetRunTime().startCount(); List result = orderService.queryByUserIdWithLoop(200L); - run.endCountOneLine("loop"); - - run.startCount(); + log.info(": result={}", result); result = orderService.queryByUserId(200L); - run.endCountOneLine("best"); + log.info(": result={}", result); } } \ No newline at end of file diff --git a/test/src/main/java/com/github/kuangcp/customer/domain/Person.java b/test/src/main/java/com/github/kuangcp/simple/customer/domain/Person.java similarity index 74% rename from test/src/main/java/com/github/kuangcp/customer/domain/Person.java rename to test/src/main/java/com/github/kuangcp/simple/customer/domain/Person.java index d9cdc851..7449880b 100644 --- a/test/src/main/java/com/github/kuangcp/customer/domain/Person.java +++ b/test/src/main/java/com/github/kuangcp/simple/customer/domain/Person.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.customer.domain; +package com.github.kuangcp.simple.customer.domain; import lombok.Data; From de63ec888d6d0aa4c399e6b62a367c888c656831 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Sun, 11 Jul 2021 20:31:30 +0800 Subject: [PATCH 200/476] *) rename --- .../{SharedInterceptor.java => ShardingTableInterceptor.java} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename mybatis/src/main/java/com/github/kuangcp/sharding/manual/{SharedInterceptor.java => ShardingTableInterceptor.java} (98%) diff --git a/mybatis/src/main/java/com/github/kuangcp/sharding/manual/SharedInterceptor.java b/mybatis/src/main/java/com/github/kuangcp/sharding/manual/ShardingTableInterceptor.java similarity index 98% rename from mybatis/src/main/java/com/github/kuangcp/sharding/manual/SharedInterceptor.java rename to mybatis/src/main/java/com/github/kuangcp/sharding/manual/ShardingTableInterceptor.java index 49e954dd..9a4f3296 100644 --- a/mybatis/src/main/java/com/github/kuangcp/sharding/manual/SharedInterceptor.java +++ b/mybatis/src/main/java/com/github/kuangcp/sharding/manual/ShardingTableInterceptor.java @@ -25,7 +25,7 @@ @Component @Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})}) -public class SharedInterceptor implements Interceptor { +public class ShardingTableInterceptor implements Interceptor { @Autowired private AuthUtil authUtil; From 231f80133ff3d28d1c31b8981fbeb92f20889929 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Tue, 13 Jul 2021 01:14:43 +0800 Subject: [PATCH 201/476] +) compare stream and page query --- .../manual/ConsistentHashingAlgorithm.java | 6 + .../simple/customer/dao/CustomerDao.java | 13 ++- .../com/github/kuangcp/stream/Report.java | 69 +++++++++++ .../github/kuangcp/stream/dao/ReportDao.java | 42 +++++++ ...tarter.java => SpringBootTestStarter.java} | 2 +- ...st.java => CustomerDaoSpringBootTest.java} | 4 +- ...oTest.java => OrderDaoSpringBootTest.java} | 4 +- ...t.java => OrderServiceSpringBootTest.java} | 4 +- .../stream/CursorSessionSpringBootTest.java | 110 ++++++++++++++++++ 9 files changed, 245 insertions(+), 9 deletions(-) create mode 100644 mybatis/src/main/java/com/github/kuangcp/stream/Report.java create mode 100644 mybatis/src/main/java/com/github/kuangcp/stream/dao/ReportDao.java rename mybatis/src/test/java/com/github/kuangcp/base/{TestStarter.java => SpringBootTestStarter.java} (83%) rename mybatis/src/test/java/com/github/kuangcp/simple/customer/dao/{CustomerDaoTest.java => CustomerDaoSpringBootTest.java} (90%) rename mybatis/src/test/java/com/github/kuangcp/simple/order/dao/{OrderDaoTest.java => OrderDaoSpringBootTest.java} (92%) rename mybatis/src/test/java/com/github/kuangcp/simple/order/service/{OrderServiceTest.java => OrderServiceSpringBootTest.java} (81%) create mode 100644 mybatis/src/test/java/com/github/kuangcp/stream/CursorSessionSpringBootTest.java diff --git a/mybatis/src/main/java/com/github/kuangcp/sharding/manual/ConsistentHashingAlgorithm.java b/mybatis/src/main/java/com/github/kuangcp/sharding/manual/ConsistentHashingAlgorithm.java index e988184b..2d75d946 100644 --- a/mybatis/src/main/java/com/github/kuangcp/sharding/manual/ConsistentHashingAlgorithm.java +++ b/mybatis/src/main/java/com/github/kuangcp/sharding/manual/ConsistentHashingAlgorithm.java @@ -20,6 +20,9 @@ public ConsistentHashingAlgorithm(int totalNode) { } } + /** + * TODO 线程不安全,以及环的多节点维护问题 + */ public static ConsistentHashingAlgorithm useWithCache(int totalNode) { ConsistentHashingAlgorithm result = cache.get(totalNode); if (Objects.nonNull(result)) { @@ -72,6 +75,9 @@ public String get(String key) { return circle.get(hash); } + /** + * TODO 线程不安全 + */ public long hash(String key) { if (md5 == null) { try { diff --git a/mybatis/src/main/java/com/github/kuangcp/simple/customer/dao/CustomerDao.java b/mybatis/src/main/java/com/github/kuangcp/simple/customer/dao/CustomerDao.java index 7ae11c76..c1bc6513 100644 --- a/mybatis/src/main/java/com/github/kuangcp/simple/customer/dao/CustomerDao.java +++ b/mybatis/src/main/java/com/github/kuangcp/simple/customer/dao/CustomerDao.java @@ -2,8 +2,12 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.github.kuangcp.simple.customer.domain.Customer; +import org.apache.ibatis.annotations.Options; import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.ResultType; import org.apache.ibatis.annotations.Select; +import org.apache.ibatis.mapping.ResultSetType; +import org.apache.ibatis.session.ResultHandler; import org.springframework.stereotype.Repository; import java.util.List; @@ -11,6 +15,11 @@ @Repository public interface CustomerDao extends BaseMapper { - @Select({"select * from customer where name like #{name} "}) - List queryByName(@Param("name") String name); + @Select({"select * from customer where name like #{name} "}) + List queryByName(@Param("name") String name); + + @Select("SELECT * from customer") + @Options(resultSetType = ResultSetType.FORWARD_ONLY, fetchSize = Integer.MIN_VALUE) + @ResultType(Customer.class) + void streamQueryAll(ResultHandler handler); } diff --git a/mybatis/src/main/java/com/github/kuangcp/stream/Report.java b/mybatis/src/main/java/com/github/kuangcp/stream/Report.java new file mode 100644 index 00000000..74741b52 --- /dev/null +++ b/mybatis/src/main/java/com/github/kuangcp/stream/Report.java @@ -0,0 +1,69 @@ +package com.github.kuangcp.stream; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +import java.util.Date; + +/** + * @author https://github.com/kuangcp on 2021-07-12 23:50 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = false) +@TableName("pq_inquiry_parts_report") +public class Report { + private static final long serialVersionUID = 1L; + + /** + * id + */ + @TableId + private Long id; + + /** + * 用户id + */ + private Long userId; + /** + * 询价编码 + */ + private String inquiryCode; + /** + * 询价次数 + */ + private Integer inquiryCount; + /** + * 有货报出数 + */ + private Integer haveGoodsCount; + /** + * 采购次数 + */ + private Integer purchaseCount; + /** + * 统计日期 + */ + private Date statisticsTime; + /** + * 创建时间 + */ + private Date createTime; + /** + * 更新时间 + */ + private Date updateTime; + + public static final String ID = "id"; + public static final String USER_ID = "user_id"; + public static final String INQUIRY_CODE = "inquiry_code"; + public static final String INQUIRY_COUNT = "inquiry_count"; + public static final String HAVE_GOODS_COUNT = "have_goods_count"; + public static final String PURCHASE_COUNT = "purchase_count"; + public static final String STATISTICS_TIME = "statistics_time"; + public static final String CREATE_TIME = "create_time"; + public static final String UPDATE_TIME = "update_time"; +} diff --git a/mybatis/src/main/java/com/github/kuangcp/stream/dao/ReportDao.java b/mybatis/src/main/java/com/github/kuangcp/stream/dao/ReportDao.java new file mode 100644 index 00000000..f67835d0 --- /dev/null +++ b/mybatis/src/main/java/com/github/kuangcp/stream/dao/ReportDao.java @@ -0,0 +1,42 @@ +package com.github.kuangcp.stream.dao; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.github.kuangcp.stream.Report; +import org.apache.ibatis.annotations.Options; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.ResultType; +import org.apache.ibatis.annotations.Select; +import org.apache.ibatis.mapping.ResultSetType; +import org.apache.ibatis.session.ResultHandler; +import org.springframework.stereotype.Repository; + +import java.util.Date; +import java.util.List; + +/** + * @author https://github.com/kuangcp on 2021-07-12 23:52 + */ +@Repository +public interface ReportDao extends BaseMapper { + + @Select("SELECT user_id, inquiry_code, SUM(inquiry_count) AS inquiry_count" + + ", SUM(have_goods_count) AS have_goods_count, SUM(purchase_count) AS purchase_count" + + " FROM pq_inquiry_parts_report WHERE statistics_time BETWEEN #{start} AND #{end}" + + " AND user_id IN (1,2,3,4,5,6)" + + " GROUP BY inquiry_code") + @Options(resultSetType = ResultSetType.FORWARD_ONLY, fetchSize = Integer.MIN_VALUE) + @ResultType(Report.class) + void selectAutoList(@Param("start") Date start, + @Param("end") Date end, + ResultHandler handler); + + @Select("SELECT user_id, inquiry_code, SUM(inquiry_count) AS inquiry_count" + + ", SUM(have_goods_count) AS have_goods_count, SUM(purchase_count) AS purchase_count" + + " FROM pq_inquiry_parts_report WHERE statistics_time BETWEEN #{start} AND #{end}" + + " AND user_id IN (1,2,3,4,5,6)" + + " GROUP BY inquiry_code LIMIT #{startIdx},#{size}") + List queryByPage(@Param("start") Date start, + @Param("end") Date end, + @Param("startIdx") long startIdx, + @Param("size") long size); +} diff --git a/mybatis/src/test/java/com/github/kuangcp/base/TestStarter.java b/mybatis/src/test/java/com/github/kuangcp/base/SpringBootTestStarter.java similarity index 83% rename from mybatis/src/test/java/com/github/kuangcp/base/TestStarter.java rename to mybatis/src/test/java/com/github/kuangcp/base/SpringBootTestStarter.java index 6631022e..3235e510 100644 --- a/mybatis/src/test/java/com/github/kuangcp/base/TestStarter.java +++ b/mybatis/src/test/java/com/github/kuangcp/base/SpringBootTestStarter.java @@ -6,6 +6,6 @@ @SpringBootTest @RunWith(SpringRunner.class) -public abstract class TestStarter { +public abstract class SpringBootTestStarter { } diff --git a/mybatis/src/test/java/com/github/kuangcp/simple/customer/dao/CustomerDaoTest.java b/mybatis/src/test/java/com/github/kuangcp/simple/customer/dao/CustomerDaoSpringBootTest.java similarity index 90% rename from mybatis/src/test/java/com/github/kuangcp/simple/customer/dao/CustomerDaoTest.java rename to mybatis/src/test/java/com/github/kuangcp/simple/customer/dao/CustomerDaoSpringBootTest.java index c70c49f7..d9860679 100644 --- a/mybatis/src/test/java/com/github/kuangcp/simple/customer/dao/CustomerDaoTest.java +++ b/mybatis/src/test/java/com/github/kuangcp/simple/customer/dao/CustomerDaoSpringBootTest.java @@ -1,6 +1,6 @@ package com.github.kuangcp.simple.customer.dao; -import com.github.kuangcp.base.TestStarter; +import com.github.kuangcp.base.SpringBootTestStarter; import com.github.kuangcp.sharding.manual.AuthUtil; import com.github.kuangcp.simple.customer.domain.Customer; import lombok.extern.slf4j.Slf4j; @@ -11,7 +11,7 @@ import java.util.stream.LongStream; @Slf4j -public class CustomerDaoTest extends TestStarter { +public class CustomerDaoSpringBootTest extends SpringBootTestStarter { @Autowired private CustomerDao customerDao; diff --git a/mybatis/src/test/java/com/github/kuangcp/simple/order/dao/OrderDaoTest.java b/mybatis/src/test/java/com/github/kuangcp/simple/order/dao/OrderDaoSpringBootTest.java similarity index 92% rename from mybatis/src/test/java/com/github/kuangcp/simple/order/dao/OrderDaoTest.java rename to mybatis/src/test/java/com/github/kuangcp/simple/order/dao/OrderDaoSpringBootTest.java index 282ef092..45aa9e69 100644 --- a/mybatis/src/test/java/com/github/kuangcp/simple/order/dao/OrderDaoTest.java +++ b/mybatis/src/test/java/com/github/kuangcp/simple/order/dao/OrderDaoSpringBootTest.java @@ -1,7 +1,7 @@ package com.github.kuangcp.simple.order.dao; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; -import com.github.kuangcp.base.TestStarter; +import com.github.kuangcp.base.SpringBootTestStarter; import com.github.kuangcp.simple.order.domain.Order; import lombok.extern.slf4j.Slf4j; import org.junit.Test; @@ -13,7 +13,7 @@ import java.util.function.Consumer; @Slf4j -public class OrderDaoTest extends TestStarter { +public class OrderDaoSpringBootTest extends SpringBootTestStarter { @Autowired private OrderDao orderDao; diff --git a/mybatis/src/test/java/com/github/kuangcp/simple/order/service/OrderServiceTest.java b/mybatis/src/test/java/com/github/kuangcp/simple/order/service/OrderServiceSpringBootTest.java similarity index 81% rename from mybatis/src/test/java/com/github/kuangcp/simple/order/service/OrderServiceTest.java rename to mybatis/src/test/java/com/github/kuangcp/simple/order/service/OrderServiceSpringBootTest.java index 15ee2e42..b902092d 100644 --- a/mybatis/src/test/java/com/github/kuangcp/simple/order/service/OrderServiceTest.java +++ b/mybatis/src/test/java/com/github/kuangcp/simple/order/service/OrderServiceSpringBootTest.java @@ -1,6 +1,6 @@ package com.github.kuangcp.simple.order.service; -import com.github.kuangcp.base.TestStarter; +import com.github.kuangcp.base.SpringBootTestStarter; import com.github.kuangcp.simple.order.dto.OrderDTO; import java.util.List; import lombok.extern.slf4j.Slf4j; @@ -8,7 +8,7 @@ import org.springframework.beans.factory.annotation.Autowired; @Slf4j -public class OrderServiceTest extends TestStarter { +public class OrderServiceSpringBootTest extends SpringBootTestStarter { @Autowired private OrderService orderService; diff --git a/mybatis/src/test/java/com/github/kuangcp/stream/CursorSessionSpringBootTest.java b/mybatis/src/test/java/com/github/kuangcp/stream/CursorSessionSpringBootTest.java new file mode 100644 index 00000000..f142d372 --- /dev/null +++ b/mybatis/src/test/java/com/github/kuangcp/stream/CursorSessionSpringBootTest.java @@ -0,0 +1,110 @@ +package com.github.kuangcp.stream; + +import com.github.kuangcp.base.SpringBootTestStarter; +import com.github.kuangcp.sharding.manual.AuthUtil; +import com.github.kuangcp.simple.customer.dao.CustomerDao; +import com.github.kuangcp.simple.customer.domain.Customer; +import com.github.kuangcp.stream.dao.ReportDao; +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.util.StopWatch; + +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +/** + * @author https://github.com/kuangcp on 2021-07-12 23:29 + */ +@Slf4j +public class CursorSessionSpringBootTest extends SpringBootTestStarter { + + @Autowired + private CustomerDao customerDao; + @Autowired + private AuthUtil authUtil; + @Autowired + private ReportDao reportDao; + + @Test + public void testStream() throws Exception { + authUtil.completeAuth(3L); + List result = new ArrayList<>(); + customerDao.streamQueryAll(ctx -> { + Customer val = ctx.getResultObject(); + result.add(val); + }); + log.info("result={}", result); + } + + @Test + public void testInsertCache() throws Exception { + List partsCode = IntStream.range(0, 30000) + .mapToObj(v -> UUID.randomUUID().toString()).collect(Collectors.toList()); + Random random = new Random(); + for (int i = 1; i < 700_000; i++) { + LocalDateTime now = LocalDateTime.now(); + Date start = Date.from(now.plusDays(-1 * (i / 1000)).atZone(ZoneId.systemDefault()).toInstant()); + + Report report = Report.builder() + .inquiryCode(partsCode.get(random.nextInt(30000))) + .userId((long) i % 6 + 70) + .inquiryCount(i % 7) + .haveGoodsCount(i % 4) + .purchaseCount(i % 2) + .statisticsTime(start) + .build(); + reportDao.insert(report); + } + } + + @Test + public void testPageQueryOne() throws Exception { + StopWatch stopWatch = new StopWatch(); + stopWatch.start("query"); + Date start = Date.from(LocalDateTime.now().plusDays(-500).atZone(ZoneId.systemDefault()).toInstant()); + List reports = reportDao.queryByPage(start, new Date(), 0, 1000); + stopWatch.stop(); + log.info("reports.size()={}", reports.size()); + log.info("={}", stopWatch.prettyPrint()); + } + + // 90s = 30 * 3 + @Test + public void testPageQueryAll() throws Exception { + StopWatch stopWatch = new StopWatch(); + for (int i = 0; i < 30; i++) { + stopWatch.start("query" + i); + Date start = Date.from(LocalDateTime.now().plusDays(-500).atZone(ZoneId.systemDefault()).toInstant()); + List reports = reportDao.queryByPage(start, new Date(), i * 1000, 1000); + stopWatch.stop(); + log.info("reports.size()={}", reports.size()); + } + log.info("={}", stopWatch.prettyPrint()); + } + + // 3s + @Test + public void testStreamQuery() throws Exception { + Date start = Date.from(LocalDateTime.now().plusDays(-500).atZone(ZoneId.systemDefault()).toInstant()); + + List result = new ArrayList<>(); + StopWatch stopWatch = new StopWatch(); + stopWatch.start("query"); + reportDao.selectAutoList(start, new Date(), ctx -> { + Report val = ctx.getResultObject(); + result.add(val); + }); + stopWatch.stop(); + log.info("result={} {} {}", result.size(), stopWatch.getTotalTimeMillis(), stopWatch.prettyPrint()); + +// Path path = Paths.get("/tmp/report.scv"); +// String content = result.stream().map(report -> String.format("%d,%s,%d,%d,%d", report.getUserId(), +// report.getInquiryCode(), report.getInquiryCount(), report.getHaveGoodsCount(), report.getPurchaseCount())) +// .collect(Collectors.joining("\n")); +// Files.write(path, content.getBytes()); + } +} From 9196ab0455dc7473424c8187850b6680d18c98ca Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Tue, 13 Jul 2021 01:39:26 +0800 Subject: [PATCH 202/476] +) readme doc --- class/build.gradle | 24 +++--- .../sharding/manual/ShardingTable.java | 1 - .../kuangcp/simple/order/domain/Order.java | 23 +++--- .../dao/CustomerDaoSpringBootTest.java | 3 +- .../order/dao/OrderDaoSpringBootTest.java | 82 +++++++++---------- .../stream/CursorSessionSpringBootTest.java | 5 +- .../java/com/github/kuangcp/stream/Readme.md | 11 +++ 7 files changed, 82 insertions(+), 67 deletions(-) create mode 100644 mybatis/src/test/java/com/github/kuangcp/stream/Readme.md diff --git a/class/build.gradle b/class/build.gradle index bbd0f798..20a9fea1 100644 --- a/class/build.gradle +++ b/class/build.gradle @@ -1,15 +1,17 @@ dependencies { - implementation project(":common-config") + implementation project(":common-config") - implementation libs['fastjson'] - implementation libs['gson'] - implementation libs['jackson_core'] - implementation libs['jackson_databind'] - implementation libs['jackson_annotations'] - implementation libs['jackson_smile'] + implementation libs['kcp-tool'] - implementation libs['jmh'] - implementation libs['jmh-generator-annprocess'] - implementation libs['common-lang'] - implementation libs['cglib'] + implementation libs['fastjson'] + implementation libs['gson'] + implementation libs['jackson_core'] + implementation libs['jackson_databind'] + implementation libs['jackson_annotations'] + implementation libs['jackson_smile'] + + implementation libs['jmh'] + implementation libs['jmh-generator-annprocess'] + implementation libs['common-lang'] + implementation libs['cglib'] } diff --git a/mybatis/src/main/java/com/github/kuangcp/sharding/manual/ShardingTable.java b/mybatis/src/main/java/com/github/kuangcp/sharding/manual/ShardingTable.java index e2e8e78f..eb71605c 100644 --- a/mybatis/src/main/java/com/github/kuangcp/sharding/manual/ShardingTable.java +++ b/mybatis/src/main/java/com/github/kuangcp/sharding/manual/ShardingTable.java @@ -3,7 +3,6 @@ import java.lang.annotation.*; /** - * * @author https://github.com/kuangcp on 2021-07-11 18:19 */ @Documented diff --git a/mybatis/src/main/java/com/github/kuangcp/simple/order/domain/Order.java b/mybatis/src/main/java/com/github/kuangcp/simple/order/domain/Order.java index 2836c24d..f0c49853 100644 --- a/mybatis/src/main/java/com/github/kuangcp/simple/order/domain/Order.java +++ b/mybatis/src/main/java/com/github/kuangcp/simple/order/domain/Order.java @@ -1,13 +1,14 @@ package com.github.kuangcp.simple.order.domain; import com.baomidou.mybatisplus.annotation.TableName; -import java.math.BigDecimal; -import java.time.LocalDateTime; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; +import java.math.BigDecimal; +import java.time.LocalDateTime; + @Data @Builder @NoArgsConstructor @@ -15,13 +16,13 @@ @TableName("normal_order") public class Order { - private Long id; - private Long userId; - private LocalDateTime createTime; - private LocalDateTime updateTime; - private String detail; - private BigDecimal price; - private BigDecimal discount; - private Integer num; - private LocalDateTime payTime; + private Long id; + private Long userId; + private LocalDateTime createTime; + private LocalDateTime updateTime; + private String detail; + private BigDecimal price; + private BigDecimal discount; + private Integer num; + private LocalDateTime payTime; } diff --git a/mybatis/src/test/java/com/github/kuangcp/simple/customer/dao/CustomerDaoSpringBootTest.java b/mybatis/src/test/java/com/github/kuangcp/simple/customer/dao/CustomerDaoSpringBootTest.java index d9860679..69c1a02a 100644 --- a/mybatis/src/test/java/com/github/kuangcp/simple/customer/dao/CustomerDaoSpringBootTest.java +++ b/mybatis/src/test/java/com/github/kuangcp/simple/customer/dao/CustomerDaoSpringBootTest.java @@ -18,10 +18,11 @@ public class CustomerDaoSpringBootTest extends SpringBootTestStarter { @Autowired private AuthUtil authUtil; - private void login(Long orgId){ + private void login(Long orgId) { authUtil.clearAuth(); authUtil.completeAuth(orgId); } + @Test public void testQuery() { login(2L); diff --git a/mybatis/src/test/java/com/github/kuangcp/simple/order/dao/OrderDaoSpringBootTest.java b/mybatis/src/test/java/com/github/kuangcp/simple/order/dao/OrderDaoSpringBootTest.java index 45aa9e69..faf23b8d 100644 --- a/mybatis/src/test/java/com/github/kuangcp/simple/order/dao/OrderDaoSpringBootTest.java +++ b/mybatis/src/test/java/com/github/kuangcp/simple/order/dao/OrderDaoSpringBootTest.java @@ -15,51 +15,51 @@ @Slf4j public class OrderDaoSpringBootTest extends SpringBootTestStarter { - @Autowired - private OrderDao orderDao; + @Autowired + private OrderDao orderDao; - @Test - public void testQuery() { - Order origin = Order.builder() - .num(1) - .detail("detail") - .createTime(LocalDateTime.now()) - .payTime(LocalDateTime.now()) - .build(); - orderDao.insert(origin); - List orders = orderDao.selectList(new QueryWrapper<>()); - orders.forEach(item -> log.info("{}", item)); - } + @Test + public void testQuery() { + Order origin = Order.builder() + .num(1) + .detail("detail") + .createTime(LocalDateTime.now()) + .payTime(LocalDateTime.now()) + .build(); + orderDao.insert(origin); + List orders = orderDao.selectList(new QueryWrapper<>()); + orders.forEach(item -> log.info("{}", item)); + } - @Test - public void testBulkInsert() throws InterruptedException { + @Test + public void testBulkInsert() throws InterruptedException { - Consumer consumer = start -> { - Order temp = Order.builder().num(44) - .createTime(LocalDateTime.now()).build(); - for (int i = 0; i < 10000; i++) { - long id = (start + i); - temp.setId(id); - temp.setUserId(id % 400); - orderDao.insert(temp); - } - }; + Consumer consumer = start -> { + Order temp = Order.builder().num(44) + .createTime(LocalDateTime.now()).build(); + for (int i = 0; i < 10000; i++) { + long id = (start + i); + temp.setId(id); + temp.setUserId(id % 400); + orderDao.insert(temp); + } + }; - List threads = new ArrayList<>(); - for (int i = 0; i < 8; i++) { - int finalI = i; - threads.add(new Thread(() -> consumer.accept(500200 + finalI * 20000))); - } + List threads = new ArrayList<>(); + for (int i = 0; i < 8; i++) { + int finalI = i; + threads.add(new Thread(() -> consumer.accept(500200 + finalI * 20000))); + } - threads.forEach(v -> { - try { - v.start(); - v.join(); - } catch (InterruptedException e) { - e.printStackTrace(); - } - }); + threads.forEach(v -> { + try { + v.start(); + v.join(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + }); - Thread.yield(); - } + Thread.yield(); + } } \ No newline at end of file diff --git a/mybatis/src/test/java/com/github/kuangcp/stream/CursorSessionSpringBootTest.java b/mybatis/src/test/java/com/github/kuangcp/stream/CursorSessionSpringBootTest.java index f142d372..5bf561c5 100644 --- a/mybatis/src/test/java/com/github/kuangcp/stream/CursorSessionSpringBootTest.java +++ b/mybatis/src/test/java/com/github/kuangcp/stream/CursorSessionSpringBootTest.java @@ -45,13 +45,14 @@ public void testInsertCache() throws Exception { List partsCode = IntStream.range(0, 30000) .mapToObj(v -> UUID.randomUUID().toString()).collect(Collectors.toList()); Random random = new Random(); + long userFrame = 30; // 构造不同数据段 for (int i = 1; i < 700_000; i++) { LocalDateTime now = LocalDateTime.now(); Date start = Date.from(now.plusDays(-1 * (i / 1000)).atZone(ZoneId.systemDefault()).toInstant()); Report report = Report.builder() .inquiryCode(partsCode.get(random.nextInt(30000))) - .userId((long) i % 6 + 70) + .userId((long) i % 6 + userFrame) .inquiryCount(i % 7) .haveGoodsCount(i % 4) .purchaseCount(i % 2) @@ -72,7 +73,7 @@ public void testPageQueryOne() throws Exception { log.info("={}", stopWatch.prettyPrint()); } - // 90s = 30 * 3 + // 90s = 30 * 3s @Test public void testPageQueryAll() throws Exception { StopWatch stopWatch = new StopWatch(); diff --git a/mybatis/src/test/java/com/github/kuangcp/stream/Readme.md b/mybatis/src/test/java/com/github/kuangcp/stream/Readme.md new file mode 100644 index 00000000..503934ea --- /dev/null +++ b/mybatis/src/test/java/com/github/kuangcp/stream/Readme.md @@ -0,0 +1,11 @@ +使用流优化大数据量查询和导出 + +Stream模式 + +弊端: + +查看Mybatis实现原理,是否有连接高频占用和释放风险 + +优势: + +降低SQL查询扫描行数 \ No newline at end of file From 44a8b5bd95b8d3acd6f84572cb4ce99ba473b0e1 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Tue, 13 Jul 2021 22:59:58 +0800 Subject: [PATCH 203/476] *) more data --- .../com/github/kuangcp/stream/Report.java | 43 +++---------------- .../github/kuangcp/stream/dao/ReportDao.java | 16 +++---- .../stream/CursorSessionSpringBootTest.java | 29 ++++++++----- 3 files changed, 31 insertions(+), 57 deletions(-) diff --git a/mybatis/src/main/java/com/github/kuangcp/stream/Report.java b/mybatis/src/main/java/com/github/kuangcp/stream/Report.java index 74741b52..38e33f83 100644 --- a/mybatis/src/main/java/com/github/kuangcp/stream/Report.java +++ b/mybatis/src/main/java/com/github/kuangcp/stream/Report.java @@ -14,7 +14,7 @@ @NoArgsConstructor @AllArgsConstructor @EqualsAndHashCode(callSuper = false) -@TableName("pq_inquiry_parts_report") +@TableName("report") public class Report { private static final long serialVersionUID = 1L; @@ -28,42 +28,9 @@ public class Report { * 用户id */ private Long userId; - /** - * 询价编码 - */ - private String inquiryCode; - /** - * 询价次数 - */ - private Integer inquiryCount; - /** - * 有货报出数 - */ - private Integer haveGoodsCount; - /** - * 采购次数 - */ - private Integer purchaseCount; - /** - * 统计日期 - */ + private String b; + private Integer c; + private Integer d; + private Integer e; private Date statisticsTime; - /** - * 创建时间 - */ - private Date createTime; - /** - * 更新时间 - */ - private Date updateTime; - - public static final String ID = "id"; - public static final String USER_ID = "user_id"; - public static final String INQUIRY_CODE = "inquiry_code"; - public static final String INQUIRY_COUNT = "inquiry_count"; - public static final String HAVE_GOODS_COUNT = "have_goods_count"; - public static final String PURCHASE_COUNT = "purchase_count"; - public static final String STATISTICS_TIME = "statistics_time"; - public static final String CREATE_TIME = "create_time"; - public static final String UPDATE_TIME = "update_time"; } diff --git a/mybatis/src/main/java/com/github/kuangcp/stream/dao/ReportDao.java b/mybatis/src/main/java/com/github/kuangcp/stream/dao/ReportDao.java index f67835d0..ffad2a2c 100644 --- a/mybatis/src/main/java/com/github/kuangcp/stream/dao/ReportDao.java +++ b/mybatis/src/main/java/com/github/kuangcp/stream/dao/ReportDao.java @@ -19,22 +19,22 @@ @Repository public interface ReportDao extends BaseMapper { - @Select("SELECT user_id, inquiry_code, SUM(inquiry_count) AS inquiry_count" + - ", SUM(have_goods_count) AS have_goods_count, SUM(purchase_count) AS purchase_count" + - " FROM pq_inquiry_parts_report WHERE statistics_time BETWEEN #{start} AND #{end}" + + @Select("SELECT user_id, b, SUM(c) AS c" + + ", SUM(d) AS d, SUM(e) AS e" + + " FROM report WHERE statistics_time BETWEEN #{start} AND #{end}" + " AND user_id IN (1,2,3,4,5,6)" + - " GROUP BY inquiry_code") + " GROUP BY b") @Options(resultSetType = ResultSetType.FORWARD_ONLY, fetchSize = Integer.MIN_VALUE) @ResultType(Report.class) void selectAutoList(@Param("start") Date start, @Param("end") Date end, ResultHandler handler); - @Select("SELECT user_id, inquiry_code, SUM(inquiry_count) AS inquiry_count" + - ", SUM(have_goods_count) AS have_goods_count, SUM(purchase_count) AS purchase_count" + - " FROM pq_inquiry_parts_report WHERE statistics_time BETWEEN #{start} AND #{end}" + + @Select("SELECT user_id, b, SUM(c) AS c" + + ", SUM(d) AS d, SUM(e) AS e" + + " FROM report WHERE statistics_time BETWEEN #{start} AND #{end}" + " AND user_id IN (1,2,3,4,5,6)" + - " GROUP BY inquiry_code LIMIT #{startIdx},#{size}") + " GROUP BY b LIMIT #{startIdx},#{size}") List queryByPage(@Param("start") Date start, @Param("end") Date end, @Param("startIdx") long startIdx, diff --git a/mybatis/src/test/java/com/github/kuangcp/stream/CursorSessionSpringBootTest.java b/mybatis/src/test/java/com/github/kuangcp/stream/CursorSessionSpringBootTest.java index 5bf561c5..3aa235c5 100644 --- a/mybatis/src/test/java/com/github/kuangcp/stream/CursorSessionSpringBootTest.java +++ b/mybatis/src/test/java/com/github/kuangcp/stream/CursorSessionSpringBootTest.java @@ -42,21 +42,23 @@ public void testStream() throws Exception { @Test public void testInsertCache() throws Exception { - List partsCode = IntStream.range(0, 30000) + List strList = IntStream.range(0, 70000) .mapToObj(v -> UUID.randomUUID().toString()).collect(Collectors.toList()); Random random = new Random(); - long userFrame = 30; // 构造不同数据段 - for (int i = 1; i < 700_000; i++) { + long userFrame = 1; // 构造不同数据段 + for (int i = 1; i < 120_000; i++) { LocalDateTime now = LocalDateTime.now(); - Date start = Date.from(now.plusDays(-1 * (i / 1000)).atZone(ZoneId.systemDefault()).toInstant()); + LocalDateTime time = now.plusDays(-1 * (i / 2_000)).plusMinutes(i % 30).plusSeconds(i % 33).plusNanos(i % 4000); + Date statisticDate = Date.from(time.atZone(ZoneId.systemDefault()).toInstant()); Report report = Report.builder() - .inquiryCode(partsCode.get(random.nextInt(30000))) .userId((long) i % 6 + userFrame) - .inquiryCount(i % 7) - .haveGoodsCount(i % 4) - .purchaseCount(i % 2) - .statisticsTime(start) +// .userId((long) i * new Random().nextInt(300)) + .b(strList.get(random.nextInt(70000))) + .c(i % 7) + .d(i % 4) + .e(i % 2) + .statisticsTime(statisticDate) .build(); reportDao.insert(report); } @@ -77,7 +79,7 @@ public void testPageQueryOne() throws Exception { @Test public void testPageQueryAll() throws Exception { StopWatch stopWatch = new StopWatch(); - for (int i = 0; i < 30; i++) { + for (int i = 0; i < 10; i++) { stopWatch.start("query" + i); Date start = Date.from(LocalDateTime.now().plusDays(-500).atZone(ZoneId.systemDefault()).toInstant()); List reports = reportDao.queryByPage(start, new Date(), i * 1000, 1000); @@ -97,10 +99,15 @@ public void testStreamQuery() throws Exception { stopWatch.start("query"); reportDao.selectAutoList(start, new Date(), ctx -> { Report val = ctx.getResultObject(); +// try { +// TimeUnit.NANOSECONDS.sleep(100); +// } catch (InterruptedException e) { +// e.printStackTrace(); +// } result.add(val); }); stopWatch.stop(); - log.info("result={} {} {}", result.size(), stopWatch.getTotalTimeMillis(), stopWatch.prettyPrint()); + log.info("result={} {}ms {}", result.size(), stopWatch.getTotalTimeMillis(), stopWatch.prettyPrint()); // Path path = Paths.get("/tmp/report.scv"); // String content = result.stream().map(report -> String.format("%d,%s,%d,%d,%d", report.getUserId(), From d871f7f681ddc0d6e2d9893ef133daf073a64733 Mon Sep 17 00:00:00 2001 From: kcp Date: Fri, 23 Jul 2021 21:57:45 +0800 Subject: [PATCH 204/476] =?UTF-8?q?+)=20distinct=20=E5=92=8C=20hashCode=20?= =?UTF-8?q?equals?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kuangcp/stream/distinct/DistinctTest.java | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 java8/src/test/java/com/github/kuangcp/stream/distinct/DistinctTest.java diff --git a/java8/src/test/java/com/github/kuangcp/stream/distinct/DistinctTest.java b/java8/src/test/java/com/github/kuangcp/stream/distinct/DistinctTest.java new file mode 100644 index 00000000..e2852a78 --- /dev/null +++ b/java8/src/test/java/com/github/kuangcp/stream/distinct/DistinctTest.java @@ -0,0 +1,81 @@ +package com.github.kuangcp.stream.distinct; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; + +/** + * @author kuangcp + */ +@Slf4j +public class DistinctTest { + + // 如果使用 Data注解会使用到 EqualsAndHashCode 注解(使用值计算hashCode和equals) +// @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + static class ValElement { + + String name; + BigDecimal val; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public BigDecimal getVal() { + return val; + } + + public void setVal(BigDecimal val) { + this.val = val; + } + + @Override + public String toString() { + return "ValElement{" + + "name='" + name + '\'' + + ", val=" + val + + '}'; + } + } + + @Test + public void testDistinct() { + List cache = new ArrayList<>(); + ValElement a = ValElement.builder().name("a").val(BigDecimal.ONE).build(); + ValElement b = ValElement.builder().name("b").val(BigDecimal.TEN).build(); + ValElement c = ValElement.builder().name("c").val(BigDecimal.valueOf(100)).build(); + + cache.add(a); + cache.add(a); + cache.add(a); + cache.add(b); + cache.add(b); + cache.add(b); + cache.add(c); + cache.add(c); + cache.add(c); + cache.add(c); + + cache.stream().distinct().forEach(v -> { + BigDecimal old = v.getVal(); + BigDecimal newVal = old.multiply(BigDecimal.valueOf(3)); + log.info("old={} new={} v={} code={}", old, newVal, v, v.hashCode()); + v.setVal(newVal); + }); + + cache.forEach(v -> log.info("v={}", v)); + } +} From 7fc1676fe86393d7c272518561028e25f6beb648 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Mon, 30 Aug 2021 03:30:36 +0800 Subject: [PATCH 205/476] *) upgrade version --- class/build.gradle | 3 ++- dependency.gradle | 18 +++++++++--------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/class/build.gradle b/class/build.gradle index 20a9fea1..9beda8a5 100644 --- a/class/build.gradle +++ b/class/build.gradle @@ -11,7 +11,8 @@ dependencies { implementation libs['jackson_smile'] implementation libs['jmh'] - implementation libs['jmh-generator-annprocess'] + annotationProcessor libs['jmh-generator-annprocess'] + implementation libs['common-lang'] implementation libs['cglib'] } diff --git a/dependency.gradle b/dependency.gradle index 38f03f8b..2d6a1816 100644 --- a/dependency.gradle +++ b/dependency.gradle @@ -3,19 +3,19 @@ ext { ver = [ - logback : '1.2.3' + logback : '1.2.5' , jedis : '2.9.0' , junit : '4.12' - , groovy : '2.5.6' + , groovy : '3.0.8' , mail : '1.4.7' , jackson : '2.9.5' , hamcrest: '1.3' , kcp_tool: '1.0.7' - , netty : '4.1.35.Final' - , guava : '27.1-jre' - , jmh : '1.2.1' + , netty : '4.1.67.Final' + , guava : '30.1.1-jre' + , jmh : '1.33' , kafka : '2.3.1' - , spring : '5.1.5.RELEASE' + , spring : '5.1.20.RELEASE' , aspectj : '1.9.0' , hadoop : '2.7.2' , flink : '1.8.0' @@ -36,8 +36,8 @@ ext { // test, junit has removed hamcrest since 4.11 , "hamcrest-core" : "org.hamcrest:hamcrest-core:$ver.hamcrest" , "hamcrest-lib" : "org.hamcrest:hamcrest-library:$ver.hamcrest" - , "jmh" : "org.openjdk.jmh:jmh-core:1.30" - , "jmh-generator-annprocess": "org.openjdk.jmh:jmh-generator-annprocess:1.30" + , "jmh" : "org.openjdk.jmh:jmh-core:$ver.jmh" + , "jmh-generator-annprocess": "org.openjdk.jmh:jmh-generator-annprocess:$ver.jmh" , "junit" : "junit:junit:$ver.junit" , "mockito-core" : "org.mockito:mockito-core:2.21.0" @@ -83,7 +83,7 @@ ext { // kafka , "kafka-clients" : "org.apache.kafka:kafka-clients:$ver.kafka" - // flink + // flink , "flink-java" : "org.apache.flink:flink-java:$ver.flink" , "flink-clients" : "org.apache.flink:flink-clients_2.12:$ver.flink" , "flink-streaming-java" : "org.apache.flink:flink-streaming-java_2.12:$ver.flink" From 9f34d90171187006a9a1c4e5c056766d5946a361 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Mon, 30 Aug 2021 03:30:59 +0800 Subject: [PATCH 206/476] *) format --- build.gradle | 80 +++++++++---------- dependency.gradle | 192 +++++++++++++++++++++++----------------------- 2 files changed, 136 insertions(+), 136 deletions(-) diff --git a/build.gradle b/build.gradle index f2fe6e60..dbe7301c 100644 --- a/build.gradle +++ b/build.gradle @@ -1,57 +1,57 @@ apply from: 'dependency.gradle' buildscript { - repositories { - maven { - url "https://plugins.gradle.org/m2/" + repositories { + maven { + url "https://plugins.gradle.org/m2/" + } + } + dependencies { + classpath "org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:2.8" } - } - dependencies { - classpath "org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:2.8" - } } // parent and child project all use this config subprojects { - apply plugin: 'java' - apply plugin: 'maven-publish' - apply plugin: "org.sonarqube" + apply plugin: 'java' + apply plugin: 'maven-publish' + apply plugin: "org.sonarqube" - group = 'com.github.kuangcp' + group = 'com.github.kuangcp' - // default build dir is build/ but idea compile dir is out/, i'm dislike this way - // dir out/ just have production/ and test/ dir - buildDir = 'out/build' + // default build dir is build/ but idea compile dir is out/, i'm dislike this way + // dir out/ just have production/ and test/ dir + buildDir = 'out/build' - sourceCompatibility = 1.8 - targetCompatibility = 1.8 + sourceCompatibility = 1.8 + targetCompatibility = 1.8 - repositories { - mavenLocal() + repositories { + mavenLocal() - def aliYun = "https://maven.aliyun.com/nexus/content/groups/public/" - def kuangcp = "https://gitee.com/gin9/MavenRepos/raw/master" + def aliYun = "https://maven.aliyun.com/nexus/content/groups/public/" + def kuangcp = "https://gitee.com/gin9/MavenRepos/raw/master" - maven { - url = aliYun + maven { + url = aliYun + } + maven { + url = kuangcp + } + jcenter() } - maven { - url = kuangcp + + // All sub-modules add the following dependencies + dependencies { + annotationProcessor libs['lombok'] + compileOnly libs['lombok'] + testAnnotationProcessor libs['lombok'] + testCompileOnly libs['lombok'] + + implementation libs['logback-classic'] + + testImplementation libs['junit'] + testImplementation libs['hamcrest-core'] + testImplementation libs['hamcrest-lib'] } - jcenter() - } - - // All sub-modules add the following dependencies - dependencies { - annotationProcessor libs['lombok'] - compileOnly libs['lombok'] - testAnnotationProcessor libs['lombok'] - testCompileOnly libs['lombok'] - - implementation libs['logback-classic'] - - testImplementation libs['junit'] - testImplementation libs['hamcrest-core'] - testImplementation libs['hamcrest-lib'] - } } diff --git a/dependency.gradle b/dependency.gradle index 2d6a1816..e4a58146 100644 --- a/dependency.gradle +++ b/dependency.gradle @@ -2,100 +2,100 @@ // just when module build.gradle use this dependency, then download and build project ext { - ver = [ - logback : '1.2.5' - , jedis : '2.9.0' - , junit : '4.12' - , groovy : '3.0.8' - , mail : '1.4.7' - , jackson : '2.9.5' - , hamcrest: '1.3' - , kcp_tool: '1.0.7' - , netty : '4.1.67.Final' - , guava : '30.1.1-jre' - , jmh : '1.33' - , kafka : '2.3.1' - , spring : '5.1.20.RELEASE' - , aspectj : '1.9.0' - , hadoop : '2.7.2' - , flink : '1.8.0' - ] - libs = [ - // mine tool - "kcp-tool" : "com.github.kuangcp:kcp-tool:$ver.kcp_tool" - - // tool - , "lombok" : "org.projectlombok:lombok:1.18.2" - , "common-lang" : "org.apache.commons:commons-lang3:3.7" - , "groovy" : "org.codehaus.groovy:groovy-all:$ver.groovy" - , "cglib" : "cglib:cglib:3.2.9" - , "common-math" : "org.apache.commons:commons-math3:3.6.1" - , "common-codec" : "commons-codec:commons-codec:1.13" - , "guava" : "com.google.guava:guava:$ver.guava" - - // test, junit has removed hamcrest since 4.11 - , "hamcrest-core" : "org.hamcrest:hamcrest-core:$ver.hamcrest" - , "hamcrest-lib" : "org.hamcrest:hamcrest-library:$ver.hamcrest" - , "jmh" : "org.openjdk.jmh:jmh-core:$ver.jmh" - , "jmh-generator-annprocess": "org.openjdk.jmh:jmh-generator-annprocess:$ver.jmh" - - , "junit" : "junit:junit:$ver.junit" - , "mockito-core" : "org.mockito:mockito-core:2.21.0" - , "testng" : "org.testng:testng:6.14.3" - - // log - , "logback-classic" : "ch.qos.logback:logback-classic:$ver.logback" - - // network - , "ok-http" : "com.squareup.okhttp3:okhttp:3.13.1" - , "netty" : "io.netty:netty-all:$ver.netty" - - // mail - , "mail" : "javax.mail:mail:$ver.mail" - , "activation" : "javax.activation:activation:1.1.1" - - // db - , "jedis" : "redis.clients:jedis:3.0.1" - , "MySQL" : "mysql:mysql-connector-java:8.0.15" - - // JSON dependency - , "gson" : "com.google.code.gson:gson:2.8.5" - , "fastjson" : "com.alibaba:fastjson:1.2.47" - , "jackson_core" : "com.fasterxml.jackson.core:jackson-core:$ver.jackson" - , "jackson_databind" : "com.fasterxml.jackson.core:jackson-databind:$ver.jackson" - , "jackson_annotations" : "com.fasterxml.jackson.core:jackson-annotations:$ver.jackson" - , "jackson_smile" : "com.fasterxml.jackson.dataformat:jackson-dataformat-smile:$ver.jackson" - - , "aspectjrt" : "org.aspectj:aspectjrt:$ver.aspectj" - , "aspectjweaver" : "org.aspectj:aspectjweaver:$ver.aspectj" - - // Spring - , "spring-core" : "org.springframework:spring-core:$ver.spring" - , "spring-ctx" : "org.springframework:spring-context:$ver.spring" - , "spring-beans" : "org.springframework:spring-beans:$ver.spring" - , "spring-aop" : "org.springframework:spring-aop:$ver.spring" - , "spring-orm" : "org.springframework:spring-orm:$ver.spring" - - // mybatis - , "mybatis" : "org.mybatis:mybatis:3.5.2" - , "mybatis-plus" : "com.baomidou:mybatis-plus:3.1.2" - - // kafka - , "kafka-clients" : "org.apache.kafka:kafka-clients:$ver.kafka" - - // flink - , "flink-java" : "org.apache.flink:flink-java:$ver.flink" - , "flink-clients" : "org.apache.flink:flink-clients_2.12:$ver.flink" - , "flink-streaming-java" : "org.apache.flink:flink-streaming-java_2.12:$ver.flink" - - // hadoop - , "hadoop-common" : "org.apache.hadoop:hadoop-common:$ver.hadoop" - , "hadoop-client" : "org.apache.hadoop:hadoop-client:$ver.hadoop" - , "hadoop-hdfs" : "org.apache.hadoop:hadoop-hdfs:$ver.hadoop" - - // the dependencies with specific version for problem situation - , "cglib-3.2.4" : "cglib:cglib:3.2.4" - , "asm-3.1" : "asm:asm:3.1" - , "asm-5.1" : "org.ow2.asm:asm:5.1" - ] + ver = [ + logback : '1.2.5' + , jedis : '2.9.0' + , junit : '4.12' + , groovy : '3.0.8' + , mail : '1.4.7' + , jackson : '2.9.5' + , hamcrest: '1.3' + , kcp_tool: '1.0.7' + , netty : '4.1.67.Final' + , guava : '30.1.1-jre' + , jmh : '1.33' + , kafka : '2.3.1' + , spring : '5.1.20.RELEASE' + , aspectj : '1.9.0' + , hadoop : '2.7.2' + , flink : '1.8.0' + ] + libs = [ + // mine tool + "kcp-tool" : "com.github.kuangcp:kcp-tool:$ver.kcp_tool" + + // tool + , "lombok" : "org.projectlombok:lombok:1.18.2" + , "common-lang" : "org.apache.commons:commons-lang3:3.7" + , "groovy" : "org.codehaus.groovy:groovy-all:$ver.groovy" + , "cglib" : "cglib:cglib:3.2.9" + , "common-math" : "org.apache.commons:commons-math3:3.6.1" + , "common-codec" : "commons-codec:commons-codec:1.13" + , "guava" : "com.google.guava:guava:$ver.guava" + + // test, junit has removed hamcrest since 4.11 + , "hamcrest-core" : "org.hamcrest:hamcrest-core:$ver.hamcrest" + , "hamcrest-lib" : "org.hamcrest:hamcrest-library:$ver.hamcrest" + , "jmh" : "org.openjdk.jmh:jmh-core:$ver.jmh" + , "jmh-generator-annprocess": "org.openjdk.jmh:jmh-generator-annprocess:$ver.jmh" + + , "junit" : "junit:junit:$ver.junit" + , "mockito-core" : "org.mockito:mockito-core:2.21.0" + , "testng" : "org.testng:testng:6.14.3" + + // log + , "logback-classic" : "ch.qos.logback:logback-classic:$ver.logback" + + // network + , "ok-http" : "com.squareup.okhttp3:okhttp:3.13.1" + , "netty" : "io.netty:netty-all:$ver.netty" + + // mail + , "mail" : "javax.mail:mail:$ver.mail" + , "activation" : "javax.activation:activation:1.1.1" + + // db + , "jedis" : "redis.clients:jedis:3.0.1" + , "MySQL" : "mysql:mysql-connector-java:8.0.15" + + // JSON dependency + , "gson" : "com.google.code.gson:gson:2.8.5" + , "fastjson" : "com.alibaba:fastjson:1.2.47" + , "jackson_core" : "com.fasterxml.jackson.core:jackson-core:$ver.jackson" + , "jackson_databind" : "com.fasterxml.jackson.core:jackson-databind:$ver.jackson" + , "jackson_annotations" : "com.fasterxml.jackson.core:jackson-annotations:$ver.jackson" + , "jackson_smile" : "com.fasterxml.jackson.dataformat:jackson-dataformat-smile:$ver.jackson" + + , "aspectjrt" : "org.aspectj:aspectjrt:$ver.aspectj" + , "aspectjweaver" : "org.aspectj:aspectjweaver:$ver.aspectj" + + // Spring + , "spring-core" : "org.springframework:spring-core:$ver.spring" + , "spring-ctx" : "org.springframework:spring-context:$ver.spring" + , "spring-beans" : "org.springframework:spring-beans:$ver.spring" + , "spring-aop" : "org.springframework:spring-aop:$ver.spring" + , "spring-orm" : "org.springframework:spring-orm:$ver.spring" + + // mybatis + , "mybatis" : "org.mybatis:mybatis:3.5.2" + , "mybatis-plus" : "com.baomidou:mybatis-plus:3.1.2" + + // kafka + , "kafka-clients" : "org.apache.kafka:kafka-clients:$ver.kafka" + + // flink + , "flink-java" : "org.apache.flink:flink-java:$ver.flink" + , "flink-clients" : "org.apache.flink:flink-clients_2.12:$ver.flink" + , "flink-streaming-java" : "org.apache.flink:flink-streaming-java_2.12:$ver.flink" + + // hadoop + , "hadoop-common" : "org.apache.hadoop:hadoop-common:$ver.hadoop" + , "hadoop-client" : "org.apache.hadoop:hadoop-client:$ver.hadoop" + , "hadoop-hdfs" : "org.apache.hadoop:hadoop-hdfs:$ver.hadoop" + + // the dependencies with specific version for problem situation + , "cglib-3.2.4" : "cglib:cglib:3.2.4" + , "asm-3.1" : "asm:asm:3.1" + , "asm-5.1" : "org.ow2.asm:asm:5.1" + ] } \ No newline at end of file From 13062e34a2d0f02392f827d4b878d4f3bbc0f497 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Sun, 5 Sep 2021 01:48:08 +0800 Subject: [PATCH 207/476] +) add web test --- .gitignore | 2 + .../main/java/jmh/StringBuilderBenchmark.java | 51 +++++++-------- .../java/jmh/StringBuilderBenchmarkTest.java | 62 ++++++++++--------- concurrency/.dockerignore | 2 + concurrency/Dockerfile | 18 ++++++ concurrency/Readme.md | 6 +- concurrency/build.gradle | 23 ++++--- .../src/main/java/cmd/Application.java | 13 ++++ .../cmd/situation/TimeoutPoolController.java | 40 ++++++++++++ .../kuangcp/countlatch/CountLatchDemo.java | 36 ----------- .../kuangcp/countlatch/ProcessingThread.java | 29 --------- .../kuangcp/forkjoin/ForkJoinEasyDemo.groovy | 16 ++--- .../situation/timoutpool/CreateNewPool.java | 55 ++++++++++++++++ .../main/java/situation/timoutpool/Param.java | 14 +++++ .../main/java/situation/timoutpool/Readme.md | 0 .../java/situation/timoutpool/Result.java | 16 +++++ .../situation/timoutpool/TaskExecutor.java | 11 ++++ .../src/main/java/thread/UseThreadPool.java | 26 ++++---- .../kuangcp/latch/CountDownLatchTest.java | 37 +++++++++++ .../github/kuangcp/lock/pvp/PlayerTest.java | 7 ++- .../situation/timoutpool/TimeoutPoolTest.java | 37 +++++++++++ dependency.gradle | 4 +- 22 files changed, 352 insertions(+), 153 deletions(-) create mode 100644 concurrency/.dockerignore create mode 100644 concurrency/Dockerfile create mode 100644 concurrency/src/main/java/cmd/Application.java create mode 100644 concurrency/src/main/java/cmd/situation/TimeoutPoolController.java delete mode 100644 concurrency/src/main/java/com/github/kuangcp/countlatch/CountLatchDemo.java delete mode 100644 concurrency/src/main/java/com/github/kuangcp/countlatch/ProcessingThread.java create mode 100644 concurrency/src/main/java/situation/timoutpool/CreateNewPool.java create mode 100644 concurrency/src/main/java/situation/timoutpool/Param.java create mode 100644 concurrency/src/main/java/situation/timoutpool/Readme.md create mode 100644 concurrency/src/main/java/situation/timoutpool/Result.java create mode 100644 concurrency/src/main/java/situation/timoutpool/TaskExecutor.java create mode 100644 concurrency/src/test/java/com/github/kuangcp/latch/CountDownLatchTest.java create mode 100644 concurrency/src/test/java/situation/timoutpool/TimeoutPoolTest.java diff --git a/.gitignore b/.gitignore index a398522a..98afe137 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,8 @@ # projcet # *.log logs/ +jmh/ +jmh_* # maven # target/ diff --git a/class/src/main/java/jmh/StringBuilderBenchmark.java b/class/src/main/java/jmh/StringBuilderBenchmark.java index c846e629..67b1bbcd 100644 --- a/class/src/main/java/jmh/StringBuilderBenchmark.java +++ b/class/src/main/java/jmh/StringBuilderBenchmark.java @@ -1,6 +1,5 @@ package jmh; -import java.util.concurrent.TimeUnit; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; import org.openjdk.jmh.annotations.Fork; @@ -10,40 +9,42 @@ import org.openjdk.jmh.annotations.Threads; import org.openjdk.jmh.annotations.Warmup; +import java.util.concurrent.TimeUnit; + /** * @author kuangcp on 2019-04-21 11:38 PM */ @BenchmarkMode(Mode.Throughput) -@Warmup(iterations = 3) -@Measurement(iterations = 5, time = 2) -@Threads(3) -@Fork(3) +@Warmup(iterations = 2) +@Measurement(iterations = 2, time = 1) +@Threads(2) +@Fork(2) @OutputTimeUnit(TimeUnit.MILLISECONDS) public class StringBuilderBenchmark { - @Benchmark - public void testStringAdd() { - String a = ""; - for (int i = 0; i < 10; i++) { - a += i; + @Benchmark + public void testStringAdd() { + String a = ""; + for (int i = 0; i < 10; i++) { + a += i; + } } - } - @Benchmark - public void testStringBuilderAdd() { - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < 10; i++) { - sb.append(i); + @Benchmark + public void testStringBuilderAdd() { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < 10; i++) { + sb.append(i); + } + sb.toString(); } - sb.toString(); - } - @Benchmark - public void testStringBufferAdd() { - StringBuffer sb = new StringBuffer(); - for (int i = 0; i < 10; i++) { - sb.append(i); + @Benchmark + public void testStringBufferAdd() { + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < 10; i++) { + sb.append(i); + } + sb.toString(); } - sb.toString(); - } } \ No newline at end of file diff --git a/class/src/test/java/jmh/StringBuilderBenchmarkTest.java b/class/src/test/java/jmh/StringBuilderBenchmarkTest.java index 5fae127c..037e039a 100644 --- a/class/src/test/java/jmh/StringBuilderBenchmarkTest.java +++ b/class/src/test/java/jmh/StringBuilderBenchmarkTest.java @@ -1,6 +1,5 @@ package jmh; -import java.util.concurrent.TimeUnit; import org.junit.Test; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; @@ -15,6 +14,8 @@ import org.openjdk.jmh.runner.options.Options; import org.openjdk.jmh.runner.options.OptionsBuilder; +import java.util.concurrent.TimeUnit; + /** * @author kuangcp on 2019-04-21 11:39 PM */ @@ -26,39 +27,40 @@ @OutputTimeUnit(TimeUnit.MILLISECONDS) public class StringBuilderBenchmarkTest { - @Test - public void test() throws RunnerException { - String output = "/tmp/StringBuilderBenchmark-" + System.currentTimeMillis() + ".log"; - System.out.println(output); - Options options = new OptionsBuilder() - .include(StringBuilderBenchmarkTest.class.getSimpleName()) - .output(output).build(); - new Runner(options).run(); - } + @Test + public void test() throws RunnerException { + String output = "/tmp/StringBuilderBenchmark-" + System.currentTimeMillis() + ".log"; + System.out.println(output); + Options options = new OptionsBuilder() + .include(StringBuilderBenchmark.class.getSimpleName()) +// .include(StringBuilderBenchmarkTest.class.getSimpleName()) + .output(output).build(); + new Runner(options).run(); + } - @Benchmark - public void testStringAdd() { - String a = ""; - for (int i = 0; i < 10; i++) { - a += i; + @Benchmark + public void testStringAdd() { + String a = ""; + for (int i = 0; i < 10; i++) { + a += i; + } } - } - @Benchmark - public void testStringBuilderAdd() { - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < 10; i++) { - sb.append(i); + @Benchmark + public void testStringBuilderAdd() { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < 10; i++) { + sb.append(i); + } + sb.toString(); } - sb.toString(); - } - @Benchmark - public void testStringBufferAdd() { - StringBuffer sb = new StringBuffer(); - for (int i = 0; i < 10; i++) { - sb.append(i); + @Benchmark + public void testStringBufferAdd() { + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < 10; i++) { + sb.append(i); + } + sb.toString(); } - sb.toString(); - } } \ No newline at end of file diff --git a/concurrency/.dockerignore b/concurrency/.dockerignore new file mode 100644 index 00000000..44946a96 --- /dev/null +++ b/concurrency/.dockerignore @@ -0,0 +1,2 @@ +out/ +!out/build/distributions/concurrency.tar \ No newline at end of file diff --git a/concurrency/Dockerfile b/concurrency/Dockerfile new file mode 100644 index 00000000..7d382c8a --- /dev/null +++ b/concurrency/Dockerfile @@ -0,0 +1,18 @@ +FROM mythkuang/jdk-alpine-cst:8.181 + +WORKDIR /app + +COPY ./out/build/distributions/concurrency.tar /app + +RUN tar -xvf concurrency.tar && \ + sed -i 's/DEFAULT_JVM_OPTS=""/DEFAULT_JVM_OPTS="-server \ + -Xms200m \ + -Xmx200m \ + -Xss256k\ + -verbose:gc\ + -XX:+PrintGCDetails\ + -XX:+PrintGCDateStamps\ + -Xloggc:\/app\/gc.log\ + -XX:ErrorFile=\/app\/hs_err_pid%p.log"/g' /app/concurrency/bin/concurrency + +CMD ["/app/concurrency/bin/concurrency"] diff --git a/concurrency/Readme.md b/concurrency/Readme.md index 823110fa..72d5489b 100644 --- a/concurrency/Readme.md +++ b/concurrency/Readme.md @@ -1,4 +1,8 @@ # Java中的并发 > [笔记](https://github.com/Kuangcp/Note/blob/master/Java/AdvancedLearning/Concurrency.md) -应尽量避免线程的阻塞和等待,因为这是在浪费CPU \ No newline at end of file +应尽量避免线程的阻塞和等待,因为这是在浪费CPU + +gradle clean build -x test +docker build -t con . +docker run --rm -p 9000:9000 --cpuset-cpus="1" --cpu-quota=5000 --cpu-period=5000 -it con \ No newline at end of file diff --git a/concurrency/build.gradle b/concurrency/build.gradle index d0390ff5..4df76b88 100644 --- a/concurrency/build.gradle +++ b/concurrency/build.gradle @@ -1,12 +1,21 @@ plugins { - id 'groovy' - id 'maven-publish' + id 'groovy' + id 'maven-publish' + id 'application' +} + +application { + mainClass = 'cmd.Application' } dependencies { - implementation project(":common-config") - implementation libs['jedis'] - implementation libs['groovy'] - implementation libs['common-lang'] - testImplementation(libs['testng']) + implementation project(":common-config") + implementation libs['kcp-tuple'] + + implementation libs['blade-mvc'] + + implementation libs['jedis'] + implementation libs['groovy'] + implementation libs['common-lang'] + testImplementation(libs['testng']) } \ No newline at end of file diff --git a/concurrency/src/main/java/cmd/Application.java b/concurrency/src/main/java/cmd/Application.java new file mode 100644 index 00000000..44fde604 --- /dev/null +++ b/concurrency/src/main/java/cmd/Application.java @@ -0,0 +1,13 @@ +package cmd; + +import com.blade.Blade; + +/** + * @author https://github.com/kuangcp on 2021-09-05 00:18 + */ +public class Application { + + public static void main(String[] args) { + Blade.of().get("/", ctx -> ctx.text("Hello Blade")).start(); + } +} diff --git a/concurrency/src/main/java/cmd/situation/TimeoutPoolController.java b/concurrency/src/main/java/cmd/situation/TimeoutPoolController.java new file mode 100644 index 00000000..d704b2b4 --- /dev/null +++ b/concurrency/src/main/java/cmd/situation/TimeoutPoolController.java @@ -0,0 +1,40 @@ +package cmd.situation; + +import com.blade.mvc.annotation.GetRoute; +import com.blade.mvc.annotation.Path; +import com.blade.mvc.http.Response; +import lombok.extern.slf4j.Slf4j; +import situation.timoutpool.CreateNewPool; +import situation.timoutpool.Param; +import situation.timoutpool.Result; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +/** + * @author https://github.com/kuangcp on 2021-09-05 00:56 + */ +@Path +@Slf4j +public class TimeoutPoolController { + + @GetRoute("/situation/timeout/createNew") + public void createNew(Response response) throws InterruptedException { + int loop = 60; + final CountDownLatch latch = new CountDownLatch(loop); + final ExecutorService exe = Executors.newFixedThreadPool(loop / 15); + for (int i = 0; i < loop; i++) { + exe.execute(() -> { + final CreateNewPool pool = new CreateNewPool(); + final long start = System.nanoTime(); + final Result result = pool.execute(Param.builder().start(1).total(40).build(), 5, TimeUnit.SECONDS); + log.info("result={} {}ms", result, (System.nanoTime() - start) / 1000_000); + latch.countDown(); + }); + } + latch.await(); + response.body("complete: " + loop); + } +} diff --git a/concurrency/src/main/java/com/github/kuangcp/countlatch/CountLatchDemo.java b/concurrency/src/main/java/com/github/kuangcp/countlatch/CountLatchDemo.java deleted file mode 100644 index 8fedeba8..00000000 --- a/concurrency/src/main/java/com/github/kuangcp/countlatch/CountLatchDemo.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.github.kuangcp.countlatch; - -import java.util.HashSet; -import java.util.Set; -import java.util.concurrent.CountDownLatch; -import lombok.extern.slf4j.Slf4j; - -/** - * Created by https://github.com/kuangcp on 17-8-14 下午9:44 - * 用锁存器辅助初始化: - * 这里把锁存器的初始值设置为 quorum .一旦达到这个数量,就可以开始处理更新了,每个线程完成初始化后都会马上调用 countDown() - * 主线程只要等 quorum 的达到,然后启动,再派发更新(这里没给出) - */ -@Slf4j -public class CountLatchDemo { - - public static void main(String[] s) { - final int MAX_THREADS = 12; - // 同一进程内的一组更新线程至少必须要有一般的线程正确初始化之后,才能开始接收系统发送给他们任何一个线程的更新 - final int quorum = 1 + (MAX_THREADS / 2); - final CountDownLatch cdl = new CountDownLatch(quorum); - - final Set nodes = new HashSet<>(); - try { - for (int i = 0; i < MAX_THREADS; i++) { - ProcessingThread local = new ProcessingThread("localhost" + (9000 + i), cdl); - nodes.add(local); - local.start(); - log.info("a: i={}", i); - } - cdl.await(); //达到quorum 开始发送更新 - } catch (InterruptedException e) { - log.error(e.getMessage(), e); - } - } -} \ No newline at end of file diff --git a/concurrency/src/main/java/com/github/kuangcp/countlatch/ProcessingThread.java b/concurrency/src/main/java/com/github/kuangcp/countlatch/ProcessingThread.java deleted file mode 100644 index 7c07a758..00000000 --- a/concurrency/src/main/java/com/github/kuangcp/countlatch/ProcessingThread.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.github.kuangcp.countlatch; - -import java.util.concurrent.CountDownLatch; - -/** - * created by https://gitee.com/gin9 - * - * @author kuangcp on 3/4/19-7:56 AM - */ -class ProcessingThread extends Thread { - - private final String ids; - private final CountDownLatch latch; - - public ProcessingThread(String id, CountDownLatch latch) { - this.ids = id; - this.latch = latch; - } - - public void init() { - // 节点初始化 - latch.countDown(); - } - - public void run() { - init(); - System.out.println(this.ids); - } -} diff --git a/concurrency/src/main/java/com/github/kuangcp/forkjoin/ForkJoinEasyDemo.groovy b/concurrency/src/main/java/com/github/kuangcp/forkjoin/ForkJoinEasyDemo.groovy index 2814f0a2..ed4c6b93 100644 --- a/concurrency/src/main/java/com/github/kuangcp/forkjoin/ForkJoinEasyDemo.groovy +++ b/concurrency/src/main/java/com/github/kuangcp/forkjoin/ForkJoinEasyDemo.groovy @@ -1,32 +1,34 @@ package com.github.kuangcp.forkjoin import java.util.concurrent.ForkJoinPool + /** * Created by https://github.com/kuangcp on 17-8-18 下午9:46 - * TODO 使用ForkJoin的简单例子,但是没看懂 */ // 生成数据 List lu = new ArrayList() for (int i = 0; i < 12; i++) { - Element element = new Element(System.currentTimeMillis()) - lu.add(element) - sleep(2) + Element element = new Element(i + 1) + lu.add(element) + sleep(2) } + // 打乱顺序 Collections.shuffle(lu) for (Element el : lu) { - println(el.toString()) + println(el.toString()) } + elements = lu.toArray(new Element[0])// 传入空数组,省掉空间分配 ElementSorter sorter = new ElementSorter(elements as Element[]) // 使用任务池封装一层再调用,或者直接运行方法都可以 //sorter.compute() ForkJoinPool pool = new ForkJoinPool(4) -pool.invoke(sorter) // StackOverflowError 这个可能是因为数据量太小了会出现 +pool.invoke(sorter) println("排序后") for (Element element : sorter.getResult()) { - println(element.toString()) + println(element.toString()) } \ No newline at end of file diff --git a/concurrency/src/main/java/situation/timoutpool/CreateNewPool.java b/concurrency/src/main/java/situation/timoutpool/CreateNewPool.java new file mode 100644 index 00000000..e1e654e1 --- /dev/null +++ b/concurrency/src/main/java/situation/timoutpool/CreateNewPool.java @@ -0,0 +1,55 @@ +package situation.timoutpool; + +import lombok.extern.slf4j.Slf4j; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +/** + * 方案: 使用临时线程池 + * 缺陷 遇到突发请求流量时线程大增,有拖垮服务器的风险 + * + * @author https://github.com/kuangcp on 2021-09-04 23:23 + */ +@Slf4j +public class CreateNewPool implements TaskExecutor { + + @Override + public Result execute(Param param, long timeout, TimeUnit timeUnit) { + final LinkedBlockingQueue workQueue = new LinkedBlockingQueue<>(1000); + final Result result = Result.builder().dataList(Collections.synchronizedList(new ArrayList<>())).build(); + final ThreadPoolExecutor pool = new ThreadPoolExecutor(3, 3, 1, TimeUnit.SECONDS, + workQueue, new ThreadPoolExecutor.DiscardPolicy()); + for (int i = 0; i < param.getTotal(); i++) { + final Param tmpParam = Param.builder().start(i).build(); + pool.execute(() -> { + try { + TimeUnit.MILLISECONDS.sleep(ThreadLocalRandom.current().nextInt(600) + 200); + } catch (InterruptedException e) { + e.printStackTrace(); + } +// log.info("tmpParam={}", tmpParam); + result.getDataList().add(tmpParam.toString()); + }); + } + + try { + pool.shutdown(); + final boolean complete = pool.awaitTermination(timeout, timeUnit); + log.info("complete={}", complete); + if (!complete) { + log.info("workQueue={}", workQueue.size()); + } + } catch (Exception e) { + log.error("", e); + } + + + return result; + } + +} diff --git a/concurrency/src/main/java/situation/timoutpool/Param.java b/concurrency/src/main/java/situation/timoutpool/Param.java new file mode 100644 index 00000000..45d235cd --- /dev/null +++ b/concurrency/src/main/java/situation/timoutpool/Param.java @@ -0,0 +1,14 @@ +package situation.timoutpool; + +import lombok.Builder; +import lombok.Data; + +/** + * @author https://github.com/kuangcp on 2021-09-04 23:27 + */ +@Data +@Builder +public class Param { + private int start; + private int total; +} diff --git a/concurrency/src/main/java/situation/timoutpool/Readme.md b/concurrency/src/main/java/situation/timoutpool/Readme.md new file mode 100644 index 00000000..e69de29b diff --git a/concurrency/src/main/java/situation/timoutpool/Result.java b/concurrency/src/main/java/situation/timoutpool/Result.java new file mode 100644 index 00000000..be7b7e35 --- /dev/null +++ b/concurrency/src/main/java/situation/timoutpool/Result.java @@ -0,0 +1,16 @@ +package situation.timoutpool; + +import lombok.Builder; +import lombok.Data; + +import java.util.List; + +/** + * @author https://github.com/kuangcp on 2021-09-04 23:28 + */ +@Data +@Builder +public class Result { + + private List dataList; +} diff --git a/concurrency/src/main/java/situation/timoutpool/TaskExecutor.java b/concurrency/src/main/java/situation/timoutpool/TaskExecutor.java new file mode 100644 index 00000000..07923e35 --- /dev/null +++ b/concurrency/src/main/java/situation/timoutpool/TaskExecutor.java @@ -0,0 +1,11 @@ +package situation.timoutpool; + +import java.util.concurrent.TimeUnit; + +/** + * @author https://github.com/kuangcp on 2021-09-04 23:23 + */ +public interface TaskExecutor { + + R execute(P param, long timeout, TimeUnit timeUnit); +} diff --git a/concurrency/src/main/java/thread/UseThreadPool.java b/concurrency/src/main/java/thread/UseThreadPool.java index 03153486..9a44e642 100644 --- a/concurrency/src/main/java/thread/UseThreadPool.java +++ b/concurrency/src/main/java/thread/UseThreadPool.java @@ -12,28 +12,26 @@ */ public class UseThreadPool { - private void baseType() { - // 创建有缓存功能的线程池 - ExecutorService a = Executors.newCachedThreadPool(); + private void baseType() { + // 创建有缓存功能的线程池 + ExecutorService a = Executors.newCachedThreadPool(); - // 创建具有固定大小的线程池 - ExecutorService b = Executors.newFixedThreadPool(1); + // 创建具有固定大小的线程池 + ExecutorService b = Executors.newFixedThreadPool(1); - // 创建单线程的线程池 - ExecutorService c = Executors.newSingleThreadExecutor(); + // 创建单线程的线程池 + ExecutorService c = Executors.newSingleThreadExecutor(); - // 创建具有定时功能的线程池 指定基本线程池数量, 该线程池的队列是无限队列 - ScheduledExecutorService d = Executors.newScheduledThreadPool(1); + // 创建具有定时功能的线程池 指定基本线程池数量, 该线程池的队列是无限队列 + ScheduledExecutorService d = Executors.newScheduledThreadPool(1); - /////////////// - - // 创建单线程的线程池,指定延迟 - ScheduledExecutorService e = Executors.newSingleThreadScheduledExecutor(); + // 创建单线程的线程池,指定延迟 + ScheduledExecutorService e = Executors.newSingleThreadScheduledExecutor(); // a.submit(); 提交任务 // a.execute(); 执行任务 // a.shutdown(); 关闭线程池, 等待任务执行完成 // a.shutdownNow(); 关闭线程池, 立即关闭 - } + } } diff --git a/concurrency/src/test/java/com/github/kuangcp/latch/CountDownLatchTest.java b/concurrency/src/test/java/com/github/kuangcp/latch/CountDownLatchTest.java new file mode 100644 index 00000000..82742add --- /dev/null +++ b/concurrency/src/test/java/com/github/kuangcp/latch/CountDownLatchTest.java @@ -0,0 +1,37 @@ +package com.github.kuangcp.latch; + +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +/** + * @author https://github.com/kuangcp on 2021-09-04 23:02 + */ +@Slf4j +public class CountDownLatchTest { + + @Test + public void testFirstUse() throws Exception { + final ExecutorService pool = Executors.newFixedThreadPool(3); + final CountDownLatch latch = new CountDownLatch(10); + + for (int i = 0; i < 10; i++) { + int finalI = i; + pool.execute(() -> { + try { + TimeUnit.SECONDS.sleep(1); + log.info("fin: {}", finalI); + latch.countDown(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + }); + } + + latch.await(3, TimeUnit.SECONDS); + } +} diff --git a/concurrency/src/test/java/com/github/kuangcp/lock/pvp/PlayerTest.java b/concurrency/src/test/java/com/github/kuangcp/lock/pvp/PlayerTest.java index 2a8a3c61..7deca7be 100644 --- a/concurrency/src/test/java/com/github/kuangcp/lock/pvp/PlayerTest.java +++ b/concurrency/src/test/java/com/github/kuangcp/lock/pvp/PlayerTest.java @@ -1,6 +1,9 @@ package com.github.kuangcp.lock.pvp; import com.github.kuangcp.tuple.Tuple2; +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.Test; + import java.time.Duration; import java.util.ArrayList; import java.util.HashMap; @@ -8,8 +11,6 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadLocalRandom; -import lombok.extern.slf4j.Slf4j; -import org.junit.jupiter.api.Test; /** * @author kuangcp on 2019-04-21 11:14 AM @@ -19,7 +20,7 @@ @Slf4j public class PlayerTest { - private Map playerMap = new HashMap<>(); + private final Map playerMap = new HashMap<>(); private void arenaLogic(Player a, Player b) throws InterruptedException { log.info("enter arena: a={} b={}", a, b); diff --git a/concurrency/src/test/java/situation/timoutpool/TimeoutPoolTest.java b/concurrency/src/test/java/situation/timoutpool/TimeoutPoolTest.java new file mode 100644 index 00000000..b26de726 --- /dev/null +++ b/concurrency/src/test/java/situation/timoutpool/TimeoutPoolTest.java @@ -0,0 +1,37 @@ +package situation.timoutpool; + +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.Test; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.MatcherAssert.assertThat; + +/** + * @author https://github.com/kuangcp on 2021-09-04 23:42 + */ +@Slf4j +public class TimeoutPoolTest { + + @Test + public void testCreateNew() throws Exception { + int loop = 6000; + final CountDownLatch latch = new CountDownLatch(loop); + final ExecutorService exe = Executors.newFixedThreadPool(loop / 15); + for (int i = 0; i < loop; i++) { + exe.execute(() -> { + final CreateNewPool pool = new CreateNewPool(); + final long start = System.nanoTime(); + final Result result = pool.execute(Param.builder().start(1).total(40).build(), 5, TimeUnit.SECONDS); + log.info("result={} {}ms", result, (System.nanoTime() - start) / 1000_000); + latch.countDown(); + assertThat(result.getDataList(), equalTo(40)); + }); + } + latch.await(); + } +} \ No newline at end of file diff --git a/dependency.gradle b/dependency.gradle index e4a58146..6b1b35d8 100644 --- a/dependency.gradle +++ b/dependency.gradle @@ -5,7 +5,7 @@ ext { ver = [ logback : '1.2.5' , jedis : '2.9.0' - , junit : '4.12' + , junit : '4.13.2' , groovy : '3.0.8' , mail : '1.4.7' , jackson : '2.9.5' @@ -23,6 +23,7 @@ ext { libs = [ // mine tool "kcp-tool" : "com.github.kuangcp:kcp-tool:$ver.kcp_tool" + , "kcp-tuple" : "com.github.kuangcp:kcp-tuple:$ver.kcp_tool" // tool , "lombok" : "org.projectlombok:lombok:1.18.2" @@ -49,6 +50,7 @@ ext { // network , "ok-http" : "com.squareup.okhttp3:okhttp:3.13.1" , "netty" : "io.netty:netty-all:$ver.netty" + , "blade-mvc" : "com.bladejava:blade-mvc:2.0.15.RELEASE" // mail , "mail" : "javax.mail:mail:$ver.mail" From 0346e2703efa900593975a6f2f7c3dcf4aa13c9e Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Sun, 5 Sep 2021 02:28:44 +0800 Subject: [PATCH 208/476] *) slim image size --- concurrency/.dockerignore | 2 +- concurrency/Dockerfile | 15 ++------------- concurrency/Readme.md | 10 ++++++++-- concurrency/run.sh | 15 +++++++++++++++ 4 files changed, 26 insertions(+), 16 deletions(-) create mode 100644 concurrency/run.sh diff --git a/concurrency/.dockerignore b/concurrency/.dockerignore index 44946a96..f5c464bd 100644 --- a/concurrency/.dockerignore +++ b/concurrency/.dockerignore @@ -1,2 +1,2 @@ out/ -!out/build/distributions/concurrency.tar \ No newline at end of file +!out/build/distributions/concurrency/ \ No newline at end of file diff --git a/concurrency/Dockerfile b/concurrency/Dockerfile index 7d382c8a..759c9fdd 100644 --- a/concurrency/Dockerfile +++ b/concurrency/Dockerfile @@ -2,17 +2,6 @@ FROM mythkuang/jdk-alpine-cst:8.181 WORKDIR /app -COPY ./out/build/distributions/concurrency.tar /app +COPY ./out/build/distributions/concurrency/ /app -RUN tar -xvf concurrency.tar && \ - sed -i 's/DEFAULT_JVM_OPTS=""/DEFAULT_JVM_OPTS="-server \ - -Xms200m \ - -Xmx200m \ - -Xss256k\ - -verbose:gc\ - -XX:+PrintGCDetails\ - -XX:+PrintGCDateStamps\ - -Xloggc:\/app\/gc.log\ - -XX:ErrorFile=\/app\/hs_err_pid%p.log"/g' /app/concurrency/bin/concurrency - -CMD ["/app/concurrency/bin/concurrency"] +CMD ["/app/bin/concurrency"] diff --git a/concurrency/Readme.md b/concurrency/Readme.md index 72d5489b..ca4b1ed1 100644 --- a/concurrency/Readme.md +++ b/concurrency/Readme.md @@ -1,8 +1,14 @@ # Java中的并发 > [笔记](https://github.com/Kuangcp/Note/blob/master/Java/AdvancedLearning/Concurrency.md) -应尽量避免线程的阻塞和等待,因为这是在浪费CPU gradle clean build -x test + docker build -t con . -docker run --rm -p 9000:9000 --cpuset-cpus="1" --cpu-quota=5000 --cpu-period=5000 -it con \ No newline at end of file + +docker run --cap-add=SYS_PTRACE --name con-test --rm -p 9000:9000 --cpuset-cpus="1" --cpu-quota=5000 --cpu-period=5000 -it con + + +`--cap-add=SYS_PTRACE` + +解决 jmap -heap 1 时报错: Can't attach to the process: ptrace \ No newline at end of file diff --git a/concurrency/run.sh b/concurrency/run.sh new file mode 100644 index 00000000..e578f1f7 --- /dev/null +++ b/concurrency/run.sh @@ -0,0 +1,15 @@ +gradle clean build -x test + +tar xf ./out/build/distributions/concurrency.tar -C ./out/build/distributions/ + +sed -i 's/DEFAULT_JVM_OPTS=""/DEFAULT_JVM_OPTS="-server \ + -Xms200m \ + -Xmx200m \ + -Xss256k\ + -verbose:gc\ + -XX:+PrintGCDetails\ + -XX:+PrintGCDateStamps\ + -Xloggc:\/app\/gc.log\ + -XX:ErrorFile=\/app\/hs_err_pid%p.log"/g' ./out/build/distributions/concurrency/bin/concurrency + +docker build -t con . \ No newline at end of file From de869d4874e0ce835e97f447a1999335046cde34 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Sun, 5 Sep 2021 02:43:19 +0800 Subject: [PATCH 209/476] *) rename to web --- concurrency/Readme.md | 12 ------------ concurrency/build.gradle | 2 +- concurrency/{run.sh => run-web.sh} | 5 ++++- .../src/main/java/{cmd => web}/Application.java | 2 +- .../situation/TimeoutPoolController.java | 2 +- 5 files changed, 7 insertions(+), 16 deletions(-) rename concurrency/{run.sh => run-web.sh} (63%) rename concurrency/src/main/java/{cmd => web}/Application.java (94%) rename concurrency/src/main/java/{cmd => web}/situation/TimeoutPoolController.java (98%) diff --git a/concurrency/Readme.md b/concurrency/Readme.md index ca4b1ed1..c14a4c91 100644 --- a/concurrency/Readme.md +++ b/concurrency/Readme.md @@ -1,14 +1,2 @@ # Java中的并发 > [笔记](https://github.com/Kuangcp/Note/blob/master/Java/AdvancedLearning/Concurrency.md) - - -gradle clean build -x test - -docker build -t con . - -docker run --cap-add=SYS_PTRACE --name con-test --rm -p 9000:9000 --cpuset-cpus="1" --cpu-quota=5000 --cpu-period=5000 -it con - - -`--cap-add=SYS_PTRACE` - -解决 jmap -heap 1 时报错: Can't attach to the process: ptrace \ No newline at end of file diff --git a/concurrency/build.gradle b/concurrency/build.gradle index 4df76b88..fc4ac4eb 100644 --- a/concurrency/build.gradle +++ b/concurrency/build.gradle @@ -5,7 +5,7 @@ plugins { } application { - mainClass = 'cmd.Application' + mainClass = 'web.Application' } dependencies { diff --git a/concurrency/run.sh b/concurrency/run-web.sh similarity index 63% rename from concurrency/run.sh rename to concurrency/run-web.sh index e578f1f7..f1db6a86 100644 --- a/concurrency/run.sh +++ b/concurrency/run-web.sh @@ -12,4 +12,7 @@ sed -i 's/DEFAULT_JVM_OPTS=""/DEFAULT_JVM_OPTS="-server \ -Xloggc:\/app\/gc.log\ -XX:ErrorFile=\/app\/hs_err_pid%p.log"/g' ./out/build/distributions/concurrency/bin/concurrency -docker build -t con . \ No newline at end of file +docker build -t con . + +# docker run --cap-add=SYS_PTRACE --name con-test --rm -p 9000:9000 --cpuset-cpus="1" --cpu-quota=5000 --cpu-period=5000 -it con +# docker run --cap-add=SYS_PTRACE --name con-test --rm -p 9000:9000 -it con diff --git a/concurrency/src/main/java/cmd/Application.java b/concurrency/src/main/java/web/Application.java similarity index 94% rename from concurrency/src/main/java/cmd/Application.java rename to concurrency/src/main/java/web/Application.java index 44fde604..0610d470 100644 --- a/concurrency/src/main/java/cmd/Application.java +++ b/concurrency/src/main/java/web/Application.java @@ -1,4 +1,4 @@ -package cmd; +package web; import com.blade.Blade; diff --git a/concurrency/src/main/java/cmd/situation/TimeoutPoolController.java b/concurrency/src/main/java/web/situation/TimeoutPoolController.java similarity index 98% rename from concurrency/src/main/java/cmd/situation/TimeoutPoolController.java rename to concurrency/src/main/java/web/situation/TimeoutPoolController.java index d704b2b4..bb1473eb 100644 --- a/concurrency/src/main/java/cmd/situation/TimeoutPoolController.java +++ b/concurrency/src/main/java/web/situation/TimeoutPoolController.java @@ -1,4 +1,4 @@ -package cmd.situation; +package web.situation; import com.blade.mvc.annotation.GetRoute; import com.blade.mvc.annotation.Path; From 6d9dd3e5e5d1167724d475a711edfcbff9967dd4 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Sun, 5 Sep 2021 03:07:52 +0800 Subject: [PATCH 210/476] *) package --- build.gradle | 3 +- .../situation/timoutpool/CreateNewPool.java | 3 + .../situation/timoutpool/TimeoutFuture.java | 18 ++++++ .../timoutpool/{ => base}/Param.java | 2 +- .../timoutpool/{ => base}/Result.java | 2 +- .../timoutpool/{ => base}/TaskExecutor.java | 2 +- .../src/main/java/thread/ThreadJoinDemo.java | 5 +- .../java/thread/ThreadStatusTransfer.java | 5 +- .../web/situation/TimeoutPoolController.java | 4 +- .../situation/timoutpool/TimeoutPoolTest.java | 2 + dependency.gradle | 3 +- gui/build.gradle | 13 +++++ .../virusbroadcast/DefaultThreadFactory.java | 34 +++++++++++ .../virusbroadcast/VirusBroadcast.java | 58 +++++-------------- io/build.gradle | 4 +- 15 files changed, 101 insertions(+), 57 deletions(-) create mode 100644 concurrency/src/main/java/situation/timoutpool/TimeoutFuture.java rename concurrency/src/main/java/situation/timoutpool/{ => base}/Param.java (84%) rename concurrency/src/main/java/situation/timoutpool/{ => base}/Result.java (85%) rename concurrency/src/main/java/situation/timoutpool/{ => base}/TaskExecutor.java (85%) create mode 100644 gui/src/main/java/com/github/kuangcp/virusbroadcast/DefaultThreadFactory.java diff --git a/build.gradle b/build.gradle index dbe7301c..cc5e1aa1 100644 --- a/build.gradle +++ b/build.gradle @@ -38,11 +38,12 @@ subprojects { maven { url = kuangcp } - jcenter() } // All sub-modules add the following dependencies dependencies { + implementation libs['kcp-tool'] + annotationProcessor libs['lombok'] compileOnly libs['lombok'] testAnnotationProcessor libs['lombok'] diff --git a/concurrency/src/main/java/situation/timoutpool/CreateNewPool.java b/concurrency/src/main/java/situation/timoutpool/CreateNewPool.java index e1e654e1..3f5b0c59 100644 --- a/concurrency/src/main/java/situation/timoutpool/CreateNewPool.java +++ b/concurrency/src/main/java/situation/timoutpool/CreateNewPool.java @@ -1,6 +1,9 @@ package situation.timoutpool; import lombok.extern.slf4j.Slf4j; +import situation.timoutpool.base.Param; +import situation.timoutpool.base.Result; +import situation.timoutpool.base.TaskExecutor; import java.util.ArrayList; import java.util.Collections; diff --git a/concurrency/src/main/java/situation/timoutpool/TimeoutFuture.java b/concurrency/src/main/java/situation/timoutpool/TimeoutFuture.java new file mode 100644 index 00000000..eaa07528 --- /dev/null +++ b/concurrency/src/main/java/situation/timoutpool/TimeoutFuture.java @@ -0,0 +1,18 @@ +package situation.timoutpool; + +import situation.timoutpool.base.Param; +import situation.timoutpool.base.Result; +import situation.timoutpool.base.TaskExecutor; + +import java.util.concurrent.TimeUnit; + +/** + * @author https://github.com/kuangcp on 2021-09-05 02:45 + */ +public class TimeoutFuture implements TaskExecutor { + + @Override + public Result execute(Param param, long timeout, TimeUnit timeUnit) { + return null; + } +} diff --git a/concurrency/src/main/java/situation/timoutpool/Param.java b/concurrency/src/main/java/situation/timoutpool/base/Param.java similarity index 84% rename from concurrency/src/main/java/situation/timoutpool/Param.java rename to concurrency/src/main/java/situation/timoutpool/base/Param.java index 45d235cd..8f2ae467 100644 --- a/concurrency/src/main/java/situation/timoutpool/Param.java +++ b/concurrency/src/main/java/situation/timoutpool/base/Param.java @@ -1,4 +1,4 @@ -package situation.timoutpool; +package situation.timoutpool.base; import lombok.Builder; import lombok.Data; diff --git a/concurrency/src/main/java/situation/timoutpool/Result.java b/concurrency/src/main/java/situation/timoutpool/base/Result.java similarity index 85% rename from concurrency/src/main/java/situation/timoutpool/Result.java rename to concurrency/src/main/java/situation/timoutpool/base/Result.java index be7b7e35..01ef087f 100644 --- a/concurrency/src/main/java/situation/timoutpool/Result.java +++ b/concurrency/src/main/java/situation/timoutpool/base/Result.java @@ -1,4 +1,4 @@ -package situation.timoutpool; +package situation.timoutpool.base; import lombok.Builder; import lombok.Data; diff --git a/concurrency/src/main/java/situation/timoutpool/TaskExecutor.java b/concurrency/src/main/java/situation/timoutpool/base/TaskExecutor.java similarity index 85% rename from concurrency/src/main/java/situation/timoutpool/TaskExecutor.java rename to concurrency/src/main/java/situation/timoutpool/base/TaskExecutor.java index 07923e35..43ed8c51 100644 --- a/concurrency/src/main/java/situation/timoutpool/TaskExecutor.java +++ b/concurrency/src/main/java/situation/timoutpool/base/TaskExecutor.java @@ -1,4 +1,4 @@ -package situation.timoutpool; +package situation.timoutpool.base; import java.util.concurrent.TimeUnit; diff --git a/concurrency/src/main/java/thread/ThreadJoinDemo.java b/concurrency/src/main/java/thread/ThreadJoinDemo.java index 4c86777d..61480ea8 100644 --- a/concurrency/src/main/java/thread/ThreadJoinDemo.java +++ b/concurrency/src/main/java/thread/ThreadJoinDemo.java @@ -1,8 +1,9 @@ package thread; -import java.util.concurrent.TimeUnit; import lombok.extern.slf4j.Slf4j; +import java.util.concurrent.TimeUnit; + /** * @author kuangcp on 2019-04-22 9:55 AM */ @@ -11,7 +12,7 @@ public class ThreadJoinDemo { static class Domino implements Runnable { - private Thread thread; + private final Thread thread; public Domino(Thread thread) { this.thread = thread; diff --git a/concurrency/src/main/java/thread/ThreadStatusTransfer.java b/concurrency/src/main/java/thread/ThreadStatusTransfer.java index d0780d65..35acb93c 100644 --- a/concurrency/src/main/java/thread/ThreadStatusTransfer.java +++ b/concurrency/src/main/java/thread/ThreadStatusTransfer.java @@ -14,14 +14,13 @@ public class ThreadStatusTransfer { private static boolean flag = true; - private static Object lock = new Object(); + private static final Object lock = new Object(); static class Wait implements Runnable { @Override public void run() { - // IDE 警告: - // synchronized 一个非 final 的变量, 容易发生 当该对象的引用地址更改后, 同步块里的代码能被并行运行, 因为锁的是真实堆上的对象 + // 如果 synchronized 一个非 final 的变量, 容易发生 当该对象的引用地址更改后, 同步块里的代码可以被并发执行,因为锁的对象发生变化 synchronized (lock) { while (flag) { try { diff --git a/concurrency/src/main/java/web/situation/TimeoutPoolController.java b/concurrency/src/main/java/web/situation/TimeoutPoolController.java index bb1473eb..f18d7225 100644 --- a/concurrency/src/main/java/web/situation/TimeoutPoolController.java +++ b/concurrency/src/main/java/web/situation/TimeoutPoolController.java @@ -5,8 +5,8 @@ import com.blade.mvc.http.Response; import lombok.extern.slf4j.Slf4j; import situation.timoutpool.CreateNewPool; -import situation.timoutpool.Param; -import situation.timoutpool.Result; +import situation.timoutpool.base.Param; +import situation.timoutpool.base.Result; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; diff --git a/concurrency/src/test/java/situation/timoutpool/TimeoutPoolTest.java b/concurrency/src/test/java/situation/timoutpool/TimeoutPoolTest.java index b26de726..ba562368 100644 --- a/concurrency/src/test/java/situation/timoutpool/TimeoutPoolTest.java +++ b/concurrency/src/test/java/situation/timoutpool/TimeoutPoolTest.java @@ -2,6 +2,8 @@ import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.Test; +import situation.timoutpool.base.Param; +import situation.timoutpool.base.Result; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; diff --git a/dependency.gradle b/dependency.gradle index 6b1b35d8..c2502e2d 100644 --- a/dependency.gradle +++ b/dependency.gradle @@ -10,7 +10,7 @@ ext { , mail : '1.4.7' , jackson : '2.9.5' , hamcrest: '1.3' - , kcp_tool: '1.0.7' + , kcp_tool: '1.0.8' , netty : '4.1.67.Final' , guava : '30.1.1-jre' , jmh : '1.33' @@ -24,6 +24,7 @@ ext { // mine tool "kcp-tool" : "com.github.kuangcp:kcp-tool:$ver.kcp_tool" , "kcp-tuple" : "com.github.kuangcp:kcp-tuple:$ver.kcp_tool" + , "kcp-core" : "com.github.kuangcp:kcp-core:$ver.kcp_tool" // tool , "lombok" : "org.projectlombok:lombok:1.18.2" diff --git a/gui/build.gradle b/gui/build.gradle index 0974dde3..6305ab3c 100644 --- a/gui/build.gradle +++ b/gui/build.gradle @@ -1,3 +1,7 @@ +plugins { + id 'application' +} + task uberJar(type: Jar) { archiveClassifier = 'all-dependency' @@ -9,4 +13,13 @@ task uberJar(type: Jar) { manifest { attributes 'Main-Class': 'com.github.kuangcp.notepad.Note' } +} + +application { + mainClass = 'com.github.kuangcp.notepad.Note' +} + + +dependencies { + implementation libs['kcp-core'] } \ No newline at end of file diff --git a/gui/src/main/java/com/github/kuangcp/virusbroadcast/DefaultThreadFactory.java b/gui/src/main/java/com/github/kuangcp/virusbroadcast/DefaultThreadFactory.java new file mode 100644 index 00000000..72d41369 --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/virusbroadcast/DefaultThreadFactory.java @@ -0,0 +1,34 @@ +package com.github.kuangcp.virusbroadcast; + +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * @author https://github.com/kuangcp on 2021-09-05 03:05 + */ +class DefaultThreadFactory implements ThreadFactory { + + private final AtomicInteger threadNumber = new AtomicInteger(1); + private final ThreadGroup group; + private final String namePrefix; + + DefaultThreadFactory(String namePrefix) { + SecurityManager var1 = System.getSecurityManager(); + this.group = var1 != null ? var1.getThreadGroup() : Thread.currentThread().getThreadGroup(); + this.namePrefix = namePrefix; + } + + public Thread newThread(Runnable runnable) { + Thread var2 = new Thread(this.group, runnable, + this.namePrefix + "-" + this.threadNumber.getAndIncrement(), 0L); + if (var2.isDaemon()) { + var2.setDaemon(false); + } + + if (var2.getPriority() != 5) { + var2.setPriority(5); + } + + return var2; + } +} diff --git a/gui/src/main/java/com/github/kuangcp/virusbroadcast/VirusBroadcast.java b/gui/src/main/java/com/github/kuangcp/virusbroadcast/VirusBroadcast.java index f665ab67..ad0f7793 100644 --- a/gui/src/main/java/com/github/kuangcp/virusbroadcast/VirusBroadcast.java +++ b/gui/src/main/java/com/github/kuangcp/virusbroadcast/VirusBroadcast.java @@ -2,56 +2,28 @@ import com.github.kuangcp.virusbroadcast.domain.City; import com.github.kuangcp.virusbroadcast.gui.DisplayPanel; + +import javax.swing.*; import java.util.concurrent.ExecutorService; import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; -import javax.swing.JFrame; public class VirusBroadcast { - private static final ExecutorService executors = new ThreadPoolExecutor(2, 2, 0L, - TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(), new DefaultThreadFactory("virus")); - - public static void main(String[] args) { - DisplayPanel panel = new DisplayPanel(); - JFrame frame = new JFrame(); - frame.add(panel); - frame.setSize(1000, 800); - frame.setLocationRelativeTo(null); - frame.setVisible(true); - frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - - executors.submit(panel); - executors.submit(City.getInstance()); - } -} - -class DefaultThreadFactory implements ThreadFactory { + private static final ExecutorService executors = new ThreadPoolExecutor(2, 2, + 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(), new DefaultThreadFactory("virus")); - private final AtomicInteger threadNumber = new AtomicInteger(1); - private final ThreadGroup group; - private final String namePrefix; + public static void main(String[] args) { + DisplayPanel panel = new DisplayPanel(); + JFrame frame = new JFrame(); + frame.add(panel); + frame.setSize(1000, 800); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - DefaultThreadFactory(String namePrefix) { - SecurityManager var1 = System.getSecurityManager(); - this.group = var1 != null ? var1.getThreadGroup() : Thread.currentThread().getThreadGroup(); - this.namePrefix = namePrefix; - } - - public Thread newThread(Runnable runnable) { - Thread var2 = new Thread(this.group, runnable, - this.namePrefix + "-" + this.threadNumber.getAndIncrement(), 0L); - if (var2.isDaemon()) { - var2.setDaemon(false); - } - - if (var2.getPriority() != 5) { - var2.setPriority(5); + executors.submit(panel); + executors.submit(City.getInstance()); } - - return var2; - } -} \ No newline at end of file +} diff --git a/io/build.gradle b/io/build.gradle index 00553fed..044aab2c 100644 --- a/io/build.gradle +++ b/io/build.gradle @@ -1,4 +1,4 @@ dependencies { - implementation project(":common-config") - implementation libs['common-lang'] + implementation project(":common-config") + implementation libs['common-lang'] } From bc644b61cc14c89abcd3e19fd6f5f004f847d9ea Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Mon, 6 Sep 2021 01:40:49 +0800 Subject: [PATCH 211/476] *) import tank --- gui/build.gradle | 21 +- .../kuangcp/tank/constant/SettingCommand.java | 19 + .../com/github/kuangcp/tank/util/Audio.java | 80 ++ .../com/github/kuangcp/tank/util/Saved.java | 478 ++++++++++++ .../com/github/kuangcp/tank/util/Setting.java | 108 +++ .../github/kuangcp/tank/util/TankTool.java | 293 ++++++++ .../com/github/kuangcp/tank/v1/Brick.java | 41 ++ .../com/github/kuangcp/tank/v1/Demons.java | 554 ++++++++++++++ .../java/com/github/kuangcp/tank/v1/Hero.java | 170 +++++ .../com/github/kuangcp/tank/v1/Hinder.java | 45 ++ .../java/com/github/kuangcp/tank/v1/Iron.java | 10 + .../java/com/github/kuangcp/tank/v1/Tank.java | 82 +++ .../com/github/kuangcp/tank/v1/TankGame1.java | 238 ++++++ .../com/github/kuangcp/tank/v2/MyPanel.java | 265 +++++++ .../java/com/github/kuangcp/tank/v2/Shot.java | 71 ++ .../com/github/kuangcp/tank/v2/TankGame2.java | 89 +++ .../com/github/kuangcp/tank/v2/Thread1.java | 70 ++ .../com/github/kuangcp/tank/v2/Thread2.java | 84 +++ .../com/github/kuangcp/tank/v2/Thread3.java | 41 ++ .../com/github/kuangcp/tank/v2/Thread4.java | 77 ++ .../java/com/github/kuangcp/tank/v3/Bomb.java | 36 + .../com/github/kuangcp/tank/v3/GameMain.java | 10 + .../java/com/github/kuangcp/tank/v3/KE.java | 10 + .../com/github/kuangcp/tank/v3/MyPanel3.java | 682 ++++++++++++++++++ .../github/kuangcp/tank/v3/MyPanel301.java | 150 ++++ .../github/kuangcp/tank/v3/MyPanel302.java | 109 +++ .../github/kuangcp/tank/v3/MyPanel303.java | 41 ++ .../github/kuangcp/tank/v3/PressedTwo.java | 90 +++ .../com/github/kuangcp/tank/v3/TankFrame.java | 191 +++++ 29 files changed, 4138 insertions(+), 17 deletions(-) create mode 100644 gui/src/main/java/com/github/kuangcp/tank/constant/SettingCommand.java create mode 100644 gui/src/main/java/com/github/kuangcp/tank/util/Audio.java create mode 100644 gui/src/main/java/com/github/kuangcp/tank/util/Saved.java create mode 100644 gui/src/main/java/com/github/kuangcp/tank/util/Setting.java create mode 100644 gui/src/main/java/com/github/kuangcp/tank/util/TankTool.java create mode 100644 gui/src/main/java/com/github/kuangcp/tank/v1/Brick.java create mode 100644 gui/src/main/java/com/github/kuangcp/tank/v1/Demons.java create mode 100644 gui/src/main/java/com/github/kuangcp/tank/v1/Hero.java create mode 100644 gui/src/main/java/com/github/kuangcp/tank/v1/Hinder.java create mode 100644 gui/src/main/java/com/github/kuangcp/tank/v1/Iron.java create mode 100644 gui/src/main/java/com/github/kuangcp/tank/v1/Tank.java create mode 100644 gui/src/main/java/com/github/kuangcp/tank/v1/TankGame1.java create mode 100644 gui/src/main/java/com/github/kuangcp/tank/v2/MyPanel.java create mode 100644 gui/src/main/java/com/github/kuangcp/tank/v2/Shot.java create mode 100644 gui/src/main/java/com/github/kuangcp/tank/v2/TankGame2.java create mode 100644 gui/src/main/java/com/github/kuangcp/tank/v2/Thread1.java create mode 100644 gui/src/main/java/com/github/kuangcp/tank/v2/Thread2.java create mode 100644 gui/src/main/java/com/github/kuangcp/tank/v2/Thread3.java create mode 100644 gui/src/main/java/com/github/kuangcp/tank/v2/Thread4.java create mode 100644 gui/src/main/java/com/github/kuangcp/tank/v3/Bomb.java create mode 100644 gui/src/main/java/com/github/kuangcp/tank/v3/GameMain.java create mode 100644 gui/src/main/java/com/github/kuangcp/tank/v3/KE.java create mode 100644 gui/src/main/java/com/github/kuangcp/tank/v3/MyPanel3.java create mode 100644 gui/src/main/java/com/github/kuangcp/tank/v3/MyPanel301.java create mode 100644 gui/src/main/java/com/github/kuangcp/tank/v3/MyPanel302.java create mode 100644 gui/src/main/java/com/github/kuangcp/tank/v3/MyPanel303.java create mode 100644 gui/src/main/java/com/github/kuangcp/tank/v3/PressedTwo.java create mode 100644 gui/src/main/java/com/github/kuangcp/tank/v3/TankFrame.java diff --git a/gui/build.gradle b/gui/build.gradle index 6305ab3c..90a03422 100644 --- a/gui/build.gradle +++ b/gui/build.gradle @@ -1,25 +1,12 @@ plugins { - id 'application' -} - -task uberJar(type: Jar) { - archiveClassifier = 'all-dependency' - - from sourceSets.main.output - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith('jar') }.collect { zipTree(it) } - } - manifest { - attributes 'Main-Class': 'com.github.kuangcp.notepad.Note' - } + id 'application' } application { - mainClass = 'com.github.kuangcp.notepad.Note' +// mainClass = 'com.github.kuangcp.notepad.Note' + mainClass = 'com.github.kuangcp.tank.v3.GameMain' } - dependencies { - implementation libs['kcp-core'] + implementation libs['kcp-core'] } \ No newline at end of file diff --git a/gui/src/main/java/com/github/kuangcp/tank/constant/SettingCommand.java b/gui/src/main/java/com/github/kuangcp/tank/constant/SettingCommand.java new file mode 100644 index 00000000..2d69b158 --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/constant/SettingCommand.java @@ -0,0 +1,19 @@ +package com.github.kuangcp.tank.constant; + +/** + * @author https://github.com/kuangcp on 2021-09-06 01:01 + */ +public interface SettingCommand { + + String SHOT_SPEED_INCREMENT = "ssi"; + String SHOT_SPEED_DECREMENT = "ssd"; + + String TANK_SPEED_INCREMENT = "tsi"; + String TANK_SPEED_DECREMENT = "tsd"; + + String TANK_HP_INCREMENT = "thi"; + String TANK_HP_DECREMENT = "thd"; + + String DEMONS_COUNT_INCREMENT = "eci"; + String DEMONS_COUNT_DECREMENT = "ecd"; +} diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/Audio.java b/gui/src/main/java/com/github/kuangcp/tank/util/Audio.java new file mode 100644 index 00000000..8845d8c8 --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/util/Audio.java @@ -0,0 +1,80 @@ +package com.github.kuangcp.tank.util; + +import javax.sound.sampled.AudioFormat; +import javax.sound.sampled.AudioInputStream; +import javax.sound.sampled.AudioSystem; +import javax.sound.sampled.DataLine; +import javax.sound.sampled.SourceDataLine; +import java.io.File; +import java.io.IOException; + +/** + * 类初始化时 把路径传入 播放声音的类 + * 只能播放无损(无压缩的音乐文件)即WAV不能播放MP3 + */ +public class Audio extends Thread { + + private String filename; + private boolean live = true; + + public boolean isLive() { + return live; + } + + public void setLive(boolean live) { + this.live = live; + } + + //构造器 + public Audio(String wavfile) { + filename = wavfile; + + } + + public void run() { + + File soundFile = new File(filename); + + AudioInputStream audioInputStream = null; + try { + audioInputStream = AudioSystem.getAudioInputStream(soundFile); + } catch (Exception e1) { + e1.printStackTrace(); + return; + } + + AudioFormat format = audioInputStream.getFormat(); + SourceDataLine auline = null; + DataLine.Info info = new DataLine.Info(SourceDataLine.class, format); + + try { + auline = (SourceDataLine) AudioSystem.getLine(info); + auline.open(format); + } catch (Exception e) { + e.printStackTrace(); + return; + } + + auline.start(); + int nBytesRead = 0; + //这是缓冲 + byte[] abData = new byte[512]; + + try { + while (nBytesRead != -1) { + nBytesRead = audioInputStream.read(abData, 0, abData.length); + if (nBytesRead >= 0) + auline.write(abData, 0, nBytesRead); + } + } catch (IOException e) { + e.printStackTrace(); + return; + } finally { + auline.drain(); + auline.close(); + } + + } + + +} \ No newline at end of file diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/Saved.java b/gui/src/main/java/com/github/kuangcp/tank/util/Saved.java new file mode 100644 index 00000000..7d17783b --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/util/Saved.java @@ -0,0 +1,478 @@ + +package com.github.kuangcp.tank.util; + + +import com.github.kuangcp.tank.v1.Brick; +import com.github.kuangcp.tank.v1.Demons; +import com.github.kuangcp.tank.v1.Hero; +import com.github.kuangcp.tank.v1.Iron; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Vector; + +/** + * 用文件系统实现保存数据的功能,真繁琐 + * 这里使用了上课时老师教的那种方法,三重流,先字节流然后转换流然后字符流 + * 韩顺平用的是直接字符流 FileReader 和 BufferedReader 就完成了功能 + * 等学会数据库操作,再重新实现这个功能 + * 谨记!! + *

+ * 用数据库实现了续上局及其存盘退出,比文件的操作简单多了。。。 + */ +public class Saved { + private Vector ets; + private Hero hero; + private Vector irons; + private Vector bricks; + int[][] ETS; + int[] myself; + + public Saved(Vector ets, Hero hero, Vector bricks, Vector irons, int[][] ETS, int[] myself) { + this.ets = ets; + this.hero = hero; + this.bricks = bricks; + this.irons = irons; + this.ETS = ETS; + this.myself = myself; + } + + //保存一切的函数 + public void savedAll() { + + BufferedWriter bw = null; + OutputStream out = null; + OutputStreamWriter os = null; + + Demons s; + Brick b; + Iron r; + + try { + out = new FileOutputStream("./src/RE/File.txt"); +// 同等级包下文件,用于存取,导出的时候才可以用上! + //尚未解决!!! +// URL j = (Saved.class.getResource("/RE/File.txt")); +// out = new FileOutputStream(j.getPath()); + os = new OutputStreamWriter(out); + bw = new BufferedWriter(os); + //<>是敌人坦克 + + bw.write("< \r\n"); +// bw.write("写进去了\r\n"); + for (int i = 0; i < ets.size(); i++) { + if ((s = ets.get(i)) != null && s.getisLive()) { + + String qi = "", qx = "", qy = "";//i-2,x-3,y-3 + + if (i < 10) qi = i + " "; + else qi += i; + if (s.getX() < 100) qx = s.getX() + " "; + else qx += s.getX(); + if (s.getY() < 100) qy = s.getY() + " "; + else qy += s.getY(); + + bw.write(qi + " " + qx + " " + qy + "\r\n"); + } + } + bw.write("> \r\n"); + //##是自己坦克 + bw.write("# \r\n"); +// bw.write("# \r\n"); + String Hx = "", Hy = "", Hl = "", Hp = "";//x-3,y-3,l-2,p-3 + + if (hero.getX() < 100) Hx = hero.getX() + " "; + else Hx += hero.getX(); + if (hero.getY() < 100) Hy = hero.getY() + " "; + else Hy += hero.getY(); + if (hero.getLife() < 10) Hl = hero.getLife() + " "; + else Hl += hero.getLife(); + if (hero.getPrize() < 10) Hp = hero.getPrize() + " "; + else if (hero.getPrize() < 100) Hp = hero.getPrize() + " "; + else Hp += hero.getPrize(); + + bw.write(Hx + " " + Hy + " " + Hl + " " + Hp + "\r\n"); + bw.write("# \r\n"); +// //..是砖块 +// bw.write("+\r\n"); +// for(int i=0;i ")) { + System.out.println("退出了敌人信息读取"); + break; + } +// L=br.readLine(); + String Di, Dx, Dy; + + Di = L.substring(0, 2);//(0,2] + Dx = L.substring(3, 6); + Dy = L.substring(7, 10); //是针对每一行的 只要有一行的长度小于这个10了就会抛越界异常 + System.out.println(Di + "X:" + Dx + "Y:" + Dy); + + ETS[StoInt(Di)][0] = StoInt(Dx); + ETS[StoInt(Di)][1] = StoInt(Dy); +// m++; +// System.out.println("X:"+StoInt(Dx)+"Y:"+StoInt(Dy)); +// Demons et = new Demons(StoInt(Dx),StoInt(Dy),2 ,1); +// et.SetInfo(hero, ets, bricks, irons); +// et.setLive(true); +// ets.add(et); +// System.out.println("坦克创建成功"); +// Thread t = new Thread(et); +// t.start(); +// System.out.println(t.isAlive()); + + //这里创建是没有用的,这里的函数栈内存退出后就清除了 + //虽然是传进来了一个集合的引用,但是那个引用是没有内存空间 + //所以集合只是一个指针,没有内存空间,要想创建并跑起来,只能在MyPanel3里创建并且开启线程 + //同样的道理,我的坦克也是如此,也只能用数组来做 + } + } +//************* 读取我的坦克信息 + if (L.equals("# ")) { + System.out.println("进入了我的数据读取"); + while ((L = br.readLine()) != null) { + if (L.equals("# ")) { + System.out.println("退出了我的读取"); + break; + } +// L=br.readLine(); + String hx, hy, hl, hp; + hx = L.substring(0, 3); + hy = L.substring(4, 7); + hl = L.substring(8, 11); + hp = L.substring(11, 14); + System.out.println(hx + hy + hl + hp); +// hero = new Hero(StoInt(hx), StoInt(hy), 3, ets, bricks, irons); +// hero.setLife(StoInt(hl)); +// hero.setPrize(StoInt(hp)); + myself[0] = StoInt(hx); + myself[1] = StoInt(hy); + myself[2] = StoInt(hl); + myself[3] = StoInt(hp); + } + } + //读取砖块 + } + } catch (Exception e) { + e.printStackTrace(); + System.out.println("读取数据有异常"); + } finally { + try { + //后打开,先关闭 + input.close(); + ids.close(); + br.close(); + System.out.println("全部关闭了输入流"); + + } catch (Exception e2) { + e2.printStackTrace(); + System.out.println("有异常"); + } + } + } + + /** + * 将String转换成int + * 只读取字符串从右边第一个数字开始到左边第一个空格结束读取 + * 例如:" 123 4 5 "---> 5 + */ + public int StoInt(String s) { + int I = 0; + int bit; + bit = 1; + for (int i = 0; i < s.length(); i++) {//获取的length是实际长度要减一 + if (s.charAt(s.length() - i - 1) != ' ') { + I += (s.charAt(s.length() - i - 1) - 48) * bit; + bit *= 10; + } + + } + return I; + } + + // public static void main(String []e){ + public void readDataBase() { + + + PreparedStatement ps = null; + Connection cn = null; + ResultSet rs = null; + + try { + //初始化我们的对象 + //1 加载驱动 + Class.forName("com.mysql.jdbc.Driver"); + //2 得到连接 + cn = DriverManager.getConnection("jdbc:mysql://localhost:3306/Tank?user=myth&password=ad&userUnicode=true&characterEncoding=UTF8"); + //创建一个preparedStatement对象用于发送 + + +// ps = cn.prepareStatement("create table Demons(x int,y int)"); +// ps.execute(); + + //查询表 函数主要功能的实现 + ps = cn.prepareStatement("select * from demons"); + rs = ps.executeQuery(); + System.out.println("X Y"); + int nums = 0; + while (rs.next()) { + ETS[nums][0] = rs.getInt(1); + ETS[nums][1] = rs.getInt(2); + nums++; + System.out.println(rs.getInt(1) + " " + rs.getInt(2)); + } + rs.close(); + ps.close(); + //关闭上面资源,连接自己坦克的表 读入 + ps = cn.prepareStatement("select * from hero"); + rs = ps.executeQuery(); + rs.next(); + for (int i = 0; i < 4; i++) { + myself[i] = rs.getInt(i + 1); + } + +// for (int i=1;i<5;i++){ +// ps=cn.prepareStatement("insert into demons values(?,?)"); +// ps.setInt(1, i*30+30); +// ps.setInt(2, i*10+50); +// int j = ps.executeUpdate(); +// if(j==1){ +// System.out.println("成功"); +// }else { +// System.out.println("失败"); +// } +// } + } catch (Exception e1) { + e1.printStackTrace(); + } finally { + //关闭资源 + try { + if (rs != null) + rs.close(); + if (ps != null) + ps.close(); + if (cn != null) + cn.close(); + } catch (SQLException e1) { + // TODO Auto-generated catch block + e1.printStackTrace(); + } + + } + + } + + public void saveDataBase() { + PreparedStatement ps = null; + Connection cn = null; + ResultSet rs = null; + + try { + //初始化我们的对象 + //1 加载驱动 + Class.forName("com.mysql.jdbc.Driver"); + //2 得到连接 + cn = DriverManager.getConnection("jdbc:mysql://localhost:3306/Tank?user=myth&password=ad&userUnicode=true&characterEncoding=UTF8"); + //创建一个preparedStatement对象用于发送 + Demons s = null; + //在写入数据之前就要把表中数据全部删除,不然数据就溢出了 + ps = cn.prepareStatement("delete from demons where x>0"); + int deletes = ps.executeUpdate(); + if (deletes > 1) System.out.println("清除表Demons成功"); + else System.out.println("清除表Demons失败"); + if (ps != null) ps.close(); + ps = cn.prepareStatement("delete from hero where x>0"); + int deletess = ps.executeUpdate(); + if (deletess == 1) System.out.println("清除表Hero成功"); + else System.out.println("清除表Hero失败"); + if (ps != null) ps.close(); + + for (int i = 0; i < ets.size(); i++) { + if ((s = ets.get(i)) != null && s.getisLive()) { + ps = cn.prepareStatement("insert into demons values(?,?)"); + ps.setInt(1, s.getX()); + ps.setInt(2, s.getY()); + int cou = ps.executeUpdate(); + if (cou == 1) System.out.println("demons 保存成功"); + else System.out.println("demons 保存失败"); + } + } + + if (ps != null) ps.close(); + ps = cn.prepareStatement("insert into hero values(?,?,?,?)"); + ps.setInt(1, hero.getX()); + ps.setInt(2, hero.getY()); + ps.setInt(3, hero.getLife()); + ps.setInt(4, hero.getPrize()); + int hh = ps.executeUpdate(); + if (hh == 1) System.out.println("Hero 保存成功"); + else System.out.println("Hero 保存失败"); + // + } catch (Exception e) { + e.printStackTrace(); + } finally { + //关闭资源 + try { + if (rs != null) + rs.close(); + if (ps != null) + ps.close(); + if (cn != null) + cn.close(); + } catch (SQLException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + } + + } +} + + +/** + * (和src同级)相对路径下的 字节转字符文件 读取操作从同等级的包下读取文件 + * 从同等级的包下读取文件 + */ +// public void savedAll(){ + /*public static void main(String []d){ + InputStream input = null; + BufferedReader br = null; + InputStreamReader ids =null; + + BufferedWriter bw = null; + OutputStream out = null; + OutputStreamWriter os = null; + + try{ + input = new FileInputStream("File/Cache.txt"); + ids = new InputStreamReader(input); + br = new BufferedReader(ids); + + + out = new FileOutputStream("File/Cache.txt"); +// out = new FileOutputStream("E:/Game.txt"); + os = new OutputStreamWriter(out); + bw = new BufferedWriter(os); + + String s = ""; + while((s=br.readLine())!=null){ + System.out.print(s+"\r\n"); +// bw.write(" 输出流0000000 "); + } +// bw.write(" 输出流738927589 "); +// bw.flush();//一定要清除缓存 + }catch(Exception e){ + + }finally { + try { + input.close(); + ids.close(); + br.close(); + + out.close(); + os.close(); + bw.close(); + System.out.println("全部关闭"); + + } catch (Exception e2) { + e2.printStackTrace(); + System.out.println("有异常"); + // TODO: handle exception + } + } + + System.out.println("ko"); + }*/ + +/**从同等级的包下读取文件*/ // ./src/RE/GameBegin.wav +//使用了三种流 + /*public static void main(String []d){ + InputStream input = null; + BufferedReader br = null; + InputStreamReader ids =null; + try{ + input = FileReader.class.getResourceAsStream("/File/Cache.txt"); + ids = new InputStreamReader(input); + br = new BufferedReader(ids); + + String s = ""; + while((s=br.readLine())!=null){ + System.out.print(s+"\r\n"); + } + }catch(Exception e){ + + }finally { + try { + input.close(); + ids.close(); + br.close(); + } catch (Exception e2) { + // TODO: handle exception + } + } + }*/ + diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/Setting.java b/gui/src/main/java/com/github/kuangcp/tank/util/Setting.java new file mode 100644 index 00000000..7849a988 --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/util/Setting.java @@ -0,0 +1,108 @@ +package com.github.kuangcp.tank.util; + + +import com.github.kuangcp.tank.constant.SettingCommand; +import com.github.kuangcp.tank.v1.Hero; +import com.github.kuangcp.tank.v2.Shot; +import com.github.kuangcp.tank.v3.MyPanel3; + +import javax.imageio.ImageIO; +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.image.BufferedImage; +import java.io.IOException; + +public class Setting extends JFrame implements ActionListener { + + JPanel up, ss; + JLabel U; + JButton[] Plus = new JButton[4]; + JButton[] Cut = new JButton[4]; + JLabel[] Kind = new JLabel[4]; + Hero hero; + + public Setting(Hero hero) { + this.hero = hero; + try { + final BufferedImage iconImg = ImageIO.read(getClass().getResourceAsStream("/images/Me4.jpg")); + U = new JLabel(new ImageIcon(iconImg)); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + for (int i = 0; i < 4; i++) { + Plus[i] = new JButton("+"); + Cut[i] = new JButton("-"); + } + Kind[0] = new JLabel("子弹速度");//+Shot.getSpeed() + Kind[1] = new JLabel("̹坦克速度");//+hero.getSpeed() + Kind[2] = new JLabel("坦克生命");//+this.hero.getLife() + Kind[3] = new JLabel("敌人数量");//+MyPanel3.getEnSize() + + up = new JPanel(); + ss = new JPanel(); + + up.add(U); + + ss.setLayout(new GridLayout(4, 3, 0, 0)); + for (int i = 0; i < 4; i++) { + ss.add(Kind[i]); + ss.add(Plus[i]); + ss.add(Cut[i]); + Plus[i].addActionListener(this); + Cut[i].addActionListener(this); + } + Plus[0].setActionCommand(SettingCommand.SHOT_SPEED_INCREMENT); + Plus[1].setActionCommand(SettingCommand.TANK_SPEED_INCREMENT); + Plus[2].setActionCommand(SettingCommand.TANK_HP_INCREMENT); + Plus[3].setActionCommand(SettingCommand.DEMONS_COUNT_INCREMENT); + + Cut[0].setActionCommand(SettingCommand.SHOT_SPEED_DECREMENT); + Cut[1].setActionCommand(SettingCommand.TANK_SPEED_DECREMENT); + Cut[2].setActionCommand(SettingCommand.TANK_HP_DECREMENT); + Cut[3].setActionCommand(SettingCommand.DEMONS_COUNT_DECREMENT); + + this.add(up, BorderLayout.NORTH); + this.add(ss, BorderLayout.CENTER); + this.setLocation(800, 300); + this.setSize(260, 280); + this.setTitle("Setting"); +// this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + this.setVisible(true); + } + + @Override + public void actionPerformed(ActionEvent event) { + if (event.getActionCommand().equals(SettingCommand.SHOT_SPEED_INCREMENT)) { + Shot.setSpeed(Shot.getSpeed() + 1); + } + if (event.getActionCommand().equals(SettingCommand.SHOT_SPEED_DECREMENT)) { + Shot.setSpeed(Shot.getSpeed() - 1); + } + + if (event.getActionCommand().equals(SettingCommand.TANK_SPEED_INCREMENT)) { + hero.addSpeed(1); + } + if (event.getActionCommand().equals(SettingCommand.TANK_SPEED_DECREMENT)) { + hero.addSpeed(-1); + } + + if (event.getActionCommand().equals(SettingCommand.TANK_HP_INCREMENT)) { + this.hero.setLife(this.hero.getLife() + 1); + } + if (event.getActionCommand().equals(SettingCommand.TANK_HP_DECREMENT)) { + this.hero.setLife(this.hero.getLife() - 1); + } + + if (event.getActionCommand().equals(SettingCommand.DEMONS_COUNT_INCREMENT)) { + MyPanel3.setEnSize(MyPanel3.getEnSize() + 1); + } + if (event.getActionCommand().equals(SettingCommand.DEMONS_COUNT_DECREMENT)) { + MyPanel3.setEnSize(MyPanel3.getEnSize() - 1); + } + + } +} \ No newline at end of file diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/TankTool.java b/gui/src/main/java/com/github/kuangcp/tank/util/TankTool.java new file mode 100644 index 00000000..9d061848 --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/util/TankTool.java @@ -0,0 +1,293 @@ +package com.github.kuangcp.tank.util; + +import com.github.kuangcp.tank.v1.Brick; +import com.github.kuangcp.tank.v1.Hero; +import com.github.kuangcp.tank.v1.Hinder; +import com.github.kuangcp.tank.v1.Tank; +import com.github.kuangcp.tank.v2.Shot; +import com.github.kuangcp.tank.v3.Bomb; + +import java.util.Vector; + +public class TankTool { + + /** + * 工具类-检测爆炸的函数 + */ + public static void Bong(Tank t, Shot s, Vector bombs) { + /* + * 形参: 坦克 子弹 炸弹集合 + */ + +// System.out.println("t.getLife()"+t.getLife()); + switch (t.getDirect()) {//上下左右 + case 0: + case 1: + if (t.getX() - 10 <= s.sx && + t.getX() + 10 >= s.sx && + t.getY() - 15 <= s.sy && + t.getY() + 15 >= s.sy) { + s.isLive = false; + t.setLife(t.getLife() - 1);//生命值减一 +// System.out.println("减一"); + + if (t.getLife() == 0) t.setLive(false); + //创建一个炸弹,放入集合 + Bomb b = new Bomb(t.getX() - 10, t.getY() - 15);//敌方的坐标 + bombs.add(b); + if (t instanceof Hero) { + t.setX(480); + t.setY(500); + t.setDirect(0); +// try { +// Thread.sleep(300); +// } catch (InterruptedException e) { +// // TODO Auto-generated catch block +// e.printStackTrace(); +// } + } + } + break; + case 2: + case 3: + if (t.getX() - 15 <= s.sx && + t.getX() + 15 >= s.sx && + t.getY() - 10 <= s.sy && + t.getY() + 10 >= s.sy) { + s.isLive = false; + t.setLife(t.getLife() - 1); +// System.out.println("减一"); + + if (t.getLife() == 0) t.setLive(false); + //创建一个炸弹,放入集合 + + Bomb b = new Bomb(t.getX() - 15, t.getY() - 10);//敌方的坐标 + bombs.add(b); + if (t instanceof Hero) { + t.setX(480); + t.setY(500); + t.setDirect(0); +// try { +// Thread.sleep(300); +// } catch (InterruptedException e) { +// // TODO Auto-generated catch block +// e.printStackTrace(); +// } + } + } + break; + + } +// System.out.println("hero life :"+t.getLife()); + + } + + /** + * 工具类-坦克之间的碰撞检测函数 + */ + public static boolean Rush(Tank me, Tank you) { + + boolean flag = true; + switch (you.getDirect()) {//对方 上下 + case 0: + case 1: + switch (me.getDirect()) {//己方 上下左右 分开 + case 0: { + if ((you.getX() - 10 <= me.getX() - 10 && + you.getX() + 10 >= me.getX() - 10 && + you.getY() - 15 <= me.getY() - 15 && + you.getY() + 15 >= me.getY() - 15) + || + (you.getX() - 10 <= me.getX() + 10 && + you.getX() + 10 >= me.getX() + 10 && + you.getY() - 15 <= me.getY() - 15 && + you.getY() + 15 >= me.getY() - 15))//判断最前方两个点是否在对方坦克区域内 + { + flag = false; + } + break; + } + case 1: { + if ((you.getX() - 10 <= me.getX() - 10 && + you.getX() + 10 >= me.getX() - 10 && + you.getY() - 15 <= me.getY() + 15 && + you.getY() + 15 >= me.getY() + 15) + || + (you.getX() - 10 <= me.getX() + 10 && + you.getX() + 10 >= me.getX() + 10 && + you.getY() - 15 <= me.getY() + 15 && + you.getY() + 15 >= me.getY() + 15))//判断最前方两个点是否在对方坦克区域内 + { + flag = false; + } + break; + } + case 2: { + if ((you.getX() - 10 <= me.getX() - 15 && + you.getX() + 10 >= me.getX() - 15 && + you.getY() - 15 <= me.getY() - 10 && + you.getY() + 15 >= me.getY() - 10) + || + (you.getX() - 10 <= me.getX() - 15 && + you.getX() + 10 >= me.getX() - 15 && + you.getY() - 15 <= me.getY() + 10 && + you.getY() + 15 >= me.getY() + 10))//判断最前方两个点是否在对方坦克区域内 + { + flag = false; + } + break; + } + case 3: { + if ((you.getX() - 10 <= me.getX() + 15 && + you.getX() + 10 >= me.getX() + 15 && + you.getY() - 15 <= me.getY() - 10 && + you.getY() + 15 >= me.getY() - 10) + || + (you.getX() - 10 <= me.getX() + 15 && + you.getX() + 10 >= me.getX() + 15 && + you.getY() - 15 <= me.getY() + 10 && + you.getY() + 15 >= me.getY() + 10))//判断最前方两个点是否在对方坦克区域内 + { + flag = false; + } + break; + } + default: + break; + } + + case 2: + case 3://对方左右 + switch (me.getDirect()) {//己方 上下左右 分开 + case 0: { + if ((you.getX() - 15 <= me.getX() - 10 && + you.getX() + 15 >= me.getX() - 10 && + you.getY() - 10 <= me.getY() - 15 && + you.getY() + 10 >= me.getY() - 15) + || + (you.getX() - 15 <= me.getX() + 10 && + you.getX() + 15 >= me.getX() + 10 && + you.getY() - 10 <= me.getY() - 15 && + you.getY() + 10 >= me.getY() - 15))//判断最前方两个点是否在对方坦克区域内 + { + flag = false; + } + break; + } + case 1: { + if ((you.getX() - 15 <= me.getX() - 10 && + you.getX() + 15 >= me.getX() - 10 && + you.getY() - 10 <= me.getY() + 15 && + you.getY() + 10 >= me.getY() + 15) + || + (you.getX() - 15 <= me.getX() + 10 && + you.getX() + 15 >= me.getX() + 10 && + you.getY() - 10 <= me.getY() + 15 && + you.getY() + 10 >= me.getY() + 15))//判断最前方两个点是否在对方坦克区域内 + { + flag = false; + } + break; + } + case 2: { + if ((you.getX() - 15 <= me.getX() - 15 && + you.getX() + 15 >= me.getX() - 15 && + you.getY() - 10 <= me.getY() - 10 && + you.getY() + 10 >= me.getY() - 10) + || + (you.getX() - 15 <= me.getX() - 15 && + you.getX() + 15 >= me.getX() - 15 && + you.getY() - 10 <= me.getY() + 10 && + you.getY() + 10 >= me.getY() + 10))//判断最前方两个点是否在对方坦克区域内 + { + flag = false; + } + break; + } + case 3: { + if ((you.getX() - 10 <= me.getX() + 15 && + you.getX() + 10 >= me.getX() + 15 && + you.getY() - 15 <= me.getY() - 10 && + you.getY() + 15 >= me.getY() - 10) + || + (you.getX() - 10 <= me.getX() + 15 && + you.getX() + 10 >= me.getX() + 15 && + you.getY() - 15 <= me.getY() + 10 && + you.getY() + 15 >= me.getY() + 10))//判断最前方两个点是否在对方坦克区域内 + { + flag = false; + } + break; + } + default: + break; + } + } + //switch 语句之外: + return flag; + } + + /** + * 工具类 检测是否有障碍物是否可以通行 + */ + public static boolean hasHint(Tank t, Hinder h) { + //坦克 障碍物 + boolean flag = true; + flag = true; + int hx = 20, hy = 10; + switch (t.getDirect()) { + case 0://上 + if (t.getX() - 10 >= h.getHx() && t.getX() - 10 <= h.getHx() + hx + && t.getY() - 15 >= h.getHy() && t.getY() - 15 <= h.getHy() + hy + || t.getX() >= h.getHx() && t.getX() <= h.getHx() + hx + && t.getY() - 15 >= h.getHy() && t.getY() - 15 <= h.getHy() + hy + || t.getX() + 10 >= h.getHx() && t.getX() + 10 <= h.getHx() + hx + && t.getY() - 15 >= h.getHy() && t.getY() - 15 <= h.getHy() + hy) + flag = false; + return flag; + case 1://下 + if (t.getX() - 10 >= h.getHx() && t.getX() - 10 <= h.getHx() + hx + && t.getY() + 15 >= h.getHy() && t.getY() + 15 <= h.getHy() + hy + || t.getX() >= h.getHx() && t.getX() <= h.getHx() + hx + && t.getY() + 15 >= h.getHy() && t.getY() + 15 <= h.getHy() + hy + || t.getX() + 10 >= h.getHx() && t.getX() + 10 <= h.getHx() + hx + && t.getY() + 15 >= h.getHy() && t.getY() + 15 <= h.getHy() + hy) + flag = false; + return flag; + case 2: + if (t.getX() - 15 >= h.getHx() && t.getX() - 15 <= h.getHx() + hx + && t.getY() - 10 >= h.getHy() && t.getY() - 10 <= h.getHy() + hy + || t.getX() - 15 >= h.getHx() && t.getX() - 15 <= h.getHx() + hx + && t.getY() >= h.getHy() && t.getY() <= h.getHy() + hy + || t.getX() - 15 >= h.getHx() && t.getX() - 15 <= h.getHx() + hx + && t.getY() + 10 >= h.getHy() && t.getY() + 10 <= h.getHy() + hy) + flag = false; + return flag; + case 3: + if (t.getX() + 15 >= h.getHx() - 2 && t.getX() + 15 <= h.getHx() + hx + && t.getY() + 10 >= h.getHy() && t.getY() + 10 <= h.getHy() + hy + || t.getX() + 15 >= h.getHx() - 2 && t.getX() + 15 <= h.getHx() + hx + && t.getY() >= h.getHy() && t.getY() <= h.getHy() + hy + || t.getX() + 15 >= h.getHx() - 2 && t.getX() + 15 <= h.getHx() + hx + && t.getY() - 10 >= h.getHy() && t.getY() - 10 <= h.getHy() + hy) + flag = false; + return flag; + } + return flag; + + } + + /** + * 子弹和障碍物 碰撞监测 + */ + public static void judgeHint(Shot s, Hinder h) { + if (s.sx >= h.getHx() - 1 && s.sx <= h.getHx() + 20 + && s.sy >= h.getHy() - 1 && s.sy <= h.getHy() + 10) { + s.isLive = false; + + if (h instanceof Brick) { + h.setLive(false); + } + } + } +} diff --git a/gui/src/main/java/com/github/kuangcp/tank/v1/Brick.java b/gui/src/main/java/com/github/kuangcp/tank/v1/Brick.java new file mode 100644 index 00000000..b5cde85f --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/v1/Brick.java @@ -0,0 +1,41 @@ +package com.github.kuangcp.tank.v1; + +import java.awt.*; + +/** + * 砖块 + */ +public class Brick extends Hinder { + + Graphics g; + + public Brick(int hx, int hy) { + super(hx, hy); + } + + /** + * 构造器全是沿用父类的,父类要是没有无参构造器,子类有了就属于非法 + * 并且 父类有了含参构造器,子类必须也要有,显式的super一下 + */ + public Brick() { + } + //被舍弃 不使用 +/**画出砖块 20*10大小 为了便于使用 就直接(x,y)到(x,y)画出矩形 虽然实现了但是不利于控制死亡*/ + /*public void d(Graphics g,Vector bricks,int startX,int startY,int endX,int endY){ + +// g.setColor(Color.LIGHT_GRAY);//钢板的颜色 + g.setColor(Color.orange);//砖块颜色 + + for( hx=startX;hx + * 还是有一个小bug 子弹集合是敌方坦克的成员属性 + * 坦克内存一回收 子弹也就没了,但是不符合实际 + * 坦克死了 子弹也应该继续飞 + */ +public class Demons extends Tank implements Runnable { + + public boolean alive; + public Vector ds = new Vector<>();//子弹集合 + public Vector ets; + public Vector bricks; + public Vector irons; + public Shot s = null; + Hero hero; + boolean with = true; //同类之间有无重叠(true是可以往前走) + + public boolean isWith() { + return with; + } + + + public void setWith(boolean with) { + this.with = with; + } + + + public boolean isBri() { + return bri; + } + + + public void setBri(boolean bri) { + this.bri = bri; + } + + boolean bri = true; + boolean to = true; + + + //继承了属性,即使直接使用父类的构造器,构造器也一定要显式声明 + public Demons(int x, int y, int speed, int direct) { + super(x, y, speed); + type = 1; + this.direct = direct; + this.speed = speed; + this.alive = true; + } + + public void SetInfo(Hero hero, Vector ets, Vector bricks, Vector irons) { + this.hero = hero; + this.ets = ets; + this.bricks = bricks; + this.irons = irons; + } + + /** + * 视频上的思想是 在panel上写一个工具函数 形参是 子弹和坦克 + * 把函数放在 Run函数内跑 + * 遮掩更显得逻辑性强一些,代码复用率高一点 + * @param hero + */ + + /** + * 重新封装 + */ + + /** + * 发射子弹 函数 + */ + public void shotEnemy() { + //判断坦克方向来 初始化子弹的起始发射位置 + + switch (this.getDirect()) { + case 0: {//0123 代表 上下左右 + s = new Shot(this.getX() - 1, this.getY() - 15, 0); + ds.add(s); + break; + } + case 1: { + s = new Shot(this.getX() - 2, this.getY() + 15, 1); + ds.add(s); + break; + } + case 2: { + s = new Shot(this.getX() - 15 - 2, this.getY(), 2); + ds.add(s); + break; + } + case 3: { + s = new Shot(this.getX() + 15 - 2, this.getY() - 1, 3); + ds.add(s); + break; + } + } + //启动子弹线程 + Thread t = new Thread(s); + t.start(); + } + +// public void run (){ +//// synchronized (this) { +// while(true){ +// this.direct = ((int)(Math.random()*100))%4; +//// System.out.println(this.direct); +// +// switch(this.getDirect()){ +// case 0:{ +// for(int i=0;i<8;i++) if(this.toUp());else break; +//// System.out.println(this.direct); +// if(this.y%25==0)this.shotEnemy(); +// break; +// } +// case 1:{ +// for(int i=0;i<8;i++) if(this.toDown());else break; +// if(this.y%25==0)this.shotEnemy(); +// break; +// } +// case 2:{ +// for(int i=0;i<8;i++) if(this.toLeft());else break; +// if(this.y%25==0)this.shotEnemy(); +// break; +// } +// case 3:{ +// for(int i=0;i<8;i++) if(this.toRight());else break; +// if(this.y%25==0)this.shotEnemy(); +// break; +// +// } +// default: +// break; +// } +// +//// this.direct = ((int)(Math.random()*100))%4; +// +// if (!this.getisLive()){ +// //让坦克退出while即退出线程 +// hero.setPrize(hero.getPrize()+1); +// break; +// } +// } +//// } +// +// } + + + //还是没有用,因为坦克太多,会卡住。。。 + public boolean TouchOther() { + boolean flag = false;//没有碰到 + + switch (this.direct) { + case 0://上 + for (int i = 0; i < ets.size(); i++) { + Demons et = ets.get(i); + if (et != this) { + if (et.direct == 0 || et.direct == 1) {//对方是上下 + //自己的上左 + if ((et.x - 10 <= this.x - 10 && et.x + 10 >= this.x - 10 && + et.y - 15 <= this.y - 15 && et.y + 15 >= this.y - 15)) { + flag = true; + } + //上右 + if (et.x - 10 <= this.x + 10 && et.x + 10 >= this.x + 10 && + et.y - 15 <= this.y - 15 && et.y + 15 >= this.y - 15) {//判断最前方两个点是否在对方坦克区域内 + flag = true; + } + } + if (et.direct == 2 || et.direct == 3) {//对方是左右 + //自己的上左 + if ((et.x - 15 <= this.x - 10 && et.x + 15 >= this.x - 10 && + et.y - 10 <= this.y - 15 && et.y + 10 >= this.y - 15)) { + flag = true; + } + //上右 + if (et.x - 15 <= this.x + 10 && et.x + 15 >= this.x + 10 && + et.y - 10 <= this.y - 15 && et.y + 10 >= this.y - 15) {//判断最前方两个点是否在对方坦克区域内 + flag = true; + } + } + } + } + break; + case 1: + for (int i = 0; i < ets.size(); i++) { + Demons et = ets.get(i); + if (et != this) { + //对方是上下 + if (et.direct == 0 || et.direct == 1) { + //自己的下左 + if ((et.x - 10 <= this.x - 10 && et.x + 10 >= this.x - 10 && + et.y - 15 <= this.y + 15 && et.y + 15 >= this.y + 15)) { + flag = true; + } + //下右 + if (et.x - 10 <= this.x + 10 && et.x + 10 >= this.x + 10 && + et.y - 15 <= this.y + 15 && et.y + 15 >= this.y + 15) {//判断最前方两个点是否在对方坦克区域内 + flag = true; + } + } + //对方是左右 + if (et.direct == 2 || et.direct == 3) { + //自己的下左 + if ((et.x - 15 <= this.x - 10 && et.x + 15 >= this.x - 10 && + et.y - 10 <= this.y + 15 && et.y + 10 >= this.y + 15)) { + flag = true; + } + //下右 + if (et.x - 15 <= this.x + 10 && et.x + 15 >= this.x + 10 && + et.y - 10 <= this.y + 15 && et.y + 10 >= this.y + 15) {//判断最前方两个点是否在对方坦克区域内 + flag = true; + } + } + } + } + break; + case 2: + for (int i = 0; i < ets.size(); i++) { + Demons et = ets.get(i); + if (et != this) { + if (et.direct == 0 || et.direct == 1) { + //对方是上下 + //自己的左上 + if ((et.x - 10 <= this.x - 15 && et.x + 10 >= this.x - 15 && + et.y - 15 <= this.y - 10 && et.y + 15 >= this.y - 10)) { + flag = true; + } + //右下 + if (et.x - 10 <= this.x - 15 && et.x + 10 >= this.x - 15 && + et.y - 15 <= this.y + 10 && et.y + 15 >= this.y + 10) {//判断最前方两个点是否在对方坦克区域内 + flag = true; + } + } + if (et.direct == 2 || et.direct == 3) { + //对方是左右 + //自己的左上 + if ((et.x - 15 <= this.x - 15 && et.x + 15 >= this.x - 15 && + et.y - 10 <= this.y - 10 && et.y + 10 >= this.y - 10)) { + flag = true; + } + //右下 + if (et.x - 15 <= this.x - 15 && et.x + 15 >= this.x - 15 && + et.y - 10 <= this.y + 10 && et.y + 10 >= this.y + 10) {//判断最前方两个点是否在对方坦克区域内 + flag = true; + } + } + } + } + break; + case 3: + for (int i = 0; i < ets.size(); i++) { + Demons et = ets.get(i); + if (et != this) { + if (et.direct == 0 || et.direct == 1) { + //对方是上下 + //自己的左上 + if ((et.x - 10 <= this.x + 15 && et.x + 10 >= this.x + 15 && + et.y - 15 <= this.y - 10 && et.y + 15 >= this.y - 10)) { + flag = true; + } + //右下 + if (et.x - 10 <= this.x + 15 && et.x + 10 >= this.x + 15 && + et.y - 15 <= this.y + 10 && et.y + 15 >= this.y + 10) {//判断最前方两个点是否在对方坦克区域内 + flag = true; + } + } + if (et.direct == 2 || et.direct == 3) { + //对方是左右 + //自己的左上 + if ((et.x - 15 <= this.x + 15 && et.x + 15 >= this.x + 15 && + et.y - 10 <= this.y - 10 && et.y + 10 >= this.y - 10)) { + flag = true; + } + //右下 + if (et.x - 15 <= this.x + 15 && et.x + 15 >= this.x + 15 && + et.y - 10 <= this.y + 10 && et.y + 10 >= this.y + 10) {//判断最前方两个点是否在对方坦克区域内 + flag = true; + } + } + } + } + break; + } + + return flag; + } + + //只是移动一步的函数 + public boolean toUp() { + if (y > 30) { + to = true; + for (int k = 0; k < ets.size(); k++) { + if (!TankTool.Rush(this, ets.get(k))) { + to = (false); + break; + } + } + for (int i = 0; i < bricks.size(); i++) { + if (!TankTool.hasHint(this, bricks.get(i))) + to = false; + } + for (int i = 0; i < irons.size(); i++) { + if (!TankTool.hasHint(this, irons.get(i))) + to = false; + } + if (to && TankTool.Rush(this, hero)) { + y -= speed; + try { + Thread.sleep(100); + } catch (Exception e) { + e.printStackTrace(); + } + } else { + return false; +// this.direct = ((int)(Math.random()*100))%4; + } + + } else return false; + return true; + } + + public boolean toDown() { + if (y < 530) { + to = true; + for (int k = 0; k < ets.size(); k++) { + if (!TankTool.Rush(this, ets.get(k))) { + to = (false); + break; + } + } + for (int i = 0; i < bricks.size(); i++) { + if (!TankTool.hasHint(this, bricks.get(i))) + to = false; + } + for (int i = 0; i < irons.size(); i++) { + if (!TankTool.hasHint(this, irons.get(i))) + to = false; + } + if (to && TankTool.Rush(this, hero)) { + y += speed; + try { + Thread.sleep(100); + } catch (Exception e) { + e.printStackTrace(); + } + } else { + return false; +// this.direct = ((int)(Math.random()*100))%4; + } + + } else return false; + return true; + } + + public boolean toLeft() { + if (x > 30) { + to = true; + for (int k = 0; k < ets.size(); k++) { + if (!TankTool.Rush(this, ets.get(k))) { + to = (false); + break; + } + } + for (int i = 0; i < bricks.size(); i++) { + if (!TankTool.hasHint(this, bricks.get(i))) + to = false; + } + for (int i = 0; i < irons.size(); i++) { + if (!TankTool.hasHint(this, irons.get(i))) + to = false; + } + if (to && TankTool.Rush(this, hero)) { + x -= speed; + try { + Thread.sleep(100); + } catch (Exception e) { + e.printStackTrace(); + } + } else { + return true; +// this.direct = ((int)(Math.random()*100))%4; + } + + } else return false; + return true; + } + + public boolean toRight() { + if (x < 710) { + to = true; + for (int k = 0; k < ets.size(); k++) { + if (!TankTool.Rush(this, ets.get(k))) { + to = (false); + break; + } + } + for (int i = 0; i < bricks.size(); i++) { + if (!TankTool.hasHint(this, bricks.get(i))) + to = false; + } + for (int i = 0; i < irons.size(); i++) { + if (!TankTool.hasHint(this, irons.get(i))) + to = false; + } + if (to && TankTool.Rush(this, hero)) { + x += speed; + try { + Thread.sleep(100); + } catch (Exception e) { + e.printStackTrace(); + } + } else { + return false; +// this.direct = ((int)(Math.random()*100))%4; + } + + } else return false; + return true; + } + + //重写 + int min = 0; + + public void run() { + //子弹发射的(时间)坐标间隔 + + while (true) { + min++; + if (min > 10000) min -= 10000; +// System.out.println("x = "+this.x+" y = "+this.y); +// System.out.println(speed); 终于找出错误,speed没有初始化 + + //让坦克随机产生一个随机方向 + if (this.speed != 0) this.direct = (int) (Math.random() * 4); + + switch (this.direct) {//上下左右 + case 0://说明坦克正在向上移动 + for (int i = 0; i < 30; i++) { + min++; +// if(!bri)this.direct = (int)(Math.random()*4); + if (y > 30) { + if (TankTool.Rush(this, hero) && with) y -= speed; +// if(bri)y-=speed; +// else {y+=speed;this.direct = 1;} +// else continue; + else break;//这样才不会有敌人坦克凑在你附近不动 +// else this.direct = (int)(Math.random()*4); + if (min % 27 == 0) + this.shotEnemy(); + try { + Thread.sleep(50); + } catch (Exception e) { + e.printStackTrace(); + } + } + + } + + break; + case 1: + for (int i = 0; i < 30; i++) { + min++; +// if(!bri)this.direct = (int)(Math.random()*4); + + if (y < 530) { + if (TankTool.Rush(this, hero) && with && bri) y += speed; +// if(bri) y+=speed; +// else {y-=speed;this.direct = 0;} +// else continue; + else break; + if (min % 27 == 0) + this.shotEnemy(); + try { + Thread.sleep(50); + } catch (Exception e) { + e.printStackTrace(); + } + } + + } + + break; + case 2: + for (int i = 0; i < 30; i++) { + min++; +// if(!bri)this.direct = (int)(Math.random()*4); + + if (x > 30) { + if (TankTool.Rush(this, hero) && with && bri) x -= speed; +// if(bri)x-=speed; +// else{x+=speed;this.direct = 3;} +// else continue; + else break; + if (min % 27 == 0) + this.shotEnemy(); + try { + Thread.sleep(50); + } catch (Exception e) { + e.printStackTrace(); + } + } + + } + + break; + case 3: + for (int i = 0; i < 30; i++) { + min++; +// if(!bri)this.direct = (int)(Math.random()*4); + + if (x < 710) { + if (TankTool.Rush(this, hero) && with && bri) x += speed; +// if(bri)x+=speed; +// else{x-=speed;this.direct = 2;} + else break; +// else continue; + if (min % 27 == 0) + this.shotEnemy(); + try { + Thread.sleep(50); + } catch (Exception e) { + e.printStackTrace(); + } + } + + + } + break; + default: + break; + + } + + + //判断坦克是否死亡 + if (!this.getisLive()) { + //让坦克退出while即退出线程 + hero.setPrize(hero.getPrize() + 1); + break; + } + } + } + +} diff --git a/gui/src/main/java/com/github/kuangcp/tank/v1/Hero.java b/gui/src/main/java/com/github/kuangcp/tank/v1/Hero.java new file mode 100644 index 00000000..a13bc212 --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/v1/Hero.java @@ -0,0 +1,170 @@ +package com.github.kuangcp.tank.v1; + + +import com.github.kuangcp.tank.v2.Shot; +import com.github.kuangcp.tank.util.TankTool; + +import java.awt.*; +import java.util.Vector; + +public class Hero extends Tank { + + //子弹集合 + public Vector ss = new Vector<>(); + + public Shot s = null;//子弹 + public Graphics g; //画笔不可少 + public boolean flag = false;//是否击中 + static int round = 0; + public Vector ets;//只是一个指针(引用) + public Vector bricks; + public Vector irons; + public boolean to = true; + static int prize = 0;//击敌个数 + public int SHOTS = 8;//主坦克子弹线程存活的最大数 + int speed = 3; + static int Life = 10; + + + public int getLife() { + return Life; + } + + public void setLife(int life) { + Life = life; + } + + public int getSpeed() { + return speed; + } + + public void setSpeed(int speed) { + this.speed = speed; + } + + public void addSpeed(int delta) { + this.speed += delta; + } + + public int getPrize() { + return prize; + } + + public void setPrize(int prize) { + this.prize = prize; + } + + + public Hero(int x, int y, int speed, Vector ets, Vector bricks, Vector irons) { + super(x, y, speed); +// this.Life = 10; +// 要给g分配内存 初始化对象 +// g = new Graphics(); +// g.setColor(Color.yellow); + this.ets = ets; + this.bricks = bricks; + this.irons = irons; + } + + /** + * 被重新封装 + */ + // 视频的样板开火函数 + public void shotEnemy() { + //判断坦克方向来 初始化子弹的起始发射位置 + + switch (this.getDirect()) { + case 0://0123 代表 上下左右 + s = new Shot(this.getX() - 1, this.getY() - 15, 0); + ss.add(s); + break; + case 1: + s = new Shot(this.getX() - 2, this.getY() + 15, 1); + ss.add(s); + break; + case 2: + s = new Shot(this.getX() - 15 - 2, this.getY(), 2); + ss.add(s); + break; + case 3: + s = new Shot(this.getX() + 15 - 2, this.getY() - 1, 3); + ss.add(s); + break; + } + //启动子弹线程 + Thread t = new Thread(s); + t.start(); + } + + //移动的函数 + public void moveup() { + to = true; + //检测敌方坦克碰撞 + for (Demons et : ets) { + if (!TankTool.Rush(this, et)) + to = false; + } + //检测障碍物 + for (Brick brick : bricks) { + if (!TankTool.hasHint(this, brick)) + to = false; + } + for (Iron iron : irons) { + if (!TankTool.hasHint(this, iron)) + to = false; + } + if (to) + setY(getY() - getSpeed()); + } + + public void movedown() { + to = true; + for (Demons et : ets) + if (!TankTool.Rush(this, et)) + to = false; + for (Brick brick : bricks) { + if (!TankTool.hasHint(this, brick)) + to = false; + } + for (Iron iron : irons) { + if (!TankTool.hasHint(this, iron)) + to = false; + } + if (to) + setY(getY() + getSpeed()); + } + + public void moveleft() { + to = true; + for (Demons et : ets) + if (!TankTool.Rush(this, et)) + to = false; + for (Brick brick : bricks) { + if (!TankTool.hasHint(this, brick)) + to = false; + } + for (Iron iron : irons) { + if (!TankTool.hasHint(this, iron)) + to = false; + } + if (to) + setX(getX() - getSpeed()); + } + + public void moveright() { + to = true; + for (Demons et : ets) + if (!TankTool.Rush(this, et)) + to = false; + for (Brick brick : bricks) { + if (!TankTool.hasHint(this, brick)) + to = false; + } + for (Iron iron : irons) { + if (!TankTool.hasHint(this, iron)) + to = false; + } + if (to) + setX(getX() + getSpeed()); + } +} diff --git a/gui/src/main/java/com/github/kuangcp/tank/v1/Hinder.java b/gui/src/main/java/com/github/kuangcp/tank/v1/Hinder.java new file mode 100644 index 00000000..38a311d3 --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/v1/Hinder.java @@ -0,0 +1,45 @@ +package com.github.kuangcp.tank.v1; + +/** + * 障碍物的最基本类 + */ +public abstract class Hinder { + int hx, hy;//障碍物坐标 绘图坐标 也就是左上角的坐标 + boolean Live;//生存状态 + + public Hinder(int hx, int hy) { + Live = true; + this.hx = hx; + this.hy = hy; + + } + + public Hinder() { + } + + public Boolean getLive() { + return Live; + } + + public void setLive(Boolean live) { + Live = live; + } + + public int getHx() { + return hx; + } + + public void setHx(int hx) { + this.hx = hx; + } + + public int getHy() { + return hy; + } + + public void setHy(int hy) { + this.hy = hy; + } + + +} diff --git a/gui/src/main/java/com/github/kuangcp/tank/v1/Iron.java b/gui/src/main/java/com/github/kuangcp/tank/v1/Iron.java new file mode 100644 index 00000000..11cca711 --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/v1/Iron.java @@ -0,0 +1,10 @@ +package com.github.kuangcp.tank.v1; + +/** + * 铁块 + */ +public class Iron extends Hinder { + public Iron(int i, int j) { + super(i, j); + } +} diff --git a/gui/src/main/java/com/github/kuangcp/tank/v1/Tank.java b/gui/src/main/java/com/github/kuangcp/tank/v1/Tank.java new file mode 100644 index 00000000..a171b64f --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/v1/Tank.java @@ -0,0 +1,82 @@ +package com.github.kuangcp.tank.v1; + +/** + * 最起初的坦克类 + * 往后有继承 + */ +public class Tank { + int x; // 坦克中心的横坐标 + int y; // 坦克中心的纵坐标 + int direct = 0; // 初始方向 + int type = 0; // 坦克的种类 + int speed = 5; // 前进的步长 + boolean isLive = true;//是否存活 + int Life = 1;//生命值 + + public int getLife() { + return Life; + } + + public void setLife(int life) { + Life = life; + } + + public boolean getisLive() { + return isLive; + } + + public void setLive(boolean isLive) { + this.isLive = isLive; + } + + public int getSpeed() { + return speed; + } + + public void setSpeed(int s) { + speed = s; + } + + public int getDirect() { + return direct; + } + + public void setDirect(int direct) { + this.direct = direct; + } + + public int getType() { + return type; + } + + public void setType(int type) { + this.type = type; + } + + public int getX() { + return x; + } + + public void setX(int x) { + this.x = x; + } + + public int getY() { + return y; + } + + public void setY(int y) { + this.y = y; + } +// 判断子弹是否击中坦克 +// public void Bong(){ +// +// } + + // 构造器 + public Tank(int x, int y, int speed) { + this.x = x; + this.y = y; + this.speed = speed; + } +} diff --git a/gui/src/main/java/com/github/kuangcp/tank/v1/TankGame1.java b/gui/src/main/java/com/github/kuangcp/tank/v1/TankGame1.java new file mode 100644 index 00000000..b936490a --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/v1/TankGame1.java @@ -0,0 +1,238 @@ +package com.github.kuangcp.tank.v1; +/** + * 1 绘画出坦克 并且能做到对它的移动操作 + * 2 移动最好封装成函数(面向对象思想)这是坦克的行为属性 可扩展 + * 3 加上对坦克运动的边界限定 + */ + +import javax.swing.*; +import java.awt.*; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; +import java.util.Vector; + +@SuppressWarnings("serial") +public class TankGame1 extends JFrame { + + + MyPanel mp = null; + @SuppressWarnings("unused") + public static void main(String[] args) { + // TODO Auto-generated method stub + TankGame1 Tank = new TankGame1(); + } + + //最外层JFrame的构造函数 + public TankGame1 (){ + + + mp = new MyPanel(); + + this.add(mp); + //注册键盘监听 + //下面的语句翻译为 :当前类的监;听者是mp + this.addKeyListener(mp); + + this.setLocation(900,200); + this.setSize(500,400); + this.setVisible(true); + this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + } + +} + +@SuppressWarnings("serial") +///////////////////////////////////////////////////////////////////////////// +class MyPanel extends JPanel implements KeyListener { + + //定义我的坦克 + Hero hero = null; + int enSize = 3; //敌人的数量 + //定义泛型的集合类 + Vector ets = new Vector(); + Vector bricks = new Vector(); + + //画板的构造函数 放图形的对象 + public MyPanel(){ + //多个敌方坦克应该用集合类,而且要考虑线程安全所以不能用ArrayList只能用Vector + //实例化英雄坦克 +// hero = new Hero(10,200,10,ets,bricks);//坐标和步长 + + + //初始化敌人的坦克 + /*for (int i=0;i0) + hero.moveleft(); + + } + if (e.getKeyCode()== KeyEvent.VK_D){ + // System.out.print("右"); + hero.setDirect(3); + if ((hero.getX()+15)<405) + hero.moveright(); + } + + if (e.getKeyCode()== KeyEvent.VK_W){ + // System.out.print("上"); + hero.setDirect(0); + if ((hero.getY()-13)>0) + hero.moveup(); + + } + if (e.getKeyCode()== KeyEvent.VK_S){ + // System.out.print("下"); + hero.setDirect(1); + if ((hero.getY()-15)<275) + hero.movedown(); + + }this.repaint(); + + //必须重新绘制窗口,不然上面的方法不能视觉上动起来 + this.repaint(); + + } + + public void keyReleased(KeyEvent arg0) { + // TODO Auto-generated method stub + + } + + public void keyTyped(KeyEvent arg0) { + // TODO Auto-generated method stub + + } +} +/* +//为了能一边走一边发子弹而设计的专门处理按下j键子弹发射的类 +class JS extends JPanel implements KeyListener{ + + Hero hero=null; + public JS(){} + public JS(Hero h){ + hero = h; + } + public void keyTyped(KeyEvent e) { + // TODO Auto-generated method stub + + } + + public void keyPressed(KeyEvent e) { + // TODO Auto-generated method stub + if (e.getKeyChar() == KeyEvent.VK_J){ + //开火 + hero.shotEnemy(); + } + + //必须重新绘制窗口,不然上面的方法不能视觉上动起来 + this.repaint(); + + } + + public void keyReleased(KeyEvent e) { + // TODO Auto-generated method stub + + } + +} +*/ \ No newline at end of file diff --git a/gui/src/main/java/com/github/kuangcp/tank/v2/MyPanel.java b/gui/src/main/java/com/github/kuangcp/tank/v2/MyPanel.java new file mode 100644 index 00000000..d3982bb4 --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/v2/MyPanel.java @@ -0,0 +1,265 @@ +package com.github.kuangcp.tank.v2; + + +import com.github.kuangcp.tank.v1.Brick; +import com.github.kuangcp.tank.v1.Demons; +import com.github.kuangcp.tank.v1.Hero; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; +import java.util.Vector; + + +@SuppressWarnings("serial") +public class MyPanel extends JPanel implements KeyListener, Runnable { + + //定义我的坦克 + Hero hero = null; + int enSize = 3; // 敌人的数量 + //定义一个 泛型的集合类 表示敌人坦克集合 + Vector ets = new Vector(); + Vector bricks = new Vector(); + + + //画板的构造函数 放图形的对象 + public MyPanel() { + //多个敌方坦克应该用集合类,而且要考虑线程安全所以不能用ArrayList只能用Vector + //实例化英雄坦克 +// hero = new Hero(10,200,10,ets,bricks);//坐标和步长 +// JS j = new JS(hero); + + + //初始化敌人的坦克 + /*for (int i=0;i0){ + g.draw3DRect(sx, sy, 1, 1, false); + sy-=3; + repaint(); + } + break; + case 1: + while (sy<300){ + g.draw3DRect(sx, sy, 1, 1, false); + sy+=3; + repaint(); + } + break; + case 2: + while (sx>0){ + g.draw3DRect(sx, sy, 1, 1, false); + sx-=3; + repaint(); + } + break; + case 3: + while (sx<400){ + g.draw3DRect(sx, sy, 1, 1, false); + sx+=3; + repaint(); + } + break; + } + repaint(); + } + */ + //画出坦克的函数 XY是坦克中心的坐标,不是画图参照点 + public void drawTank(int X, int Y, Graphics g, int direct, int type) { + //判断坦克类型 + int x, y;//系统画图函数的参照点 + switch (type) { + case 1: + g.setColor(Color.cyan); + break; + case 0: + g.setColor(Color.yellow); + hero.g = g; + break; + } + + //判断方向 + switch (direct) { + + case 0: {//向上VK_UP + x = X - 10; + y = Y - 15; + //1.左边的矩形 + g.fill3DRect(x, y, 5, 30, false); + //2.画出右边矩形 + g.fill3DRect(x + 15, y, 5, 30, false); + //3.画出中间矩形 + g.fill3DRect(x + 5, y + 5, 10, 20, false); + //4.画出圆形 + g.fillOval(x + 4, y + 10, 10, 10); + //5.画出线 + g.drawLine(x + 9, y + 15, x + 9, y); + break; + } + case 1: {//向下 VK_DOWN + x = X - 10; + y = Y - 15; + g.fill3DRect(x, y, 5, 30, false); + g.fill3DRect(x + 15, y, 5, 30, false); + g.fill3DRect(x + 5, y + 5, 10, 20, false); + g.fillOval(x + 4, y + 10, 10, 10); + g.drawLine(x + 9, y + 15, x + 9, y + 30); + break; + } + case 2: {//向左 VK_LEFT + x = X - 15; + y = Y - 10; + g.fill3DRect(x, y, 30, 5, false); + g.fill3DRect(x, y + 15, 30, 5, false); + g.fill3DRect(x + 5, y + 5, 20, 10, false); + g.fillOval(x + 10, y + 4, 10, 10); + g.drawLine(x + 15, y + 9, x, y + 9); + break; + } + case 3: {//向右VK_RIGHT + x = X - 15; + y = Y - 10; + g.fill3DRect(x, y, 30, 5, false); + g.fill3DRect(x, y + 15, 30, 5, false); + g.fill3DRect(x + 5, y + 5, 20, 10, false); + g.fillOval(x + 10, y + 4, 10, 10); + g.drawLine(x + 15, y + 9, x + 30, y + 9); + break; + } + } + } + + //当按下键盘上的键时监听者的处理函数 + public void keyPressed(KeyEvent e) { +// 加了if条件后 实现了墙的限制(如果是游戏中的道具,该怎么办) + + if (e.getKeyCode() == KeyEvent.VK_A) { + // System.out.print("左"); + hero.setDirect(2); + if ((hero.getX() - 10) > 0) + hero.moveleft(); + + } + if (e.getKeyCode() == KeyEvent.VK_D) { + // System.out.print("右"); + hero.setDirect(3); + if ((hero.getX() + 15) < 405) + hero.moveright(); + } + + if (e.getKeyCode() == KeyEvent.VK_W) { + // System.out.print("上"); + hero.setDirect(0); + if ((hero.getY() - 13) > 0) + hero.moveup(); + + } + if (e.getKeyCode() == KeyEvent.VK_S) { + // System.out.print("下"); + hero.setDirect(1); + if ((hero.getY() - 15) < 275) + hero.movedown(); + + } + this.repaint(); +// 判断玩家是否按下 J键 //开火 + if (e.getKeyChar() == KeyEvent.VK_J) { + System.out.println("按下 开火键"); +// 自己做的开火函数 +// hero.drawS(hero.getX(), hero.getY(), hero.getDirect(), hero.g); + hero.shotEnemy(); + repaint(); + } + + + //必须重新绘制窗口,不然上面的方法不能视觉上动起来 + this.repaint(); + + } + + public void keyReleased(KeyEvent arg0) { + // TODO Auto-generated method stub + + } + + public void keyTyped(KeyEvent arg0) { + // TODO Auto-generated method stub + + } + + //画板做成线程 画布进行刷新 + public void run() { + // TODO Auto-generated method stub + //每隔100ms重绘 + while (true) { + + try { + Thread.sleep(100); + } catch (Exception e) { + } + + //单纯是为了测试,不需要这句话来耗资源 +// System.out.println("Panel的一个线程睡觉"); +// 进行重绘 + this.repaint(); + } + + } +} \ No newline at end of file diff --git a/gui/src/main/java/com/github/kuangcp/tank/v2/Shot.java b/gui/src/main/java/com/github/kuangcp/tank/v2/Shot.java new file mode 100644 index 00000000..9dcefc09 --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/v2/Shot.java @@ -0,0 +1,71 @@ +package com.github.kuangcp.tank.v2; + +import lombok.extern.slf4j.Slf4j; + +/** + * 子弹类 对象做成了线程 + */ +@Slf4j +public class Shot implements Runnable { + + public int sx; + public int sy; + public int direct; + public static int speed = 8;//如果改动要记得按钮事件里也要改 + public boolean isLive = true;//是否还活着 + + public static int getSpeed() { + return speed; + } + + public static void setSpeed(int s) { + speed = s; + } + + public Shot(int sx, int sy, int direct) { + this.sx = sx; + this.sy = sy; + this.direct = direct; + } + + public void run() { + //延迟子弹发射时间 + try { + Thread.sleep(20); + } catch (Exception e) { + log.error("", e); + } + + do { + try { + Thread.sleep(50); //每个子弹发射的延迟运动的时间 + } catch (Exception e) { + log.error("", e); + } + + switch (direct) { + //上下左右 + case 0: + sy -= speed; + break; + case 1: + sy += speed; + break; + case 2: + sx -= speed; + break; + case 3: + sx += speed; + break; + } + + //判断子弹是否碰到边缘 + if (sx < 20 || sx > 740 || sy < 20 || sy > 540) { + this.isLive = false; + } + if (sx < 440 && sx > 380 && sy < 540 && sy > 480) { + this.isLive = false; + } + } while (this.isLive); + } +} diff --git a/gui/src/main/java/com/github/kuangcp/tank/v2/TankGame2.java b/gui/src/main/java/com/github/kuangcp/tank/v2/TankGame2.java new file mode 100644 index 00000000..7d0a6cef --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/v2/TankGame2.java @@ -0,0 +1,89 @@ +package com.github.kuangcp.tank.v2; +/** + * 能控制坦克运动,有墙的机制 + * + * + * 坦克2.0 版本 目标:按下j键发射直线的子弹 + * 子弹是一个对象,线程 + */ + +import javax.swing.*; + +@SuppressWarnings("serial") +public class TankGame2 extends JFrame { + + + public static MyPanel mp = null;//画板 + + +// JS j = null; + @SuppressWarnings("unused") + public static void main(String[] args) { + // TODO Auto-generated method stub + TankGame2 Tank = new TankGame2(); + } + + //最外层JFrame的构造函数 + public TankGame2 (){ + + mp = new MyPanel();//已经成为一个线程 要启动它 + Thread t = new Thread(mp); + t.start(); + +// j = new JS(); + +// this.add(j); + this.add(mp); + + //注册键盘监听 + //下面的语句翻译为 :当前类的监听者是mp + this.addKeyListener(mp); +// this.addKeyListener(j); + + this.setLocation(900,200); + this.setSize(500,400); + this.setVisible(true); + this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + } + +} + + + + + +///////////////////////////////////////////////////////////////////////////// + +/* +//为了能一边走一边发子弹而设计的专门处理按下j键子弹发射的类 +class JS extends JPanel implements KeyListener{ + + Hero hero=null; + public JS(){} + public JS(Hero h){ + hero = h; + } + public void keyTyped(KeyEvent e) { + // TODO Auto-generated method stub + + } + + public void keyPressed(KeyEvent e) { + // TODO Auto-generated method stub + if (e.getKeyChar() == KeyEvent.VK_J){ + //开火 + hero.shotEnemy(); + } + + //必须重新绘制窗口,不然上面的方法不能视觉上动起来 + this.repaint(); + + } + + public void keyReleased(KeyEvent e) { + // TODO Auto-generated method stub + + } + +} +*/ diff --git a/gui/src/main/java/com/github/kuangcp/tank/v2/Thread1.java b/gui/src/main/java/com/github/kuangcp/tank/v2/Thread1.java new file mode 100644 index 00000000..31c5a837 --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/v2/Thread1.java @@ -0,0 +1,70 @@ +/** + * 演示两种 + * 如果通过 继承Thread类 来开发线程 + * 实现Runnable接口 来开发线程 + * + */ +package com.github.kuangcp.tank.v2; + +public class Thread1 { + + public static void main(String[] args) { + + //创建一个Cat对象 + Cat cat = new Cat(); + //启动线程 + cat.start(); + + //实现接口的方式来启动线程 + Dog dog = new Dog(); + Thread t = new Thread(dog); + t.start(); + + } + +} + +//继承线程类 +class Cat extends Thread { + int times = 0; + + public void run (){ + while(true){ + //休眠一秒 ,线程就会进入Blocked状态,并释放资源 + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + times++; + System.out.println("Cat hello world !"+times); + if (times==10){ + break;//这里跳出while循环,就会结束这个线程 + } + } + } +} + +//实现接口 +class Dog implements Runnable { + + int times = 0; + public void run() { + while(true){ + //休眠一秒 ,线程就会进入Blocked状态,并释放资源 + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + times++; + System.out.println("Dog Hello World !"+times); + if (times==10){ + break;//这里跳出while循环,就会结束这个线程 + } + } + } + +} \ No newline at end of file diff --git a/gui/src/main/java/com/github/kuangcp/tank/v2/Thread2.java b/gui/src/main/java/com/github/kuangcp/tank/v2/Thread2.java new file mode 100644 index 00000000..65d4ad78 --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/v2/Thread2.java @@ -0,0 +1,84 @@ +package com.github.kuangcp.tank.v2; +/** + * 两个线程同时运作 + * @author lenovo + * + */ +public class Thread2 { + + public static void main(String[] a){ + T1 t1 = new T1(10); + T2 t2 = new T2(10); + + Thread t = new Thread(t1); + Thread s = new Thread(t2); + + t.start(); + s.start(); + } +} + +//最好是用实现接口来写 方便以后扩展 +class T1 implements Runnable { + + //做累加的功能 + int n=0; + int res =0; + int times =0; + public T1(int n){ + this.n = n; + } + public void run() { + while (true){ + try { + Thread.sleep(1000); + + }catch (Exception e ){ + e.printStackTrace(); + } + res+=(++times); + System.out.println("当前结果是:"+res); + if (times == n){ + System.out.println("最后的结果是:"+res); + break; + } + } + } + +} + +class T2 implements Runnable { + + int n=0; + int times=0; + + public T2(int n){ + this.n = n; + } + public void run (){ + while (true){ + try { + Thread.sleep(400); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + times++; + System.out.println("我是一个线程,在输出第"+times+"hello world"); + if (times == n) break; + } + } +} + + + + + + + + + + + + diff --git a/gui/src/main/java/com/github/kuangcp/tank/v2/Thread3.java b/gui/src/main/java/com/github/kuangcp/tank/v2/Thread3.java new file mode 100644 index 00000000..b4135f0a --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/v2/Thread3.java @@ -0,0 +1,41 @@ +/** + * 一个线程对象只能启动一个线程 + * 一个线程只能启动一次 + * + */ + +package com.github.kuangcp.tank.v2; + +public class Thread3 { + + public static void main(String[] args) { + // TODO Auto-generated method stub + + + Cat2 cat1 = new Cat2(); + cat1.start(); +// cat1.start(); 这样启动一个线程是要报错的 + + Dog2 dog1 = new Dog2(); + Thread t = new Thread(dog1); + Thread t2 = new Thread(dog1); + t.start(); +// t.start(); 这样也是要报错的,不允许启动两次 + t2.start(); + //这样启动线程不会报错,因为是两个线程的对象t和t2,虽然是封装的是同一个对象dog1 + + } + +} + +class Cat2 extends Thread { + public void run(){ + System.out.println("猫"); + } +} + +class Dog2 implements Runnable { + public void run(){ + System.out.println("狗"); + } +} \ No newline at end of file diff --git a/gui/src/main/java/com/github/kuangcp/tank/v2/Thread4.java b/gui/src/main/java/com/github/kuangcp/tank/v2/Thread4.java new file mode 100644 index 00000000..7f1ba281 --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/v2/Thread4.java @@ -0,0 +1,77 @@ +/** + * 如果有多个线程因等待同一个对象的标志而处于阻塞状态时,当该对象的标志位(对象锁/文件锁)恢复到1状态时 + * 只会有一个线程能够进入同步代码执行,其他的线程仍然处于阻塞的状态 + * 俩线程 并发 我读你 你读我 互斥 + * 就会容易发生死锁 要尽量避免 + * + */ +package com.github.kuangcp.tank.v2; + +public class Thread4 { + + public static void main(String[] args) { + // TODO Auto-generated method stub + + //定义三个售票窗口 其实不是三个对象,这样的话初始化这里都是2000张,每个窗口都当成一个线程来理解就好了 + + TicketWindow tw1 = new TicketWindow(); +// TicketWindow tw2 = new TicketWindow(); +// TicketWindow tw3 = new TicketWindow(); + + //把一个对象封装入三个线程里 和三个对象分别装的区别? + //就是说,三个线程都在各自处理各自的对象,并没有并发,同步的问题 + //可是一旦有静态的(共享性的成员)就会有并发的那些问题,而且出现的问题还都是随机的 + Thread t1 = new Thread(tw1); + Thread t2 = new Thread(tw1); + Thread t3 = new Thread(tw1); + + t1.start(); + t2.start(); + t3.start(); + } + +} + +//售票窗口 +class TicketWindow implements Runnable { + //一共两千张票 + private static int nums = 200; + @SuppressWarnings("unused") + private Dog3 dog = new Dog3(); + + + public void run (){ + + while (true){ + + //保证其原子性 (同步代码块) + /** 不用同步的锁 + * 如果睡眠时间够长,使用三个对象分别封装 就不会容易出现并发的问题 + * 但是时间一旦短了呢 就会有明显的错误*/ + synchronized (this) { +// synchronized (dog) 括号里可以放任意的对象只是利用对象的一个对象锁功能, +// 相当于用这个对象做一个标志性的状态信息, 0 1 (标志位) + //先判断是否还有票 + if (nums >0){ + //显示售票信息、 + + System.out.println(""+ Thread.currentThread().getName()+"正在售出第 "+nums+"张票"); + try { + Thread.sleep(100); //出票速度 一秒一张 + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + nums--; + }else { + //售票结束 + break; + } + } + } + } +} + +class Dog3{ + +} diff --git a/gui/src/main/java/com/github/kuangcp/tank/v3/Bomb.java b/gui/src/main/java/com/github/kuangcp/tank/v3/Bomb.java new file mode 100644 index 00000000..fb0d921f --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/v3/Bomb.java @@ -0,0 +1,36 @@ +package com.github.kuangcp.tank.v3; + +/** + * 1 准备好图片 + * 2 定义这个类 + * 3 击中坦克的时候创建爆炸对象,加入集合 + *

+ * 如果坐标在变 最好做成一个线程 + * 用一个变量来规定图片放映的顺序及是否放映结束 结束了就可以从集合中删除 + * 不得不说 思路的确挺好 + */ +public class Bomb { + + int bx, by; + //炸弹的生命 + int life = 15; + boolean isLive = true; + + public Bomb(int bx, int by) { + this.bx = bx; + this.by = by; + } + + public Bomb() { + } + + //减少生命值 + public void lifeDown() { + if (life > 0) { + life--; + } else { + + this.isLive = false; + } + } +} diff --git a/gui/src/main/java/com/github/kuangcp/tank/v3/GameMain.java b/gui/src/main/java/com/github/kuangcp/tank/v3/GameMain.java new file mode 100644 index 00000000..a8b4650a --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/v3/GameMain.java @@ -0,0 +1,10 @@ +package com.github.kuangcp.tank.v3; + +public class GameMain { + + public static void main(String[] args) { + TankFrame T = new TankFrame(); + Thread t = new Thread(T); + t.start(); + } +} diff --git a/gui/src/main/java/com/github/kuangcp/tank/v3/KE.java b/gui/src/main/java/com/github/kuangcp/tank/v3/KE.java new file mode 100644 index 00000000..5f4d4dc0 --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/v3/KE.java @@ -0,0 +1,10 @@ +package com.github.kuangcp.tank.v3; + +public class KE { + int Key; + + public KE(){} + public KE(int k){ + Key = k; + } +} diff --git a/gui/src/main/java/com/github/kuangcp/tank/v3/MyPanel3.java b/gui/src/main/java/com/github/kuangcp/tank/v3/MyPanel3.java new file mode 100644 index 00000000..e10405da --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/v3/MyPanel3.java @@ -0,0 +1,682 @@ + +package com.github.kuangcp.tank.v3; + + +import com.github.kuangcp.tank.v1.Brick; +import com.github.kuangcp.tank.v1.Demons; +import com.github.kuangcp.tank.v1.Hero; +import com.github.kuangcp.tank.v1.Iron; +import com.github.kuangcp.tank.v2.Shot; +import com.github.kuangcp.tank.util.TankTool; +import lombok.extern.slf4j.Slf4j; + +import javax.imageio.ImageIO; +import javax.swing.*; +import java.awt.*; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; +import java.io.IOException; +import java.util.Vector; + + +/** + * (20, 20, 720, 520) 该画板坦克运动区域 + */ +@SuppressWarnings("serial") +@Slf4j +public class MyPanel3 extends JPanel implements KeyListener, Runnable { + + //定义我的坦克 + Hero hero = null; + PressedTwo PT = null; + TankFrame T; + static boolean Continue = true;//是否 不是继续上局 + final int Q = (int) (Math.random() * 5); + // 敌人的数量 + static int enSize = 10; + //定义一个 泛型的集合ets 表示敌人坦克集合 + public Vector ets = new Vector(); + + //定义炸弹爆炸集合 + public Vector bombs = new Vector(); + + //定义砖块集合 + public Vector bricks = new Vector(); + public Vector irons = new Vector(); + //所有按下键的code集合 + public Vector actions = new Vector(); + public static int[][] ETS = new int[12][2]; + public static int[] myself = new int[6]; + + //声明三个图片引用 + Image image1 = null; + Image image2 = null; + Image image3 = null; + Image over = null; + Image[] Me = new Image[5]; + Image Win = null; + + /** + * 画板的构造函数 用来初始化对象 + */ + //多个敌方坦克应该用集合类,而且要考虑线程安全所以不能用ArrayList只能用Vector + public MyPanel3(TankFrame T) { +// System.out.println("MyPanel3的成员地址"+ets); + this.T = T; +// 创建英雄坦克 + if (Continue) {//正常启动并创建坦克线程 + hero = new Hero(480, 500, 3, ets, bricks, irons);//坐标和步长和敌人坦克集合 + hero.setLife(10);//设置生命值 + } else { + hero = new Hero(myself[0], myself[1], 3, ets, bricks, irons); + hero.setLife(myself[2]); + hero.setPrize(myself[3]); + } + + //多键监听实现 + PT = new PressedTwo(actions, hero, this); + Thread p = new Thread(PT); + p.start(); + +// 创建 敌人的坦克 + Demons ett = null; + if (Continue) {//正常启动并创建坦克线程 + for (int i = 0; i < enSize; i++) { + //在四个随机区域产生坦克 + switch ((int) (Math.random() * 4)) { + case 0: + ett = new Demons(20 + (int) (Math.random() * 30), 20 + (int) (Math.random() * 30), 2, i % 4); + ett.SetInfo(hero, ets, bricks, irons); + break; + case 1: + ett = new Demons(700 - (int) (Math.random() * 30), 20 + (int) (Math.random() * 30), 2, i % 4); + ett.SetInfo(hero, ets, bricks, irons); + break; + case 2: + ett = new Demons(20 + (int) (Math.random() * 30), 200 + (int) (Math.random() * 30), 2, i % 4); + ett.SetInfo(hero, ets, bricks, irons); + break; + case 3: + ett = new Demons(700 - (int) (Math.random() * 30), 200 + (int) (Math.random() * 30), 2, i % 4); + ett.SetInfo(hero, ets, bricks, irons); + break; + } + //创建敌人坦克 再加入到集合里去 + // ett = new Demons(20+(int)(Math.random()*700),20+(int)(Math.random()*520) ,2 ,i%4,hero,ets,bricks,irons); + //默认方向是上 要调整成下(1) + // et.setDirect(1); + //启动线程 +// System.out.println(ett); + Thread t = new Thread(ett); + t.start(); + //坦克加入集合 + ets.add(ett); + } +/**进入读取文件步骤*/ + } else {//进入读取步骤 + for (int i = 0; i < ETS.length; i++) {//这里的ETS的length就是二维数组的行数 + if (ETS[i][0] == 0) break; + + ett = new Demons(ETS[i][0], ETS[i][1], 2, i % 4); + ett.SetInfo(hero, ets, bricks, irons); + Thread t = new Thread(ett); + t.start(); + //坦克加入集合 + ets.add(ett); +// System.out.print("创建单个坦克地址:"+ett); +// System.out.println("_____X="+ett.getX()+"Y="+ett.getY()); + +// System.out.println("进入了读取数组这里"); + } + } +// System.out.println("构造器里:MyPanel3的成员地址"+ets); + //创建砖块 +// createB(bricks, 40, 40, 200, 400); +// createB(bricks, 200, 40, 400, 100); +// createB(bricks, 400, 40, 700, 400); +// createB(bricks, 200, 300, 400, 400); +// createB(bricks, 40, 40, 700, 400); + //眼睛 + createI(irons, 180, 100, 260, 180); + createI(irons, 540, 100, 620, 180); +// createB(bricks, 200, 120, 240, 160); +// createB(bricks, 560, 120, 600, 160); + //鼻子 + int m = 400, n = 260; + createI(irons, m, n, m + 20, n + 10); + createI(irons, m - 10, n + 10, m + 30, n + 20); + createI(irons, m - 20, n + 20, m + 40, n + 30); +// createB(bricks, 300, 240, 520, 360); + //左右中间 +// createI(irons, 580, 370, 740, 380); +// createI(irons, 20, 370, 210, 380); + //左右下角 + createB(bricks, 20, 310, 300, 540); + createB(bricks, 520, 310, 740, 540); +// createI(irons, 250, 130, 300, 300); +// createI(irons, 300, 130, 400, 200); + //头像附近 + createB(bricks, 360, 460, 460, 480); + createB(bricks, 360, 480, 380, 540); + createB(bricks, 440, 460, 460, 540); + + createI(irons, 330, 410, 480, 430); + + +//初始化图片 引用到src目录下和包同级的图片 +// image1 = Toolkit.getDefaultToolkit().getImage(Panel.class.getResource("/bomb_1.gif")); + + //放在同级包目录下的图片:导出的时候也能找到 + /** + * for (int i=1;i<3;i++){ + * images[i] = ImageIO.read( + * getClass().getResource("/images/bomb_1.gif")); + * } + * /Game.Tanke/bomb_1.gif + */ + + // 如果这样传入图片的话,一运行就会抛异常,所以就不会有窗口显示出来,但是在后台跑 + try { + image1 = ImageIO.read(getClass().getResourceAsStream("/images/bomb_1.gif")); + image2 = ImageIO.read(getClass().getResourceAsStream("/images/bomb_2.gif")); + image3 = ImageIO.read(getClass().getResourceAsStream("/images/bomb_3.gif")); + over = ImageIO.read(getClass().getResourceAsStream("/images/Over4.jpg")); + Me[0] = ImageIO.read(getClass().getResourceAsStream("/images/Me.jpg")); + Me[1] = ImageIO.read(getClass().getResourceAsStream("/images/Me2.jpg")); + Me[2] = ImageIO.read(getClass().getResourceAsStream("/images/Me3.jpg")); + Me[3] = ImageIO.read(getClass().getResourceAsStream("/images/Me4.jpg")); + Me[4] = ImageIO.read(getClass().getResourceAsStream("/images/Me5.jpg")); + Win = ImageIO.read(getClass().getResourceAsStream("/images/Win2.jpg")); + } catch (IOException e) { + log.error("", e); + } + } + + @Override + public void paint(Graphics g) { + /*画出坦克运动区域 */ + super.paint(g); + g.setColor(new Color(0, 0, 0)); + g.fillRect(0, 0, 760, 560);//填满一个黑色矩形,说明了是一整个JPanel在JFrame上的 + g.setColor(Color.green); + g.drawRect(20, 20, 720, 520); + Shot myShot = null; + + /*画出障碍物__砖__ 铁__*/ + + g.setColor(new Color(200, 200, 200)); + for (int i = 0; i < irons.size(); i++) { + Iron ir = irons.get(i); + if (ir.getLive()) { + g.fill3DRect(ir.getHx(), ir.getHy(), 20, 10, false); + } else { + irons.remove(ir); + } + } + + g.setColor(new Color(190, 60, 50)); + for (int i = 0; i < bricks.size(); i++) { + Brick bs = bricks.get(i); + if (bs.getLive()) { + g.fill3DRect(bs.getHx(), bs.getHy(), 20, 10, false); + } else { + bricks.remove(bs); + } + } + + //眼睛 + g.setColor(Color.YELLOW); + g.fillRect(200, 120, 40, 40); + g.fillRect(560, 120, 40, 40); + /*画出头像*/ + + g.drawImage(Me[Q], 380, 480, 60, 60, this); + /*画出主坦克*/ + if (hero.getisLive()) { + for (Demons et : ets) { + for (int j = 0; j < et.ds.size(); j++) { + if (et.ds.get(j).isLive) { + TankTool.Bong(hero, et.ds.get(j), bombs); + } + } + } + + this.drawTank(hero.getX(), hero.getY(), g, hero.getDirect(), hero.getType()); + } + + +/**画出自己的子弹*/ //画子弹是可以封装成一个方法的 + // 从ss 这个子弹集合中 取出每颗子弹,并画出来 + for (int i = 0; i < hero.ss.size(); i++) { + myShot = hero.ss.get(i); + for (Brick brick : bricks) { + TankTool.judgeHint(myShot, brick); + } + for (Iron iron : irons) { + TankTool.judgeHint(myShot, iron); + } + if (myShot.sx < 440 && myShot.sx > 380 && myShot.sy < 540 && myShot.sy > 480) { + myShot.isLive = false; + hero.setLive(false); + } + if (hero.ss.get(i) != null && hero.ss.get(i).isLive) {//为什么后面的判断条件 存不存在都会没影响呢 +// System.out.println("当前是"+i+"个子弹线程"); + g.draw3DRect(myShot.sx, myShot.sy, 1, 1, false); + + // g.draw3DRect(Hero.ss.get(i).sx, Hero.ss.get(i).sy, 1, 1, false); + } + if (!myShot.isLive) { + //子弹线程死了 就要把它从集合中删除 + hero.ss.remove(myShot); + } + + } +/**敌人子弹*/ + for (Demons et : ets) { + + for (int i = 0; i < et.ds.size(); i++) { + myShot = et.ds.get(i); + for (Brick brick : bricks) { + TankTool.judgeHint(myShot, brick); + } + for (Iron iron : irons) { + TankTool.judgeHint(myShot, iron); + } + if (myShot.sx < 440 && myShot.sx > 380 && myShot.sy < 540 && myShot.sy > 480) { + myShot.isLive = false; + hero.setLive(false); + } + if (et.ds.get(i) != null && et.ds.get(i).isLive) { + // System.out.println("当前是"+i+"个子弹线程"); + g.setColor(Color.cyan); + g.draw3DRect(myShot.sx, myShot.sy, 1, 1, false); + + } + if (!myShot.isLive) { + //子弹线程死了 就要把它从集合中删除 + et.ds.remove(myShot); + } + + } + } +/** 画出炸弹*/ + for (int i = 0; i < bombs.size(); i++) { + //取出炸弹 + Bomb b = bombs.get(i); +// System.out.println("size = "+bombs.size()); + if (b.life > 10) { + g.drawImage(image1, b.bx, b.by, 30, 30, this); +// if(rr){ +// Audio B = new Audio("./src/RE/GameBegin.wav"); +// B.start(); +// rr = false; +// } + + } else if (b.life > 5) { + g.drawImage(image2, b.bx, b.by, 30, 30, this); + } else { + g.drawImage(image3, b.bx, b.by, 30, 30, this); + } + //让b的生命减少 + b.lifeDown(); + + //炸弹的生命值为零 移出集合 + if (b.life == 0) { + bombs.remove(b); + + } +// hero.LifeDown(); +// System.out.println("size = "+bombs.size()); + } +/**画出敌人坦克*/ + +// if(Continue){ + //坦克少于5个就自动添加4个 + if (ets.size() < 5) { + for (int i = 0; i < 4; i++) { + Demons d = new Demons(20 + (int) (Math.random() * 400), 20 + (int) (Math.random() * 300), 2, i % 4); + d.SetInfo(hero, ets, bricks, irons); + Thread R = new Thread(d); + R.start(); + ets.add(d); + } + } +// } +// for(int i=0;i=40){ +// g.drawImage(Win,0,0,760,560,this); +// g.drawString("您的总成绩为:"+hero.getPrize(), 320, 500); +// +// } + + + } +////////////////////////////////////////////////////////////////////////////////// + + /** + * 画出所有坦克的函数 XY是坦克中心的坐标,不是画图参照点 + */ + public void drawTank(int X, int Y, Graphics g, int direct, int type) { + + //判断坦克类型 + int x, y;//系统画图函数的参照点 + switch (type) { + case 1: + g.setColor(Color.cyan); + break; + case 0: + g.setColor(Color.yellow); + hero.g = g; + + break; + } + + //判断方向 + switch (direct) {//上下左右 + + case 0: {//向上VK_UP + + x = X - 10; + y = Y - 15;//把坦克中心坐标换算成系统用来画坦克的坐标 + //(并且全是取的左上角) + + //1.左边的矩形 + g.fill3DRect(x, y, 5, 30, false); + //2.画出右边矩形 + g.fill3DRect(x + 15, y, 5, 30, false); + //3.画出中间矩形 + g.fill3DRect(x + 5, y + 5, 10, 20, false); + //4.画出圆形 + g.fillOval(x + 4, y + 10, 10, 10); + //5.画出线 + g.drawLine(x + 9, y + 15, x + 9, y); +// for(int i=0;i<10;i++){ +//// g.setColor(Color.BLACK); +// g.fill3DRect(x,y+3*i, 5, 2,false); +// } +// for(int i=0;i<10;i++){ +//// g.setColor(Color.BLACK); +// g.fill3DRect(x+15,y+3*i, 5, 2,false); +// } +//// g.setColor(Color.yellow); + break; + } + case 1: {//向下 VK_DOWN + x = X - 10; + y = Y - 15; + g.fill3DRect(x, y, 5, 30, false); + g.fill3DRect(x + 15, y, 5, 30, false); + g.fill3DRect(x + 5, y + 5, 10, 20, false); + g.fillOval(x + 4, y + 10, 10, 10); + g.drawLine(x + 9, y + 15, x + 9, y + 30); + break; + } + case 2: {//向左 VK_LEFT + x = X - 15; + y = Y - 10; + g.fill3DRect(x, y, 30, 5, false); + g.fill3DRect(x, y + 15, 30, 5, false); + g.fill3DRect(x + 5, y + 5, 20, 10, false); + g.fillOval(x + 10, y + 4, 10, 10); + g.drawLine(x + 15, y + 9, x, y + 9); + break; + } + case 3: {//向右VK_RIGHT + x = X - 15; + y = Y - 10; + g.fill3DRect(x, y, 30, 5, false); + g.fill3DRect(x, y + 15, 30, 5, false); + g.fill3DRect(x + 5, y + 5, 20, 10, false); + g.fillOval(x + 10, y + 4, 10, 10); + g.drawLine(x + 15, y + 9, x + 30, y + 9); + break; + } + } + } + + //定义变量以便于控制按键的处理 + //把按键的处理做成一个集合,循环遍历,在另一个类里面做 + KE w, a, s, d, j; + int count = 0; + boolean ww = true, aa = true, ss = true, dd = true, jj = true; + + /** + * 实现了墙 + */ + //当按下键盘上的键时监听者的处理函数 + @Override + public void keyPressed(KeyEvent e) { +// 加了if(内层的)限制后 实现了墙的限制(如果是游戏中的道具,该怎么办) + + if (e.getKeyCode() == KeyEvent.VK_A) { + + if (aa) { + a = new KE(KeyEvent.VK_A); + actions.add(a); + count++; + aa = false; + } + + + } + if (e.getKeyCode() == KeyEvent.VK_D) { + if (dd) { + d = new KE(KeyEvent.VK_D); + actions.add(d); + count++; + dd = false; + } + + + } + + if (e.getKeyCode() == KeyEvent.VK_W) { + if (ww) { + w = new KE(KeyEvent.VK_W); + actions.add(w); + count++; + ww = false; + } + + + } + if (e.getKeyCode() == KeyEvent.VK_S) { + if (ss) { + s = new KE(KeyEvent.VK_S); + actions.add(s); + count++; + ss = false; + } + } + this.repaint(); + + if (e.getKeyChar() == KeyEvent.VK_J) { + if (jj) { + j = new KE(KeyEvent.VK_J); + actions.add(j); + count++; + jj = false; + } + + + /**自己做的开火函数 + //hero.drawS(hero.getX(), hero.getY(), hero.getDirect(), hero.g); + */ + + } + //必须重新绘制窗口,不然上面的方法不能视觉上动起来 + this.repaint(); + count = 0; + + } + + //键离开了就把按键从集合中移除 + @Override + public void keyReleased(KeyEvent re) { + // TODO Auto-generated method stub + switch (re.getKeyCode()) { + case KeyEvent.VK_A: + actions.remove(a); + aa = true; + break; + case KeyEvent.VK_D: + actions.remove(d); + dd = true; + break; + case KeyEvent.VK_S: + actions.remove(s); + ss = true; + break; + case KeyEvent.VK_W: + actions.remove(w); + ww = true; + break; + case KeyEvent.VK_J: + actions.remove(j); + jj = true; + break; + default: + break; + } + + } + + @Override + public void keyTyped(KeyEvent arg0) { + // TODO Auto-generated method stub + + } + + /** + * 创建 一块矩形的砖(20*10) 的函数 + */ + public void createB(Vector bricks, int startX, int startY, int endX, int endY) { + for (int i = startX; i < endX; i += 20) { + for (int j = startY; j < endY; j += 10) { + Brick bs = new Brick(i, j); + bricks.add(bs); + + } + } + } + + /** + * 创建铁块(20*10) + */ + public void createI(Vector irons, int startX, int startY, int endX, int endY) { + for (int i = startX; i < endX; i += 20) { + for (int j = startY; j < endY; j += 10) { + Iron bs = new Iron(i, j); + irons.add(bs); + + } + } + } + + //画板做成线程 画布进行刷新 + @Override + public void run() { + //每隔100ms重绘 + while (true) { + try { + Thread.sleep(10); + //里面的数字就是刷新时间的间隔 而且每当按下J键就会拉起MyPanel线程 相当于加快了刷新 + } catch (Exception e) { + log.error("", e); + } + + // 进行重绘 + this.repaint(); + + // 这里的中断,只是关掉了刷新,但是后台的坦克,子弹线程还在跑 + // if(!hero.getisLive()){ + // break; + // } + } + } + + public static int getEnSize() { + return enSize; + } + + public static void setEnSize(int enSize) { + MyPanel3.enSize = enSize; + } +} \ No newline at end of file diff --git a/gui/src/main/java/com/github/kuangcp/tank/v3/MyPanel301.java b/gui/src/main/java/com/github/kuangcp/tank/v3/MyPanel301.java new file mode 100644 index 00000000..f3f9b021 --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/v3/MyPanel301.java @@ -0,0 +1,150 @@ + +package com.github.kuangcp.tank.v3; + + +import com.github.kuangcp.tank.v1.Brick; +import com.github.kuangcp.tank.v1.Demons; +import com.github.kuangcp.tank.v1.Hero; +import com.github.kuangcp.tank.v1.Iron; +import com.github.kuangcp.tank.v2.Shot; +import com.github.kuangcp.tank.util.Audio; +import com.github.kuangcp.tank.util.Saved; +import com.github.kuangcp.tank.util.Setting; + +import javax.swing.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.Vector; + +/** + * 用来放按钮的面板 + * 并且监控按钮事件 + */ +@SuppressWarnings("serial") +public class MyPanel301 extends JPanel implements ActionListener { + + + static Thread t = null; + TankFrame T; + Hero hero; + Vector ets; + Vector irons; + Vector bricks; + int[][] ETS; + int[] myself; + Audio Begin; + + @Override + public void actionPerformed(ActionEvent ae) { + //监听从按钮发来的事件 + + +// if(ae.getActionCommand().equals("开始")||ae.getActionCommand().equals("Continue")){ + //上面是不合理的,会有隐藏的bug在里面,这种做法要抛弃 + if (ae.getActionCommand().equals("开始")) { + System.out.println("开始"); + T.count = 1; + T.mp.Continue = true; + Shot.setSpeed(8); + T.remove(T.jsp1); + t = new Thread(T); + + t.start();//将画板线程开启 + if (Begin != null) { + Begin.setLive(false); + } + Begin = new Audio("./src/RE/GameBegin.wav"); + Begin.start(); + } + if (ae.getActionCommand().equals("结束")) { + System.out.println("结束"); + System.exit(0); + } + if (ae.getActionCommand().equals("暂停")) { + System.out.println("暂停"); + T.mp.hero.setSpeed(0); + Shot.setSpeed(0); + for (int i = 0; i < ets.size(); i++) { + ets.get(i).setSpeed(0); + } + T.requestFocus(); + } + if (ae.getActionCommand().equals("继续")) { + System.out.println("继续"); + T.mp.hero.setSpeed(3); + Shot.setSpeed(8); + for (int i = 0; i < ets.size(); i++) { + ets.get(i).setSpeed(2); + } + T.requestFocus();//把焦点还给JFrame + } + if (ae.getActionCommand().equals("exit")) { + //不能把下面的saveExit口令放到这里来或,会出现先后顺序的一些错误 + //口令与口令之间应该独立,不要有或,与这样的复杂逻辑结构 + System.out.println("退出游戏"); + System.exit(0); + + } + if (ae.getActionCommand().equals("Help")) { + Setting se = new Setting(hero); + } + if (ae.getActionCommand().equals("saveExit")) { + System.out.println("按下了 保存并退出 按钮"); + Saved s = new Saved(ets, hero, bricks, irons, ETS, myself); +// s.savedAll(); + s.saveDataBase(); + //退出 + System.out.println("已经退出游戏"); + System.exit(0); + } + if (ae.getActionCommand().equals("Continue")) { + //重新开启一个画板 + System.out.println("开始"); + T.count = 1; + T.mp.Continue = false; + Shot.setSpeed(8); + T.remove(T.jsp1); +// Saved s = new Saved(ets, hero, bricks, irons,ETS,myself); +// s.readAll(); + //实现一样的功能还省内存 + new Saved(ets, hero, bricks, irons, ETS, myself).readDataBase(); + t = new Thread(T); + + t.start();//将画板线程开启 + //读取 +// Saved s = new Saved(ets, hero, bricks, irons); +// s.readAll(); + Begin = new Audio("./src/RE/GameBegin.wav"); + Begin.start(); + } + } + + + public MyPanel301(TankFrame T, Hero he, Vector ets, Vector bricks, Vector irons, int[][] ETS, int[] myself) { + this.T = T; + this.hero = he; + this.ets = ets; + this.bricks = bricks; + this.irons = irons; + this.ETS = ETS; + this.myself = myself; +// System.out.println("MyPanel301的成员地址"+ets); + } +} +//class P extends JPanel{ +// Image over = null; +// public P(){ +// try { +// over = ImageIO.read(getClass().getResource("/images/Over4.jpg")); +// } catch (IOException e) { +// // TODO Auto-generated catch block +// e.printStackTrace(); +// } +// } +// public void paint(Graphics g){ +// super.paint(g); +// +// g.drawImage(over,0,0,760,560,this); +// +// } +//} diff --git a/gui/src/main/java/com/github/kuangcp/tank/v3/MyPanel302.java b/gui/src/main/java/com/github/kuangcp/tank/v3/MyPanel302.java new file mode 100644 index 00000000..e96e7448 --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/v3/MyPanel302.java @@ -0,0 +1,109 @@ +/** + * 就是一个用来显示属性的画板 + * 不停的刷新 + * JLabel 被add后 就算他发生改变,对之前已经add了的组件 不会有影响 + * 但是可以用setText来进行改变 + */ +package com.github.kuangcp.tank.v3; + + +import com.github.kuangcp.tank.v1.Demons; +import com.github.kuangcp.tank.v1.Hero; + +import javax.swing.*; +import java.awt.*; +import java.util.Vector; + +@SuppressWarnings("serial") +public class MyPanel302 extends JPanel implements Runnable { + + JLabel jl1 = null; + JLabel prizeNo = null; + MyPanel302 mp =null; + Hero hero = null; + Vector ets=null; + + + public MyPanel302(JLabel jl1, Hero hero, Vector ets, JLabel prizeNo){ + this.jl1 = jl1; + this.hero = hero; + this.ets = ets; + this.prizeNo = prizeNo; +// jl1 =new JLabel("生命值: "+Hero.Life/3); +// MyPanel302 mp = new MyPanel302(); +// mp.add(jl1,BorderLayout.SOUTH); + } + + public void paint(Graphics g){ + super.paint(g); + + int X=20,Y=20; + int x,y; +/**主坦克*/ + g.setColor(Color.yellow); + //向上 + x=X-10;y=Y-15;//把坦克中心坐标换算成系统用来画坦克的坐标 + //(并且全是取的左上角) + + g.fill3DRect(x, y, 5, 30,false); + g.fill3DRect(x+15, y, 5, 30,false); + g.fill3DRect(x+5, y+5, 10, 20,false); + g.fillOval(x+4, y+10, 10, 10); + g.drawLine(x+9, y+15, x+9, y); + +/**敌人坦克*/ + g.setColor(Color.cyan); + X = 100;Y = 20; + x=X-10;y=Y-15;//把坦克中心坐标换算成系统用来画坦克的坐标 + //(并且全是取的左上角) + + g.fill3DRect(x, y, 5, 30,false); + g.fill3DRect(x+15, y, 5, 30,false); + g.fill3DRect(x+5, y+5, 10, 20,false); + g.fillOval(x+4, y+10, 10, 10); + g.drawLine(x+9, y+15, x+9, y); + +// MyPanel3 tank = new MyPanel3();//因为原本的MyPanel是做成了线程 莫名其妙就被调用到了 +// //主坦克: +// g.setColor(Color.yellow); +// tank.drawTank(0, 0, g, 0, 0); +// //敌人坦克: +// g.setColor(Color.cyan); +// tank.drawTank(0, 40, g, 0, 1); + } + public void run() { + while(true){ + /** + * 如果是下面的方法的话,来不及设置成0 就退出线程了,所以把设置0放在了退出线程那里更好 + */ +// if(!hero.getisLive()){ +// jl1.setText("生命值: 0"); +// }else { +// jl1.setText("生命值: "+hero.getLife()); +// } + + if(hero.getisLive()){//生命值的自动更改 + jl1.setText(" :"+hero.getLife()+" : "+ets.size()); + }else{ + jl1.setText(" :0"+" : "+ets.size()); + } + prizeNo.setText("战绩:"+hero.getPrize()); +// TankGame3.mp3.add(TankGame3.jl1 ); +// System.out.println("画板执行了"); + repaint(); + + try { + Thread.sleep(20); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + //不加的话线程没有退出 +// if(hero.getLife() <= 0){ +// jl1.setText(" :0"+" : "+ets.size()); +// break;//退出线程 +// } + } + } + +} diff --git a/gui/src/main/java/com/github/kuangcp/tank/v3/MyPanel303.java b/gui/src/main/java/com/github/kuangcp/tank/v3/MyPanel303.java new file mode 100644 index 00000000..cd8423d0 --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/v3/MyPanel303.java @@ -0,0 +1,41 @@ +/** + * 绘制开始游戏的界面 + * + * 如果要实现闪烁效果 就实现接口,定义一个变量,run里面++, + * 设置变量满足什么条件再画出图片 + * + */ +package com.github.kuangcp.tank.v3; + +import javax.imageio.ImageIO; +import javax.swing.*; +import java.awt.*; +import java.io.IOException; + +public class MyPanel303 extends JPanel { + + Image First; + + public MyPanel303(){ + try { + First = ImageIO.read(getClass().getResource("/images/Tank.jpg")); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + public void paint(Graphics g){ + g.drawImage(First, 0, 0, 760,560,this); + + //直接设置字符串,不用图片 + Font my = new Font("微软雅黑", Font.BOLD,15); + g.setFont(my); +// String s = "击中40辆坦克可以获胜"; + String s = "永无终止"; + g.setColor(Color.lightGray); + g.drawString(s, 50, 500); + + + } + +} diff --git a/gui/src/main/java/com/github/kuangcp/tank/v3/PressedTwo.java b/gui/src/main/java/com/github/kuangcp/tank/v3/PressedTwo.java new file mode 100644 index 00000000..e7e7901b --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/v3/PressedTwo.java @@ -0,0 +1,90 @@ +/** + * 十分感谢网上这位的算法,使用了之后,坦克根本就不会卡,行云流水般顺畅 + * 思路如下: + * 在按下键的pressed函数中创建一个该键值的对象,加入集合中, + * 在离开键Release函数中把离开的键从集合中清除 + * 另开一个线程来一直遍历这个集合,达到同时监控两个键的的动作的效果 + * 实现结果: 一边跑,一边放子弹完全不是事儿,方向的切换也十分流畅 + */ +package com.github.kuangcp.tank.v3; + + +import com.github.kuangcp.tank.v1.Hero; + +import java.awt.event.KeyEvent; +import java.util.Vector; + +public class PressedTwo implements Runnable { + + Vector actions; + Hero hero; + MyPanel3 myPanel3; + + public PressedTwo(Vector actions, Hero hero, MyPanel3 myPanel3) { + // TODO Auto-generated constructor stub + this.actions = actions; + this.hero = hero; + this.myPanel3 = myPanel3; + } + + + @Override + public void run() { + // TODO Auto-generated method stub + while (true) { + int round = 0; + for (int i = 0; i < actions.size(); i++) { + + KE p = actions.get(i); + if (p != null) { + switch (p.Key) { + case KeyEvent.VK_A: + hero.setDirect(2); + if ((hero.getX() - 10) > 20) + hero.moveleft(); + break; + case KeyEvent.VK_D: + hero.setDirect(3); + if ((hero.getX() + 15) < 742) + hero.moveright(); + break; + case KeyEvent.VK_S: + hero.setDirect(1); + if ((hero.getY() - 15) < 515) + hero.movedown(); + break; + case KeyEvent.VK_W: + hero.setDirect(0); + if ((hero.getY() - 13) > 20) + hero.moveup(); + break; + case KeyEvent.VK_J: + if (hero.ss.size() < hero.SHOTS && hero.getisLive()) + if (round % 55 == 0) hero.shotEnemy(); + break; + default: + break; + + } + myPanel3.repaint(); + try { + Thread.sleep(40); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + + } + if (!hero.getisLive()) { + break; + } + round++; + if (round > 10000) round -= 10000; + } + + + } + +} diff --git a/gui/src/main/java/com/github/kuangcp/tank/v3/TankFrame.java b/gui/src/main/java/com/github/kuangcp/tank/v3/TankFrame.java new file mode 100644 index 00000000..81d6167f --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/v3/TankFrame.java @@ -0,0 +1,191 @@ +package com.github.kuangcp.tank.v3; + +import javax.swing.*; +import java.awt.*; + +/** + * 坦克3.0 版本: + *

+ * 1 多颗子弹连发 (限制:最多五颗子弹即五个线程) + * 2 敌人的坦克也能够发射子弹 + * 3 打到敌人坦克敌人消失 做出爆炸效果 + * 4 让敌方坦克可以自由的随机上下左右移动 + * 5 控制我方坦克和敌方坦克在规定范围内移动 + * 6 有窗口提示信息 + * 7 坦克不重叠,与障碍物不重叠 + *

+ *

+ * 8 可以把这里的窗体做成线程 但是都是静态成员,所以就算做成线程 出来的新界面也还是原来的属性(一样的界面) + * 总结:不应该随便设置静态成员的 尤其是可能会新建的成员 + * 解决方案: + * 在需要用到无法引用到的对象时,除了做成静态,还可以在当前添加这个对象的引用 + * 然后构造器 里传入所要调用的对象 就能解决问题了,多线程启动也会是初始化的状态 + * !!最好放在别的函数里,不要改动构造器,会对以前的代码有影响() + * 9 可以暂停游戏,继续游戏 + * 原来这么简单,设置动的那些Speed为0 不就是静止么。。还有方向不能动 还有焦点的自动跳转 + * 10 可以继续上局游戏开始玩 + * 11 有游戏的音效(操纵声音文件) + */ +@SuppressWarnings("serial") +public class TankFrame extends JFrame implements Runnable {//implements Runnable + + public MyPanel3 mp = null;//坦克的主画板 + public MyPanel301 mp2 = null;//放按钮的画板 + public MyPanel301 mpe1 = null;//对按钮事件监听处理的 + public MyPanel302 mp3 = null; //显示属性的画板 + public MyPanel303 Fir; + public JButton jb1 = null, jb2 = null, jb3, jb4; //按钮 + public JSplitPane jsp1, jsp2;//拆分窗格 + public JLabel jl1 = null, jl2 = null, jl3 = null, me = null, prizeNo = null; + public int count = 0; //判断是否首次运行 + + //作出我需要的菜单 + JMenuBar jmb = null; + //开始游戏 + JMenu jm1 = null; + JMenu jm2 = null; + JMenuItem jmil = null; + //退出系统 + JMenuItem jmi2 = null; + //存盘退出 + JMenuItem jmi3 = null; + JMenuItem jmi4 = null; + //帮助窗口 + JMenuItem Help = null; + + // public void inter(){ +// t.interrupt(); +// } +//最外层JFrame的构造函数 +// public TankGame3_1(){ + public void run() { + + mp = new MyPanel3(this);//坦克运动的那个画布 + mpe1 = new MyPanel301(this, mp.hero, mp.ets, mp.bricks, mp.irons, mp.ETS, mp.myself);//监听按钮事件的画布对象 + mp2 = new MyPanel301(this, mp.hero, mp.ets, mp.bricks, mp.irons, mp.ETS, mp.myself);//放按钮的画布 + + //提示信息 + jl1 = new JLabel(" : " + mp.hero.getLife() + " : " + mp.ets.size()); + prizeNo = new JLabel("已击杀 :" + mp.hero.getPrize());//战绩的标签 +// jl2 = new JLabel("提示:WSAD-上下左右 J-发子弹"); +// jl3 = new JLabel("上面是生命值和敌人数"); + me = new JLabel("Myth"); + + mp3 = new MyPanel302(jl1, mp.hero, mp.ets, prizeNo);//显示一些属性 + + if (count != 0) { + //已经成为一个线程 要启动它 + Thread t = new Thread(mp); + t.start(); + Thread t2 = new Thread(mp3); + t2.start(); + } + //创建菜单及菜单选项 + jmb = new JMenuBar(); + jm1 = new JMenu("Game(G)"); + jm2 = new JMenu("Help(H)"); + + //设置快捷方式 + jm1.setMnemonic('G'); + jm2.setMnemonic('H'); + jmil = new JMenuItem("New Game(N)"); + jmi2 = new JMenuItem("Pause"); + jmi3 = new JMenuItem("Save & Exit(C)"); + jmi4 = new JMenuItem("ContinueLast(S)"); + Help = new JMenuItem("Setting"); + + // + Help.addActionListener(mpe1); + Help.setActionCommand("Help");///////// + //注册监听 + jmi4.addActionListener(mpe1); + jmi4.setActionCommand("Continue");//////////// + //注册监听 + jmi3.addActionListener(mpe1); + jmi3.setActionCommand("saveExit");//////// + // + jmi2.addActionListener(mpe1); + jmi2.setActionCommand("暂停");///////// + jmi2.setMnemonic('E'); + //对jmil相应 + jmil.addActionListener(mpe1); + jmil.setActionCommand("开始");////// + + jm1.add(jmil); +// jm1.add(jmi2); + jm1.add(jmi3); + jm1.add(jmi4); + + jm2.add(Help); + + jmb.add(jm1); + jmb.add(jm2); + + jb3 = new JButton("暂停游戏"); + jb3.addActionListener(mpe1); //注册监听 + jb3.setActionCommand("暂停");///////// + jb4 = new JButton("继续游戏"); + jb4.addActionListener(mpe1); //注册监听 + jb4.setActionCommand("继续");///////// + + jb2 = new JButton("退出游戏"); + jb2.addActionListener(mpe1); //注册监听 + jb2.setActionCommand("结束");///////// + + if (count == 0) jb1 = new JButton("游戏开始"); + else jb1 = new JButton("重新开始"); + jb1.addActionListener(mpe1); //注册监听 + jb1.setActionCommand("开始");/////////// + + mp2.add(jb1); + mp2.add(jb2); + mp2.add(jb3); + mp2.add(jb4); + + //显示属性的窗体: + mp3.setLayout(new GridLayout(6, 1, 0, 0)); + + mp3.add(jl1);//网格布局 + mp3.add(prizeNo); +// mp3.add(jl2); +// mp3.add(jl3); + mp3.add(me); +// jl1 =new JLabel("898908098");//不会对上面造成任何影响 + + jsp2 = new JSplitPane(0, mp2, mp3);//水平 + jsp2.setDividerLocation(150); + Fir = new MyPanel303(); + if (count != 0) jsp1 = new JSplitPane(1, mp, jsp2);//垂直 + else jsp1 = new JSplitPane(1, Fir, jsp2); + jsp1.setDividerLocation(760); + + //把画板加入JFrame + this.add(jsp1, BorderLayout.CENTER); +// this.add(mp2,BorderLayout.EAST); +// this.add(mp,BorderLayout.CENTER); + + //注册键盘监听 + //下面的语句翻译为 :当前类的监听者是mp + this.addKeyListener(mp); + this.setJMenuBar(jmb); + //焦点跳转 tab切换 + this.setFocusable(getFocusTraversalKeysEnabled()); +/**JFrame 窗体的属性*/ + + + this.setTitle("Tank"); + this.setLocation(150, 60); + this.setSize(1000, 625); + this.setVisible(true); + this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + + } + + +// @Override +// public void run() { +// // TODO Auto-generated method stub +// +// } + +} \ No newline at end of file From 3c39d644b63e8e78b59c506359ce59e65364cb0a Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Mon, 6 Sep 2021 02:56:35 +0800 Subject: [PATCH 212/476] *) reverse define api --- .../github/kuangcp/tank/util/TankTool.java | 18 +- .../com/github/kuangcp/tank/v1/Demons.java | 114 +++--- .../java/com/github/kuangcp/tank/v1/Hero.java | 37 +- .../com/github/kuangcp/tank/v1/TankGame1.java | 349 +++++++++--------- .../java/com/github/kuangcp/tank/v3/KE.java | 13 +- .../com/github/kuangcp/tank/v3/MyPanel3.java | 9 +- .../github/kuangcp/tank/v3/PressedTwo.java | 107 +++--- 7 files changed, 320 insertions(+), 327 deletions(-) diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/TankTool.java b/gui/src/main/java/com/github/kuangcp/tank/util/TankTool.java index 9d061848..aedb68ca 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/util/TankTool.java +++ b/gui/src/main/java/com/github/kuangcp/tank/util/TankTool.java @@ -231,9 +231,6 @@ public static boolean Rush(Tank me, Tank you) { * 工具类 检测是否有障碍物是否可以通行 */ public static boolean hasHint(Tank t, Hinder h) { - //坦克 障碍物 - boolean flag = true; - flag = true; int hx = 20, hy = 10; switch (t.getDirect()) { case 0://上 @@ -243,8 +240,7 @@ public static boolean hasHint(Tank t, Hinder h) { && t.getY() - 15 >= h.getHy() && t.getY() - 15 <= h.getHy() + hy || t.getX() + 10 >= h.getHx() && t.getX() + 10 <= h.getHx() + hx && t.getY() - 15 >= h.getHy() && t.getY() - 15 <= h.getHy() + hy) - flag = false; - return flag; + return true; case 1://下 if (t.getX() - 10 >= h.getHx() && t.getX() - 10 <= h.getHx() + hx && t.getY() + 15 >= h.getHy() && t.getY() + 15 <= h.getHy() + hy @@ -252,8 +248,7 @@ public static boolean hasHint(Tank t, Hinder h) { && t.getY() + 15 >= h.getHy() && t.getY() + 15 <= h.getHy() + hy || t.getX() + 10 >= h.getHx() && t.getX() + 10 <= h.getHx() + hx && t.getY() + 15 >= h.getHy() && t.getY() + 15 <= h.getHy() + hy) - flag = false; - return flag; + return true; case 2: if (t.getX() - 15 >= h.getHx() && t.getX() - 15 <= h.getHx() + hx && t.getY() - 10 >= h.getHy() && t.getY() - 10 <= h.getHy() + hy @@ -261,8 +256,7 @@ public static boolean hasHint(Tank t, Hinder h) { && t.getY() >= h.getHy() && t.getY() <= h.getHy() + hy || t.getX() - 15 >= h.getHx() && t.getX() - 15 <= h.getHx() + hx && t.getY() + 10 >= h.getHy() && t.getY() + 10 <= h.getHy() + hy) - flag = false; - return flag; + return true; case 3: if (t.getX() + 15 >= h.getHx() - 2 && t.getX() + 15 <= h.getHx() + hx && t.getY() + 10 >= h.getHy() && t.getY() + 10 <= h.getHy() + hy @@ -270,11 +264,9 @@ public static boolean hasHint(Tank t, Hinder h) { && t.getY() >= h.getHy() && t.getY() <= h.getHy() + hy || t.getX() + 15 >= h.getHx() - 2 && t.getX() + 15 <= h.getHx() + hx && t.getY() - 10 >= h.getHy() && t.getY() - 10 <= h.getHy() + hy) - flag = false; - return flag; + return true; } - return flag; - + return false; } /** diff --git a/gui/src/main/java/com/github/kuangcp/tank/v1/Demons.java b/gui/src/main/java/com/github/kuangcp/tank/v1/Demons.java index eb6d3add..124dfa36 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v1/Demons.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v1/Demons.java @@ -2,53 +2,37 @@ package com.github.kuangcp.tank.v1; -import com.github.kuangcp.tank.v2.Shot; import com.github.kuangcp.tank.util.TankTool; +import com.github.kuangcp.tank.v2.Shot; +import lombok.extern.slf4j.Slf4j; import java.util.Vector; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; /** * 敌方坦克父类 * 可以继续延伸做出多样化的坦克 *

- * 还是有一个小bug 子弹集合是敌方坦克的成员属性 - * 坦克内存一回收 子弹也就没了,但是不符合实际 - * 坦克死了 子弹也应该继续飞 + * FIXME 子弹集合是敌方坦克的成员属性 坦克内存一回收 子弹也就没了, 坦克死了 子弹也应该继续飞 */ +@Slf4j public class Demons extends Tank implements Runnable { public boolean alive; public Vector ds = new Vector<>();//子弹集合 + private final ExecutorService shotExecutePool; public Vector ets; public Vector bricks; public Vector irons; public Shot s = null; + public int maxLiveShot = 16; //子弹线程存活的最大数 Hero hero; - boolean with = true; //同类之间有无重叠(true是可以往前走) - - public boolean isWith() { - return with; - } - - - public void setWith(boolean with) { - this.with = with; - } - - - public boolean isBri() { - return bri; - } - - - public void setBri(boolean bri) { - this.bri = bri; - } + boolean with = true; //同类之间有无重叠(true是可以往前走) boolean bri = true; boolean to = true; - //继承了属性,即使直接使用父类的构造器,构造器也一定要显式声明 public Demons(int x, int y, int speed, int direct) { super(x, y, speed); @@ -56,6 +40,7 @@ public Demons(int x, int y, int speed, int direct) { this.direct = direct; this.speed = speed; this.alive = true; + this.shotExecutePool = Executors.newFixedThreadPool(this.maxLiveShot); } public void SetInfo(Hero hero, Vector ets, Vector bricks, Vector irons) { @@ -65,6 +50,22 @@ public void SetInfo(Hero hero, Vector ets, Vector bricks, Vector< this.irons = irons; } + public boolean isWith() { + return with; + } + + public void setWith(boolean with) { + this.with = with; + } + + public boolean isBri() { + return bri; + } + + public void setBri(boolean bri) { + this.bri = bri; + } + /** * 视频上的思想是 在panel上写一个工具函数 形参是 子弹和坦克 * 把函数放在 Run函数内跑 @@ -81,7 +82,9 @@ public void SetInfo(Hero hero, Vector ets, Vector bricks, Vector< */ public void shotEnemy() { //判断坦克方向来 初始化子弹的起始发射位置 - + if (!this.alive || ds.size() >= this.maxLiveShot) { + return; + } switch (this.getDirect()) { case 0: {//0123 代表 上下左右 s = new Shot(this.getX() - 1, this.getY() - 15, 0); @@ -105,8 +108,7 @@ public void shotEnemy() { } } //启动子弹线程 - Thread t = new Thread(s); - t.start(); + shotExecutePool.execute(s); } // public void run (){ @@ -299,18 +301,18 @@ public boolean TouchOther() { public boolean toUp() { if (y > 30) { to = true; - for (int k = 0; k < ets.size(); k++) { - if (!TankTool.Rush(this, ets.get(k))) { + for (Demons et : ets) { + if (!TankTool.Rush(this, et)) { to = (false); break; } } - for (int i = 0; i < bricks.size(); i++) { - if (!TankTool.hasHint(this, bricks.get(i))) + for (Brick brick : bricks) { + if (TankTool.hasHint(this, brick)) to = false; } - for (int i = 0; i < irons.size(); i++) { - if (!TankTool.hasHint(this, irons.get(i))) + for (Iron iron : irons) { + if (TankTool.hasHint(this, iron)) to = false; } if (to && TankTool.Rush(this, hero)) { @@ -322,7 +324,6 @@ public boolean toUp() { } } else { return false; -// this.direct = ((int)(Math.random()*100))%4; } } else return false; @@ -332,18 +333,18 @@ public boolean toUp() { public boolean toDown() { if (y < 530) { to = true; - for (int k = 0; k < ets.size(); k++) { - if (!TankTool.Rush(this, ets.get(k))) { + for (Demons et : ets) { + if (!TankTool.Rush(this, et)) { to = (false); break; } } - for (int i = 0; i < bricks.size(); i++) { - if (!TankTool.hasHint(this, bricks.get(i))) + for (Brick brick : bricks) { + if (TankTool.hasHint(this, brick)) to = false; } - for (int i = 0; i < irons.size(); i++) { - if (!TankTool.hasHint(this, irons.get(i))) + for (Iron iron : irons) { + if (TankTool.hasHint(this, iron)) to = false; } if (to && TankTool.Rush(this, hero)) { @@ -365,18 +366,18 @@ public boolean toDown() { public boolean toLeft() { if (x > 30) { to = true; - for (int k = 0; k < ets.size(); k++) { - if (!TankTool.Rush(this, ets.get(k))) { - to = (false); + for (Demons et : ets) { + if (!TankTool.Rush(this, et)) { + to = false; break; } } - for (int i = 0; i < bricks.size(); i++) { - if (!TankTool.hasHint(this, bricks.get(i))) + for (Brick brick : bricks) { + if (TankTool.hasHint(this, brick)) to = false; } - for (int i = 0; i < irons.size(); i++) { - if (!TankTool.hasHint(this, irons.get(i))) + for (Iron iron : irons) { + if (TankTool.hasHint(this, iron)) to = false; } if (to && TankTool.Rush(this, hero)) { @@ -384,11 +385,10 @@ public boolean toLeft() { try { Thread.sleep(100); } catch (Exception e) { - e.printStackTrace(); + log.error("", e); } } else { return true; -// this.direct = ((int)(Math.random()*100))%4; } } else return false; @@ -398,18 +398,18 @@ public boolean toLeft() { public boolean toRight() { if (x < 710) { to = true; - for (int k = 0; k < ets.size(); k++) { - if (!TankTool.Rush(this, ets.get(k))) { + for (Demons et : ets) { + if (!TankTool.Rush(this, et)) { to = (false); break; } } - for (int i = 0; i < bricks.size(); i++) { - if (!TankTool.hasHint(this, bricks.get(i))) + for (Brick brick : bricks) { + if (TankTool.hasHint(this, brick)) to = false; } - for (int i = 0; i < irons.size(); i++) { - if (!TankTool.hasHint(this, irons.get(i))) + for (Iron iron : irons) { + if (TankTool.hasHint(this, iron)) to = false; } if (to && TankTool.Rush(this, hero)) { @@ -538,10 +538,8 @@ public void run() { break; default: break; - } - //判断坦克是否死亡 if (!this.getisLive()) { //让坦克退出while即退出线程 diff --git a/gui/src/main/java/com/github/kuangcp/tank/v1/Hero.java b/gui/src/main/java/com/github/kuangcp/tank/v1/Hero.java index a13bc212..39c6654c 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v1/Hero.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v1/Hero.java @@ -1,16 +1,19 @@ package com.github.kuangcp.tank.v1; -import com.github.kuangcp.tank.v2.Shot; import com.github.kuangcp.tank.util.TankTool; +import com.github.kuangcp.tank.v2.Shot; import java.awt.*; import java.util.Vector; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; public class Hero extends Tank { //子弹集合 public Vector ss = new Vector<>(); + private final ExecutorService shotExecutePool; public Shot s = null;//子弹 public Graphics g; //画笔不可少 @@ -21,7 +24,7 @@ public class Hero extends Tank { public Vector irons; public boolean to = true; static int prize = 0;//击敌个数 - public int SHOTS = 8;//主坦克子弹线程存活的最大数 + public int maxLiveShot = 8;//主坦克子弹线程存活的最大数 int speed = 3; static int Life = 10; @@ -64,15 +67,16 @@ public Hero(int x, int y, int speed, Vector ets, Vector bricks, V this.ets = ets; this.bricks = bricks; this.irons = irons; + + this.shotExecutePool = Executors.newFixedThreadPool(maxLiveShot); } - /** - * 被重新封装 - */ - // 视频的样板开火函数 public void shotEnemy() { - //判断坦克方向来 初始化子弹的起始发射位置 + if (this.ss.size() >= this.maxLiveShot || !this.getisLive()) { + return; + } + //判断坦克方向来 初始化子弹的起始发射位置 switch (this.getDirect()) { case 0://0123 代表 上下左右 s = new Shot(this.getX() - 1, this.getY() - 15, 0); @@ -92,8 +96,7 @@ public void shotEnemy() { break; } //启动子弹线程 - Thread t = new Thread(s); - t.start(); + shotExecutePool.execute(s); } //移动的函数 @@ -106,11 +109,11 @@ public void moveup() { } //检测障碍物 for (Brick brick : bricks) { - if (!TankTool.hasHint(this, brick)) + if (TankTool.hasHint(this, brick)) to = false; } for (Iron iron : irons) { - if (!TankTool.hasHint(this, iron)) + if (TankTool.hasHint(this, iron)) to = false; } if (to) @@ -123,11 +126,11 @@ public void movedown() { if (!TankTool.Rush(this, et)) to = false; for (Brick brick : bricks) { - if (!TankTool.hasHint(this, brick)) + if (TankTool.hasHint(this, brick)) to = false; } for (Iron iron : irons) { - if (!TankTool.hasHint(this, iron)) + if (TankTool.hasHint(this, iron)) to = false; } if (to) @@ -140,11 +143,11 @@ public void moveleft() { if (!TankTool.Rush(this, et)) to = false; for (Brick brick : bricks) { - if (!TankTool.hasHint(this, brick)) + if (TankTool.hasHint(this, brick)) to = false; } for (Iron iron : irons) { - if (!TankTool.hasHint(this, iron)) + if (TankTool.hasHint(this, iron)) to = false; } if (to) @@ -157,11 +160,11 @@ public void moveright() { if (!TankTool.Rush(this, et)) to = false; for (Brick brick : bricks) { - if (!TankTool.hasHint(this, brick)) + if (TankTool.hasHint(this, brick)) to = false; } for (Iron iron : irons) { - if (!TankTool.hasHint(this, iron)) + if (TankTool.hasHint(this, iron)) to = false; } if (to) diff --git a/gui/src/main/java/com/github/kuangcp/tank/v1/TankGame1.java b/gui/src/main/java/com/github/kuangcp/tank/v1/TankGame1.java index b936490a..9a179665 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v1/TankGame1.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v1/TankGame1.java @@ -14,52 +14,53 @@ @SuppressWarnings("serial") public class TankGame1 extends JFrame { - - MyPanel mp = null; - @SuppressWarnings("unused") - public static void main(String[] args) { - // TODO Auto-generated method stub - TankGame1 Tank = new TankGame1(); - } - - //最外层JFrame的构造函数 - public TankGame1 (){ - - - mp = new MyPanel(); - - this.add(mp); - //注册键盘监听 - //下面的语句翻译为 :当前类的监;听者是mp - this.addKeyListener(mp); - - this.setLocation(900,200); - this.setSize(500,400); - this.setVisible(true); - this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - } + + MyPanel mp = null; + + @SuppressWarnings("unused") + public static void main(String[] args) { + // TODO Auto-generated method stub + TankGame1 Tank = new TankGame1(); + } + + //最外层JFrame的构造函数 + public TankGame1() { + + + mp = new MyPanel(); + + this.add(mp); + //注册键盘监听 + //下面的语句翻译为 :当前类的监;听者是mp + this.addKeyListener(mp); + + this.setLocation(900, 200); + this.setSize(500, 400); + this.setVisible(true); + this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + } } @SuppressWarnings("serial") ///////////////////////////////////////////////////////////////////////////// class MyPanel extends JPanel implements KeyListener { - - //定义我的坦克 - Hero hero = null; - int enSize = 3; //敌人的数量 - //定义泛型的集合类 - Vector ets = new Vector(); - Vector bricks = new Vector(); - - //画板的构造函数 放图形的对象 - public MyPanel(){ - //多个敌方坦克应该用集合类,而且要考虑线程安全所以不能用ArrayList只能用Vector - //实例化英雄坦克 + + //定义我的坦克 + Hero hero = null; + int enSize = 3; //敌人的数量 + //定义泛型的集合类 + Vector ets = new Vector(); + Vector bricks = new Vector(); + + //画板的构造函数 放图形的对象 + public MyPanel() { + //多个敌方坦克应该用集合类,而且要考虑线程安全所以不能用ArrayList只能用Vector + //实例化英雄坦克 // hero = new Hero(10,200,10,ets,bricks);//坐标和步长 - - - //初始化敌人的坦克 + + + //初始化敌人的坦克 /*for (int i=0;i0) - hero.moveleft(); - - } - if (e.getKeyCode()== KeyEvent.VK_D){ - // System.out.print("右"); - hero.setDirect(3); - if ((hero.getX()+15)<405) - hero.moveright(); - } - - if (e.getKeyCode()== KeyEvent.VK_W){ - // System.out.print("上"); - hero.setDirect(0); - if ((hero.getY()-13)>0) - hero.moveup(); - - } - if (e.getKeyCode()== KeyEvent.VK_S){ - // System.out.print("下"); - hero.setDirect(1); - if ((hero.getY()-15)<275) - hero.movedown(); - - }this.repaint(); - - //必须重新绘制窗口,不然上面的方法不能视觉上动起来 - this.repaint(); - - } + } - public void keyReleased(KeyEvent arg0) { - // TODO Auto-generated method stub - - } + //重写paint + public void paint(Graphics g) { - public void keyTyped(KeyEvent arg0) { - // TODO Auto-generated method stub - - } + super.paint(g); + g.fillRect(0, 0, 500, 400); + + //调用函数绘画出主坦克 + this.drawTank(hero.getX(), hero.getY(), g, hero.getDirect(), hero.getType()); + //画出子弹 + if (hero.s != null) { + g.draw3DRect(hero.s.sx, hero.s.sy, 1, 1, false); + } + + //画出敌人坦克 + for (Demons s : ets) { + this.drawTank(s.getX(), s.getY(), g, s.getDirect(), s.getType()); + } + + // 画出砖块 + g.setColor(Color.yellow); + g.drawRect(0, 0, 400, 300); + for (int j = 0; j < 20; j++) { + g.draw3DRect(60 + 8 * j, 50, 5, 10, false); + } + + } + + //画出坦克的函数 XY是坦克中心的坐标,不是画图参照点 + public void drawTank(int X, int Y, Graphics g, int direct, int type) { + //判断坦克类型 + int x, y;//系统画图函数的参照点 + switch (type) { + case 1: + g.setColor(Color.cyan); + break; + case 0: + g.setColor(Color.yellow); + break; + } + + //判断方向 + switch (direct) { + + case 0: {//向上VK_UP + x = X - 10; + y = Y - 15; + //1.左边的矩形 + g.fill3DRect(x, y, 5, 30, false); + //2.画出右边矩形 + g.fill3DRect(x + 15, y, 5, 30, false); + //3.画出中间矩形 + g.fill3DRect(x + 5, y + 5, 10, 20, false); + //4.画出圆形 + g.fillOval(x + 4, y + 10, 10, 10); + //5.画出线 + g.drawLine(x + 9, y + 15, x + 9, y); + break; + } + case 1: {//向下 VK_DOWN + x = X - 10; + y = Y - 15; + g.fill3DRect(x, y, 5, 30, false); + g.fill3DRect(x + 15, y, 5, 30, false); + g.fill3DRect(x + 5, y + 5, 10, 20, false); + g.fillOval(x + 4, y + 10, 10, 10); + g.drawLine(x + 9, y + 15, x + 9, y + 30); + break; + } + case 2: {//向左 VK_LEFT + x = X - 15; + y = Y - 10; + g.fill3DRect(x, y, 30, 5, false); + g.fill3DRect(x, y + 15, 30, 5, false); + g.fill3DRect(x + 5, y + 5, 20, 10, false); + g.fillOval(x + 10, y + 4, 10, 10); + g.drawLine(x + 15, y + 9, x, y + 9); + break; + } + case 3: {//向右VK_RIGHT + x = X - 15; + y = Y - 10; + g.fill3DRect(x, y, 30, 5, false); + g.fill3DRect(x, y + 15, 30, 5, false); + g.fill3DRect(x + 5, y + 5, 20, 10, false); + g.fillOval(x + 10, y + 4, 10, 10); + g.drawLine(x + 15, y + 9, x + 30, y + 9); + break; + } + } + } + + //当按下键盘上的键时监听者的处理函数 + public void keyPressed(KeyEvent e) { + //加了if条件后 实现了墙的限制(如果是游戏中的道具,该怎么办) + if (e.getKeyCode() == KeyEvent.VK_A) { + // System.out.print("左"); + hero.setDirect(2); + if ((hero.getX() - 10) > 0) + hero.moveleft(); + + } + if (e.getKeyCode() == KeyEvent.VK_D) { + // System.out.print("右"); + hero.setDirect(3); + if ((hero.getX() + 15) < 405) + hero.moveright(); + } + + if (e.getKeyCode() == KeyEvent.VK_W) { + // System.out.print("上"); + hero.setDirect(0); + if ((hero.getY() - 13) > 0) + hero.moveup(); + + } + if (e.getKeyCode() == KeyEvent.VK_S) { + // System.out.print("下"); + hero.setDirect(1); + if ((hero.getY() - 15) < 275) + hero.movedown(); + + } + this.repaint(); + + //必须重新绘制窗口,不然上面的方法不能视觉上动起来 + this.repaint(); + + } + + public void keyReleased(KeyEvent arg0) { + // TODO Auto-generated method stub + + } + + public void keyTyped(KeyEvent arg0) { + // TODO Auto-generated method stub + + } } /* //为了能一边走一边发子弹而设计的专门处理按下j键子弹发射的类 diff --git a/gui/src/main/java/com/github/kuangcp/tank/v3/KE.java b/gui/src/main/java/com/github/kuangcp/tank/v3/KE.java index 5f4d4dc0..4e4dbc86 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v3/KE.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v3/KE.java @@ -1,10 +1,13 @@ package com.github.kuangcp.tank.v3; public class KE { - int Key; - public KE(){} - public KE(int k){ - Key = k; - } + int keyCode; + + public KE() { + } + + public KE(int k) { + keyCode = k; + } } diff --git a/gui/src/main/java/com/github/kuangcp/tank/v3/MyPanel3.java b/gui/src/main/java/com/github/kuangcp/tank/v3/MyPanel3.java index e10405da..2a77987b 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v3/MyPanel3.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v3/MyPanel3.java @@ -44,7 +44,8 @@ public class MyPanel3 extends JPanel implements KeyListener, Runnable { public Vector bricks = new Vector(); public Vector irons = new Vector(); //所有按下键的code集合 - public Vector actions = new Vector(); + // fixme 并发修改问题 + public Vector actions = new Vector<>(); public static int[][] ETS = new int[12][2]; public static int[] myself = new int[6]; @@ -575,22 +576,18 @@ public void keyPressed(KeyEvent e) { jj = false; } - /**自己做的开火函数 //hero.drawS(hero.getX(), hero.getY(), hero.getDirect(), hero.g); */ - } //必须重新绘制窗口,不然上面的方法不能视觉上动起来 this.repaint(); count = 0; - } //键离开了就把按键从集合中移除 @Override public void keyReleased(KeyEvent re) { - // TODO Auto-generated method stub switch (re.getKeyCode()) { case KeyEvent.VK_A: actions.remove(a); @@ -620,8 +617,6 @@ public void keyReleased(KeyEvent re) { @Override public void keyTyped(KeyEvent arg0) { - // TODO Auto-generated method stub - } /** diff --git a/gui/src/main/java/com/github/kuangcp/tank/v3/PressedTwo.java b/gui/src/main/java/com/github/kuangcp/tank/v3/PressedTwo.java index e7e7901b..ba1c1d89 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v3/PressedTwo.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v3/PressedTwo.java @@ -1,19 +1,22 @@ -/** - * 十分感谢网上这位的算法,使用了之后,坦克根本就不会卡,行云流水般顺畅 - * 思路如下: - * 在按下键的pressed函数中创建一个该键值的对象,加入集合中, - * 在离开键Release函数中把离开的键从集合中清除 - * 另开一个线程来一直遍历这个集合,达到同时监控两个键的的动作的效果 - * 实现结果: 一边跑,一边放子弹完全不是事儿,方向的切换也十分流畅 - */ package com.github.kuangcp.tank.v3; import com.github.kuangcp.tank.v1.Hero; +import lombok.extern.slf4j.Slf4j; import java.awt.event.KeyEvent; +import java.util.Objects; import java.util.Vector; +/** + * 十分感谢网上这位的算法,使用了之后,坦克根本就不会卡,行云流水般顺畅 + * 思路如下: + * 在按下键的pressed函数中创建一个该键值的对象,加入集合中, + * 在离开键Release函数中把离开的键从集合中清除 + * 另开一个线程来一直遍历这个集合,达到同时监控两个键的的动作的效果 + * 实现结果: 一边跑,一边放子弹完全不是事儿,方向的切换也十分流畅 + */ +@Slf4j public class PressedTwo implements Runnable { Vector actions; @@ -21,70 +24,62 @@ public class PressedTwo implements Runnable { MyPanel3 myPanel3; public PressedTwo(Vector actions, Hero hero, MyPanel3 myPanel3) { - // TODO Auto-generated constructor stub this.actions = actions; this.hero = hero; this.myPanel3 = myPanel3; } - @Override public void run() { - // TODO Auto-generated method stub while (true) { int round = 0; - for (int i = 0; i < actions.size(); i++) { - - KE p = actions.get(i); - if (p != null) { - switch (p.Key) { - case KeyEvent.VK_A: - hero.setDirect(2); - if ((hero.getX() - 10) > 20) - hero.moveleft(); - break; - case KeyEvent.VK_D: - hero.setDirect(3); - if ((hero.getX() + 15) < 742) - hero.moveright(); - break; - case KeyEvent.VK_S: - hero.setDirect(1); - if ((hero.getY() - 15) < 515) - hero.movedown(); - break; - case KeyEvent.VK_W: - hero.setDirect(0); - if ((hero.getY() - 13) > 20) - hero.moveup(); - break; - case KeyEvent.VK_J: - if (hero.ss.size() < hero.SHOTS && hero.getisLive()) - if (round % 55 == 0) hero.shotEnemy(); - break; - default: - break; - - } - myPanel3.repaint(); - try { - Thread.sleep(40); - } catch (InterruptedException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } + for (KE p : actions) { + if (Objects.isNull(p)) { + continue; } - + switch (p.keyCode) { + case KeyEvent.VK_A: + hero.setDirect(2); + if ((hero.getX() - 10) > 20) + hero.moveleft(); + break; + case KeyEvent.VK_D: + hero.setDirect(3); + if ((hero.getX() + 15) < 742) + hero.moveright(); + break; + case KeyEvent.VK_S: + hero.setDirect(1); + if ((hero.getY() - 15) < 515) + hero.movedown(); + break; + case KeyEvent.VK_W: + hero.setDirect(0); + if ((hero.getY() - 13) > 20) + hero.moveup(); + break; + case KeyEvent.VK_J: + hero.shotEnemy(); + break; + default: + break; + } + myPanel3.repaint(); + try { + Thread.sleep(40); + } catch (InterruptedException e) { + log.error("", e); + } } + if (!hero.getisLive()) { break; } round++; - if (round > 10000) round -= 10000; + if (round > 10000) { + round -= 10000; + } } - - } - } From ebf762139a4f0b2e6061aa47c05880aaf8a978d7 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Sat, 11 Sep 2021 21:25:04 +0800 Subject: [PATCH 213/476] x) thread leak --- .../src/main/java/thread/tryone}/Thread1.java | 2 +- .../src/main/java/thread/tryone}/Thread2.java | 2 +- .../src/main/java/thread/tryone}/Thread3.java | 2 +- .../src/main/java/thread/tryone}/Thread4.java | 2 +- gui/Readme.md | 5 + gui/build.gradle | 31 +- .../github/kuangcp/jigsaw/ButtonListener.java | 14 +- .../com/github/kuangcp/jigsaw/MainFrame.java | 10 +- .../kuangcp/tank/constant/DirectType.java | 11 + .../kuangcp/tank/constant/StageCommand.java | 9 + .../com/github/kuangcp/tank/util/Audio.java | 18 +- .../github/kuangcp/tank/util/ExecutePool.java | 21 + .../com/github/kuangcp/tank/util/Saved.java | 20 +- .../github/kuangcp/tank/util/TankTool.java | 105 +---- .../tank/v1/{Demons.java => EnemyTank.java} | 143 ++++-- .../java/com/github/kuangcp/tank/v1/Hero.java | 106 +++-- .../java/com/github/kuangcp/tank/v1/Tank.java | 16 +- .../com/github/kuangcp/tank/v1/TankGame1.java | 4 +- .../com/github/kuangcp/tank/v2/MyPanel.java | 6 +- .../java/com/github/kuangcp/tank/v2/Shot.java | 2 + .../com/github/kuangcp/tank/v2/TankGame2.java | 67 ++- .../com/github/kuangcp/tank/v3/GameMain.java | 10 - .../java/com/github/kuangcp/tank/v3/KE.java | 13 - .../kuangcp/tank/v3/ListenEventGroup.java | 94 ++++ .../github/kuangcp/tank/v3/MainTankGame.java | 13 + .../com/github/kuangcp/tank/v3/MyPanel3.java | 416 +++++++----------- .../github/kuangcp/tank/v3/MyPanel302.java | 150 ++++--- .../github/kuangcp/tank/v3/PressedTwo.java | 104 ++--- ...{MyPanel301.java => StageActionPanel.java} | 124 +++--- .../com/github/kuangcp/tank/v3/TankFrame.java | 60 ++- .../kuangcp/tank/v3/resource/AvatarMgr.java | 33 ++ .../kuangcp/tank/v3/{ => resource}/Bomb.java | 9 +- .../kuangcp/tank/v3/resource/BombMgr.java | 131 ++++++ .../tank/v3/thread/ExitFlagRunnable.java | 9 + .../kuangcp/future/CompletableFutureTest.java | 149 ++++--- 35 files changed, 1083 insertions(+), 828 deletions(-) rename {gui/src/main/java/com/github/kuangcp/tank/v2 => concurrency/src/main/java/thread/tryone}/Thread1.java (97%) rename {gui/src/main/java/com/github/kuangcp/tank/v2 => concurrency/src/main/java/thread/tryone}/Thread2.java (96%) rename {gui/src/main/java/com/github/kuangcp/tank/v2 => concurrency/src/main/java/thread/tryone}/Thread3.java (95%) rename {gui/src/main/java/com/github/kuangcp/tank/v2 => concurrency/src/main/java/thread/tryone}/Thread4.java (98%) create mode 100644 gui/src/main/java/com/github/kuangcp/tank/constant/DirectType.java create mode 100644 gui/src/main/java/com/github/kuangcp/tank/constant/StageCommand.java create mode 100644 gui/src/main/java/com/github/kuangcp/tank/util/ExecutePool.java rename gui/src/main/java/com/github/kuangcp/tank/v1/{Demons.java => EnemyTank.java} (82%) delete mode 100644 gui/src/main/java/com/github/kuangcp/tank/v3/GameMain.java delete mode 100644 gui/src/main/java/com/github/kuangcp/tank/v3/KE.java create mode 100644 gui/src/main/java/com/github/kuangcp/tank/v3/ListenEventGroup.java create mode 100644 gui/src/main/java/com/github/kuangcp/tank/v3/MainTankGame.java rename gui/src/main/java/com/github/kuangcp/tank/v3/{MyPanel301.java => StageActionPanel.java} (56%) create mode 100644 gui/src/main/java/com/github/kuangcp/tank/v3/resource/AvatarMgr.java rename gui/src/main/java/com/github/kuangcp/tank/v3/{ => resource}/Bomb.java (83%) create mode 100644 gui/src/main/java/com/github/kuangcp/tank/v3/resource/BombMgr.java create mode 100644 gui/src/main/java/com/github/kuangcp/tank/v3/thread/ExitFlagRunnable.java diff --git a/gui/src/main/java/com/github/kuangcp/tank/v2/Thread1.java b/concurrency/src/main/java/thread/tryone/Thread1.java similarity index 97% rename from gui/src/main/java/com/github/kuangcp/tank/v2/Thread1.java rename to concurrency/src/main/java/thread/tryone/Thread1.java index 31c5a837..49a3068f 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v2/Thread1.java +++ b/concurrency/src/main/java/thread/tryone/Thread1.java @@ -4,7 +4,7 @@ * 实现Runnable接口 来开发线程 * */ -package com.github.kuangcp.tank.v2; +package thread.tryone; public class Thread1 { diff --git a/gui/src/main/java/com/github/kuangcp/tank/v2/Thread2.java b/concurrency/src/main/java/thread/tryone/Thread2.java similarity index 96% rename from gui/src/main/java/com/github/kuangcp/tank/v2/Thread2.java rename to concurrency/src/main/java/thread/tryone/Thread2.java index 65d4ad78..a848aabe 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v2/Thread2.java +++ b/concurrency/src/main/java/thread/tryone/Thread2.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.tank.v2; +package thread.tryone; /** * 两个线程同时运作 * @author lenovo diff --git a/gui/src/main/java/com/github/kuangcp/tank/v2/Thread3.java b/concurrency/src/main/java/thread/tryone/Thread3.java similarity index 95% rename from gui/src/main/java/com/github/kuangcp/tank/v2/Thread3.java rename to concurrency/src/main/java/thread/tryone/Thread3.java index b4135f0a..dd11aa51 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v2/Thread3.java +++ b/concurrency/src/main/java/thread/tryone/Thread3.java @@ -4,7 +4,7 @@ * */ -package com.github.kuangcp.tank.v2; +package thread.tryone; public class Thread3 { diff --git a/gui/src/main/java/com/github/kuangcp/tank/v2/Thread4.java b/concurrency/src/main/java/thread/tryone/Thread4.java similarity index 98% rename from gui/src/main/java/com/github/kuangcp/tank/v2/Thread4.java rename to concurrency/src/main/java/thread/tryone/Thread4.java index 7f1ba281..cae3aa92 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v2/Thread4.java +++ b/concurrency/src/main/java/thread/tryone/Thread4.java @@ -5,7 +5,7 @@ * 就会容易发生死锁 要尽量避免 * */ -package com.github.kuangcp.tank.v2; +package thread.tryone; public class Thread4 { diff --git a/gui/Readme.md b/gui/Readme.md index 31c083de..66fa8d5d 100644 --- a/gui/Readme.md +++ b/gui/Readme.md @@ -1,5 +1,10 @@ # GUI +gradle tank 构建坦克游戏 +gradle note 构建记事本 +gradle virus 构建病毒模拟 +gradle calc 构建计算器 + ## GTK > [java-gnome](http://java-gnome.sourceforge.net/README.html) diff --git a/gui/build.gradle b/gui/build.gradle index 90a03422..2b3c7453 100644 --- a/gui/build.gradle +++ b/gui/build.gradle @@ -2,11 +2,30 @@ plugins { id 'application' } -application { -// mainClass = 'com.github.kuangcp.notepad.Note' - mainClass = 'com.github.kuangcp.tank.v3.GameMain' -} - dependencies { implementation libs['kcp-core'] -} \ No newline at end of file +} + +tasks.register('tank') { + dependsOn build + application.mainClass = 'com.github.kuangcp.tank.v3.MainTankGame' + println "Build Tank Game ..." +} + +tasks.register('note') { + dependsOn build + application.mainClass = 'com.github.kuangcp.notepad.Note' + println "Build Note ..." +} + +tasks.register('virus') { + dependsOn build + application.mainClass = 'com.github.kuangcp.virusbroadcast.VirusBroadcast' + println "Build Virus Demo ..." +} + +tasks.register('calc') { + dependsOn build + application.mainClass = 'com.github.kuangcp.caculator.Calculator' + println "Build Calculator ..." +} diff --git a/gui/src/main/java/com/github/kuangcp/jigsaw/ButtonListener.java b/gui/src/main/java/com/github/kuangcp/jigsaw/ButtonListener.java index ce565ce8..e451e019 100644 --- a/gui/src/main/java/com/github/kuangcp/jigsaw/ButtonListener.java +++ b/gui/src/main/java/com/github/kuangcp/jigsaw/ButtonListener.java @@ -1,8 +1,10 @@ package com.github.kuangcp.jigsaw; +import com.github.kuangcp.tank.constant.StageCommand; +import lombok.extern.slf4j.Slf4j; + import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import lombok.extern.slf4j.Slf4j; /** * created by https://gitee.com/gin9 @@ -12,11 +14,11 @@ @Slf4j public class ButtonListener implements ActionListener { - public void actionPerformed(ActionEvent e) { - log.debug("开始监听按钮的点击"); - if (e.getActionCommand().equals("开始")) { - log.debug("开始游戏"); + public void actionPerformed(ActionEvent e) { + log.debug("开始监听按钮的点击"); + if (e.getActionCommand().equals(StageCommand.START)) { + log.debug("开始游戏"); + } } - } } diff --git a/gui/src/main/java/com/github/kuangcp/jigsaw/MainFrame.java b/gui/src/main/java/com/github/kuangcp/jigsaw/MainFrame.java index 5f7175ec..424f69ac 100644 --- a/gui/src/main/java/com/github/kuangcp/jigsaw/MainFrame.java +++ b/gui/src/main/java/com/github/kuangcp/jigsaw/MainFrame.java @@ -1,9 +1,9 @@ package com.github.kuangcp.jigsaw; -import java.awt.BorderLayout; -import java.awt.HeadlessException; -import javax.swing.JButton; -import javax.swing.JFrame; +import com.github.kuangcp.tank.constant.StageCommand; + +import javax.swing.*; +import java.awt.*; /** * created by https://gitee.com/gin9 @@ -21,7 +21,7 @@ private MainFrame() throws HeadlessException { ButtonListener button = new ButtonListener(); JButton startBtn = new JButton("开始游戏"); startBtn.addActionListener(button);//注册监听 - startBtn.setActionCommand("开始");//指定特定的命令 + startBtn.setActionCommand(StageCommand.START);//指定特定的命令 MainPanel btnPanel = new MainPanel(); btnPanel.add(startBtn, BorderLayout.SOUTH); diff --git a/gui/src/main/java/com/github/kuangcp/tank/constant/DirectType.java b/gui/src/main/java/com/github/kuangcp/tank/constant/DirectType.java new file mode 100644 index 00000000..8032fe9f --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/constant/DirectType.java @@ -0,0 +1,11 @@ +package com.github.kuangcp.tank.constant; + +/** + * @author https://github.com/kuangcp on 2021-09-06 03:08 + */ +public interface DirectType { + int UP = 0; + int DOWN = 1; + int LEFT = 2; + int RIGHT = 3; +} diff --git a/gui/src/main/java/com/github/kuangcp/tank/constant/StageCommand.java b/gui/src/main/java/com/github/kuangcp/tank/constant/StageCommand.java new file mode 100644 index 00000000..eb189fed --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/constant/StageCommand.java @@ -0,0 +1,9 @@ +package com.github.kuangcp.tank.constant; + +/** + * @author https://github.com/kuangcp on 2021-09-11 17:45 + */ +public interface StageCommand { + + String START = "start"; +} diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/Audio.java b/gui/src/main/java/com/github/kuangcp/tank/util/Audio.java index 8845d8c8..ca385af9 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/util/Audio.java +++ b/gui/src/main/java/com/github/kuangcp/tank/util/Audio.java @@ -1,5 +1,7 @@ package com.github.kuangcp.tank.util; +import lombok.extern.slf4j.Slf4j; + import javax.sound.sampled.AudioFormat; import javax.sound.sampled.AudioInputStream; import javax.sound.sampled.AudioSystem; @@ -12,6 +14,7 @@ * 类初始化时 把路径传入 播放声音的类 * 只能播放无损(无压缩的音乐文件)即WAV不能播放MP3 */ +@Slf4j public class Audio extends Thread { private String filename; @@ -32,21 +35,20 @@ public Audio(String wavfile) { } public void run() { - File soundFile = new File(filename); - AudioInputStream audioInputStream = null; + AudioInputStream audioInputStream; try { audioInputStream = AudioSystem.getAudioInputStream(soundFile); - } catch (Exception e1) { - e1.printStackTrace(); + } catch (Exception e) { + log.error("", e); return; } AudioFormat format = audioInputStream.getFormat(); - SourceDataLine auline = null; DataLine.Info info = new DataLine.Info(SourceDataLine.class, format); + SourceDataLine auline; try { auline = (SourceDataLine) AudioSystem.getLine(info); auline.open(format); @@ -67,14 +69,10 @@ public void run() { auline.write(abData, 0, nBytesRead); } } catch (IOException e) { - e.printStackTrace(); - return; + log.error("", e); } finally { auline.drain(); auline.close(); } - } - - } \ No newline at end of file diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/ExecutePool.java b/gui/src/main/java/com/github/kuangcp/tank/util/ExecutePool.java new file mode 100644 index 00000000..c7eeab85 --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/util/ExecutePool.java @@ -0,0 +1,21 @@ +package com.github.kuangcp.tank.util; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; + +/** + * @author https://github.com/kuangcp on 2021-09-11 17:32 + */ +public class ExecutePool { + + public static final AtomicLong totalCounter = new AtomicLong(); + + public static ExecutorService buildFixedPool(String prefix, int coreSize) { + return new ThreadPoolExecutor(coreSize, coreSize, 0L, TimeUnit.MILLISECONDS, + new LinkedBlockingQueue<>(), + runnable -> new Thread(runnable, prefix + "-" + totalCounter.addAndGet(1))); + } +} diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/Saved.java b/gui/src/main/java/com/github/kuangcp/tank/util/Saved.java index 7d17783b..fdeedfe8 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/util/Saved.java +++ b/gui/src/main/java/com/github/kuangcp/tank/util/Saved.java @@ -1,9 +1,7 @@ - package com.github.kuangcp.tank.util; - import com.github.kuangcp.tank.v1.Brick; -import com.github.kuangcp.tank.v1.Demons; +import com.github.kuangcp.tank.v1.EnemyTank; import com.github.kuangcp.tank.v1.Hero; import com.github.kuangcp.tank.v1.Iron; @@ -32,14 +30,14 @@ * 用数据库实现了续上局及其存盘退出,比文件的操作简单多了。。。 */ public class Saved { - private Vector ets; + private Vector ets; private Hero hero; private Vector irons; private Vector bricks; int[][] ETS; int[] myself; - public Saved(Vector ets, Hero hero, Vector bricks, Vector irons, int[][] ETS, int[] myself) { + public Saved(Vector ets, Hero hero, Vector bricks, Vector irons, int[][] ETS, int[] myself) { this.ets = ets; this.hero = hero; this.bricks = bricks; @@ -50,12 +48,11 @@ public Saved(Vector ets, Hero hero, Vector bricks, Vector i //保存一切的函数 public void savedAll() { - BufferedWriter bw = null; OutputStream out = null; OutputStreamWriter os = null; - Demons s; + EnemyTank s; Brick b; Iron r; @@ -72,7 +69,7 @@ public void savedAll() { bw.write("< \r\n"); // bw.write("写进去了\r\n"); for (int i = 0; i < ets.size(); i++) { - if ((s = ets.get(i)) != null && s.getisLive()) { + if ((s = ets.get(i)) != null && s.isAlive()) { String qi = "", qx = "", qy = "";//i-2,x-3,y-3 @@ -255,8 +252,6 @@ public int StoInt(String s) { // public static void main(String []e){ public void readDataBase() { - - PreparedStatement ps = null; Connection cn = null; ResultSet rs = null; @@ -337,7 +332,7 @@ public void saveDataBase() { //2 得到连接 cn = DriverManager.getConnection("jdbc:mysql://localhost:3306/Tank?user=myth&password=ad&userUnicode=true&characterEncoding=UTF8"); //创建一个preparedStatement对象用于发送 - Demons s = null; + EnemyTank s = null; //在写入数据之前就要把表中数据全部删除,不然数据就溢出了 ps = cn.prepareStatement("delete from demons where x>0"); int deletes = ps.executeUpdate(); @@ -351,7 +346,7 @@ public void saveDataBase() { if (ps != null) ps.close(); for (int i = 0; i < ets.size(); i++) { - if ((s = ets.get(i)) != null && s.getisLive()) { + if ((s = ets.get(i)) != null && s.isAlive()) { ps = cn.prepareStatement("insert into demons values(?,?)"); ps.setInt(1, s.getX()); ps.setInt(2, s.getY()); @@ -396,6 +391,7 @@ public void saveDataBase() { /** * (和src同级)相对路径下的 字节转字符文件 读取操作从同等级的包下读取文件 * 从同等级的包下读取文件 + * 从同等级的包下读取文件 */ // public void savedAll(){ /*public static void main(String []d){ diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/TankTool.java b/gui/src/main/java/com/github/kuangcp/tank/util/TankTool.java index aedb68ca..5c9d8520 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/util/TankTool.java +++ b/gui/src/main/java/com/github/kuangcp/tank/util/TankTool.java @@ -1,92 +1,21 @@ package com.github.kuangcp.tank.util; +import com.github.kuangcp.tank.constant.DirectType; import com.github.kuangcp.tank.v1.Brick; -import com.github.kuangcp.tank.v1.Hero; import com.github.kuangcp.tank.v1.Hinder; import com.github.kuangcp.tank.v1.Tank; import com.github.kuangcp.tank.v2.Shot; -import com.github.kuangcp.tank.v3.Bomb; +import lombok.extern.slf4j.Slf4j; -import java.util.Vector; +import java.util.concurrent.TimeUnit; +@Slf4j public class TankTool { /** - * 工具类-检测爆炸的函数 + * 碰撞检测函数 坦克之间 */ - public static void Bong(Tank t, Shot s, Vector bombs) { - /* - * 形参: 坦克 子弹 炸弹集合 - */ - -// System.out.println("t.getLife()"+t.getLife()); - switch (t.getDirect()) {//上下左右 - case 0: - case 1: - if (t.getX() - 10 <= s.sx && - t.getX() + 10 >= s.sx && - t.getY() - 15 <= s.sy && - t.getY() + 15 >= s.sy) { - s.isLive = false; - t.setLife(t.getLife() - 1);//生命值减一 -// System.out.println("减一"); - - if (t.getLife() == 0) t.setLive(false); - //创建一个炸弹,放入集合 - Bomb b = new Bomb(t.getX() - 10, t.getY() - 15);//敌方的坐标 - bombs.add(b); - if (t instanceof Hero) { - t.setX(480); - t.setY(500); - t.setDirect(0); -// try { -// Thread.sleep(300); -// } catch (InterruptedException e) { -// // TODO Auto-generated catch block -// e.printStackTrace(); -// } - } - } - break; - case 2: - case 3: - if (t.getX() - 15 <= s.sx && - t.getX() + 15 >= s.sx && - t.getY() - 10 <= s.sy && - t.getY() + 10 >= s.sy) { - s.isLive = false; - t.setLife(t.getLife() - 1); -// System.out.println("减一"); - - if (t.getLife() == 0) t.setLive(false); - //创建一个炸弹,放入集合 - - Bomb b = new Bomb(t.getX() - 15, t.getY() - 10);//敌方的坐标 - bombs.add(b); - if (t instanceof Hero) { - t.setX(480); - t.setY(500); - t.setDirect(0); -// try { -// Thread.sleep(300); -// } catch (InterruptedException e) { -// // TODO Auto-generated catch block -// e.printStackTrace(); -// } - } - } - break; - - } -// System.out.println("hero life :"+t.getLife()); - - } - - /** - * 工具类-坦克之间的碰撞检测函数 - */ - public static boolean Rush(Tank me, Tank you) { - + public static boolean hasHint(Tank me, Tank you) { boolean flag = true; switch (you.getDirect()) {//对方 上下 case 0: @@ -228,12 +157,12 @@ public static boolean Rush(Tank me, Tank you) { } /** - * 工具类 检测是否有障碍物是否可以通行 + * 碰撞检测函数 坦克 和 障碍物 */ public static boolean hasHint(Tank t, Hinder h) { int hx = 20, hy = 10; switch (t.getDirect()) { - case 0://上 + case DirectType.UP: if (t.getX() - 10 >= h.getHx() && t.getX() - 10 <= h.getHx() + hx && t.getY() - 15 >= h.getHy() && t.getY() - 15 <= h.getHy() + hy || t.getX() >= h.getHx() && t.getX() <= h.getHx() + hx @@ -241,7 +170,7 @@ public static boolean hasHint(Tank t, Hinder h) { || t.getX() + 10 >= h.getHx() && t.getX() + 10 <= h.getHx() + hx && t.getY() - 15 >= h.getHy() && t.getY() - 15 <= h.getHy() + hy) return true; - case 1://下 + case DirectType.DOWN: if (t.getX() - 10 >= h.getHx() && t.getX() - 10 <= h.getHx() + hx && t.getY() + 15 >= h.getHy() && t.getY() + 15 <= h.getHy() + hy || t.getX() >= h.getHx() && t.getX() <= h.getHx() + hx @@ -249,7 +178,7 @@ public static boolean hasHint(Tank t, Hinder h) { || t.getX() + 10 >= h.getHx() && t.getX() + 10 <= h.getHx() + hx && t.getY() + 15 >= h.getHy() && t.getY() + 15 <= h.getHy() + hy) return true; - case 2: + case DirectType.LEFT: if (t.getX() - 15 >= h.getHx() && t.getX() - 15 <= h.getHx() + hx && t.getY() - 10 >= h.getHy() && t.getY() - 10 <= h.getHy() + hy || t.getX() - 15 >= h.getHx() && t.getX() - 15 <= h.getHx() + hx @@ -257,7 +186,7 @@ public static boolean hasHint(Tank t, Hinder h) { || t.getX() - 15 >= h.getHx() && t.getX() - 15 <= h.getHx() + hx && t.getY() + 10 >= h.getHy() && t.getY() + 10 <= h.getHy() + hy) return true; - case 3: + case DirectType.RIGHT: if (t.getX() + 15 >= h.getHx() - 2 && t.getX() + 15 <= h.getHx() + hx && t.getY() + 10 >= h.getHy() && t.getY() + 10 <= h.getHy() + hy || t.getX() + 15 >= h.getHx() - 2 && t.getX() + 15 <= h.getHx() + hx @@ -282,4 +211,16 @@ public static void judgeHint(Shot s, Hinder h) { } } } + + public static void yieldMsTime(long time) { + yieldTime(time, TimeUnit.MILLISECONDS); + } + + public static void yieldTime(long time, TimeUnit timeUnit) { + try { + timeUnit.sleep(time); + } catch (InterruptedException e) { + log.error("", e); + } + } } diff --git a/gui/src/main/java/com/github/kuangcp/tank/v1/Demons.java b/gui/src/main/java/com/github/kuangcp/tank/v1/EnemyTank.java similarity index 82% rename from gui/src/main/java/com/github/kuangcp/tank/v1/Demons.java rename to gui/src/main/java/com/github/kuangcp/tank/v1/EnemyTank.java index 124dfa36..564d7e80 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v1/Demons.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v1/EnemyTank.java @@ -2,31 +2,35 @@ package com.github.kuangcp.tank.v1; +import com.github.kuangcp.tank.constant.DirectType; +import com.github.kuangcp.tank.util.ExecutePool; import com.github.kuangcp.tank.util.TankTool; import com.github.kuangcp.tank.v2.Shot; import lombok.extern.slf4j.Slf4j; import java.util.Vector; import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicLong; /** * 敌方坦克父类 * 可以继续延伸做出多样化的坦克 - *

- * FIXME 子弹集合是敌方坦克的成员属性 坦克内存一回收 子弹也就没了, 坦克死了 子弹也应该继续飞 */ @Slf4j -public class Demons extends Tank implements Runnable { +public class EnemyTank extends Tank implements Runnable { - public boolean alive; + private static final AtomicLong counter = new AtomicLong(); + // 敌人 id + public long id; public Vector ds = new Vector<>();//子弹集合 private final ExecutorService shotExecutePool; - public Vector ets; + public Vector ets; public Vector bricks; public Vector irons; public Shot s = null; - public int maxLiveShot = 16; //子弹线程存活的最大数 + public int maxLiveShot = 7; //子弹线程存活的最大数 + public boolean delayRemove = false; + Hero hero; boolean with = true; //同类之间有无重叠(true是可以往前走) @@ -34,16 +38,18 @@ public class Demons extends Tank implements Runnable { boolean to = true; //继承了属性,即使直接使用父类的构造器,构造器也一定要显式声明 - public Demons(int x, int y, int speed, int direct) { + public EnemyTank(int x, int y, int speed, int direct) { super(x, y, speed); type = 1; this.direct = direct; this.speed = speed; this.alive = true; - this.shotExecutePool = Executors.newFixedThreadPool(this.maxLiveShot); + this.id = counter.addAndGet(1); + this.shotExecutePool = ExecutePool.buildFixedPool("enemyShot-" + id, this.maxLiveShot / 2); +// log.info("create new Demons. {}", id); } - public void SetInfo(Hero hero, Vector ets, Vector bricks, Vector irons) { + public void SetInfo(Hero hero, Vector ets, Vector bricks, Vector irons) { this.hero = hero; this.ets = ets; this.bricks = bricks; @@ -73,10 +79,6 @@ public void setBri(boolean bri) { * @param hero */ - /** - * 重新封装 - */ - /** * 发射子弹 函数 */ @@ -164,7 +166,7 @@ public boolean TouchOther() { switch (this.direct) { case 0://上 for (int i = 0; i < ets.size(); i++) { - Demons et = ets.get(i); + EnemyTank et = ets.get(i); if (et != this) { if (et.direct == 0 || et.direct == 1) {//对方是上下 //自己的上左 @@ -195,7 +197,7 @@ public boolean TouchOther() { break; case 1: for (int i = 0; i < ets.size(); i++) { - Demons et = ets.get(i); + EnemyTank et = ets.get(i); if (et != this) { //对方是上下 if (et.direct == 0 || et.direct == 1) { @@ -228,7 +230,7 @@ public boolean TouchOther() { break; case 2: for (int i = 0; i < ets.size(); i++) { - Demons et = ets.get(i); + EnemyTank et = ets.get(i); if (et != this) { if (et.direct == 0 || et.direct == 1) { //对方是上下 @@ -261,7 +263,7 @@ public boolean TouchOther() { break; case 3: for (int i = 0; i < ets.size(); i++) { - Demons et = ets.get(i); + EnemyTank et = ets.get(i); if (et != this) { if (et.direct == 0 || et.direct == 1) { //对方是上下 @@ -301,8 +303,8 @@ public boolean TouchOther() { public boolean toUp() { if (y > 30) { to = true; - for (Demons et : ets) { - if (!TankTool.Rush(this, et)) { + for (EnemyTank et : ets) { + if (!TankTool.hasHint(this, et)) { to = (false); break; } @@ -315,12 +317,12 @@ public boolean toUp() { if (TankTool.hasHint(this, iron)) to = false; } - if (to && TankTool.Rush(this, hero)) { + if (to && TankTool.hasHint(this, hero)) { y -= speed; try { Thread.sleep(100); } catch (Exception e) { - e.printStackTrace(); + log.error("", e); } } else { return false; @@ -333,8 +335,8 @@ public boolean toUp() { public boolean toDown() { if (y < 530) { to = true; - for (Demons et : ets) { - if (!TankTool.Rush(this, et)) { + for (EnemyTank et : ets) { + if (!TankTool.hasHint(this, et)) { to = (false); break; } @@ -347,12 +349,12 @@ public boolean toDown() { if (TankTool.hasHint(this, iron)) to = false; } - if (to && TankTool.Rush(this, hero)) { + if (to && TankTool.hasHint(this, hero)) { y += speed; try { Thread.sleep(100); } catch (Exception e) { - e.printStackTrace(); + log.error("", e); } } else { return false; @@ -366,8 +368,8 @@ public boolean toDown() { public boolean toLeft() { if (x > 30) { to = true; - for (Demons et : ets) { - if (!TankTool.Rush(this, et)) { + for (EnemyTank et : ets) { + if (!TankTool.hasHint(this, et)) { to = false; break; } @@ -380,7 +382,7 @@ public boolean toLeft() { if (TankTool.hasHint(this, iron)) to = false; } - if (to && TankTool.Rush(this, hero)) { + if (to && TankTool.hasHint(this, hero)) { x -= speed; try { Thread.sleep(100); @@ -398,8 +400,8 @@ public boolean toLeft() { public boolean toRight() { if (x < 710) { to = true; - for (Demons et : ets) { - if (!TankTool.Rush(this, et)) { + for (EnemyTank et : ets) { + if (!TankTool.hasHint(this, et)) { to = (false); break; } @@ -412,12 +414,12 @@ public boolean toRight() { if (TankTool.hasHint(this, iron)) to = false; } - if (to && TankTool.Rush(this, hero)) { + if (to && TankTool.hasHint(this, hero)) { x += speed; try { Thread.sleep(100); } catch (Exception e) { - e.printStackTrace(); + log.error("", e); } } else { return false; @@ -432,6 +434,58 @@ public boolean toRight() { int min = 0; public void run() { +// actionModeRun(); + run2(); + + // 回收 + log.info("{} clean enemy", id); + shotExecutePool.shutdownNow(); + } + + // 运动 + public void actionModeRun() { + while (true) { + try { + if (this.speed == 0) { + TankTool.yieldMsTime(1000); + continue; + } + this.direct = (int) (Math.random() * 4); + switch (this.direct) { + case DirectType.UP: + toUp(); + break; + case DirectType.DOWN: + toDown(); + break; + case DirectType.LEFT: + toLeft(); + break; + case DirectType.RIGHT: + toRight(); + break; + default: + break; + } + + //判断坦克是否死亡 + if (!this.isAlive()) { + //让坦克退出while即退出线程 + hero.addPrize(1); + break; + } + } catch (Exception e) { + log.error("", e); + } + } + } + + // 攻击 + public void actionModeAttack() { + + } + + public void run2() { //子弹发射的(时间)坐标间隔 while (true) { @@ -449,7 +503,7 @@ public void run() { min++; // if(!bri)this.direct = (int)(Math.random()*4); if (y > 30) { - if (TankTool.Rush(this, hero) && with) y -= speed; + if (TankTool.hasHint(this, hero) && with) y -= speed; // if(bri)y-=speed; // else {y+=speed;this.direct = 1;} // else continue; @@ -460,7 +514,7 @@ public void run() { try { Thread.sleep(50); } catch (Exception e) { - e.printStackTrace(); + log.error("", e); } } @@ -473,7 +527,7 @@ public void run() { // if(!bri)this.direct = (int)(Math.random()*4); if (y < 530) { - if (TankTool.Rush(this, hero) && with && bri) y += speed; + if (TankTool.hasHint(this, hero) && with && bri) y += speed; // if(bri) y+=speed; // else {y-=speed;this.direct = 0;} // else continue; @@ -483,7 +537,7 @@ public void run() { try { Thread.sleep(50); } catch (Exception e) { - e.printStackTrace(); + log.error("", e); } } @@ -496,7 +550,7 @@ public void run() { // if(!bri)this.direct = (int)(Math.random()*4); if (x > 30) { - if (TankTool.Rush(this, hero) && with && bri) x -= speed; + if (TankTool.hasHint(this, hero) && with && bri) x -= speed; // if(bri)x-=speed; // else{x+=speed;this.direct = 3;} // else continue; @@ -506,7 +560,7 @@ public void run() { try { Thread.sleep(50); } catch (Exception e) { - e.printStackTrace(); + log.error("", e); } } @@ -519,7 +573,9 @@ public void run() { // if(!bri)this.direct = (int)(Math.random()*4); if (x < 710) { - if (TankTool.Rush(this, hero) && with && bri) x += speed; + if (TankTool.hasHint(this, hero) && with && bri) { + x += speed; + } // if(bri)x+=speed; // else{x-=speed;this.direct = 2;} else break; @@ -529,7 +585,7 @@ public void run() { try { Thread.sleep(50); } catch (Exception e) { - e.printStackTrace(); + log.error("", e); } } @@ -541,9 +597,10 @@ public void run() { } //判断坦克是否死亡 - if (!this.getisLive()) { + if (!this.isAlive()) { //让坦克退出while即退出线程 - hero.setPrize(hero.getPrize() + 1); + hero.addPrize(1); + break; } } diff --git a/gui/src/main/java/com/github/kuangcp/tank/v1/Hero.java b/gui/src/main/java/com/github/kuangcp/tank/v1/Hero.java index 39c6654c..2561b81f 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v1/Hero.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v1/Hero.java @@ -1,29 +1,31 @@ package com.github.kuangcp.tank.v1; +import com.github.kuangcp.tank.util.ExecutePool; import com.github.kuangcp.tank.util.TankTool; import com.github.kuangcp.tank.v2.Shot; import java.awt.*; import java.util.Vector; import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; public class Hero extends Tank { //子弹集合 public Vector ss = new Vector<>(); private final ExecutorService shotExecutePool; + private long lastShotNs = 0; + private long shotCD = 224_000_000; public Shot s = null;//子弹 public Graphics g; //画笔不可少 public boolean flag = false;//是否击中 static int round = 0; - public Vector ets;//只是一个指针(引用) + public Vector ets;//只是一个指针(引用) public Vector bricks; public Vector irons; public boolean to = true; - static int prize = 0;//击敌个数 + private int prize = 0;//击敌个数 public int maxLiveShot = 8;//主坦克子弹线程存活的最大数 int speed = 3; static int Life = 10; @@ -57,8 +59,12 @@ public void setPrize(int prize) { this.prize = prize; } + public void addPrize(int delta) { + this.prize += delta; + } + - public Hero(int x, int y, int speed, Vector ets, Vector bricks, Vector irons) { + public Hero(int x, int y, int speed, Vector ets, Vector bricks, Vector irons) { super(x, y, speed); // this.Life = 10; // 要给g分配内存 初始化对象 @@ -68,11 +74,15 @@ public Hero(int x, int y, int speed, Vector ets, Vector bricks, V this.bricks = bricks; this.irons = irons; - this.shotExecutePool = Executors.newFixedThreadPool(maxLiveShot); + this.shotExecutePool = ExecutePool.buildFixedPool("heroShot", maxLiveShot); } public void shotEnemy() { - if (this.ss.size() >= this.maxLiveShot || !this.getisLive()) { + final long curNs = System.nanoTime(); + if (lastShotNs != 0 && curNs - lastShotNs < shotCD) { + return; + } + if (this.ss.size() >= this.maxLiveShot || !this.isAlive()) { return; } @@ -97,76 +107,78 @@ public void shotEnemy() { } //启动子弹线程 shotExecutePool.execute(s); + lastShotNs = curNs; } //移动的函数 public void moveup() { to = true; //检测敌方坦克碰撞 - for (Demons et : ets) { - if (!TankTool.Rush(this, et)) + for (EnemyTank et : ets) { + if (!TankTool.hasHint(this, et)) to = false; } //检测障碍物 - for (Brick brick : bricks) { - if (TankTool.hasHint(this, brick)) - to = false; - } - for (Iron iron : irons) { - if (TankTool.hasHint(this, iron)) - to = false; - } - if (to) +// for (Brick brick : bricks) { +// if (TankTool.hasHint(this, brick)) +// to = false; +// } +// for (Iron iron : irons) { +// if (TankTool.hasHint(this, iron)) +// to = false; +// } + if (to) { setY(getY() - getSpeed()); + } } public void movedown() { to = true; - for (Demons et : ets) - if (!TankTool.Rush(this, et)) - to = false; - for (Brick brick : bricks) { - if (TankTool.hasHint(this, brick)) + for (EnemyTank et : ets) + if (!TankTool.hasHint(this, et)) to = false; - } - for (Iron iron : irons) { - if (TankTool.hasHint(this, iron)) - to = false; - } +// for (Brick brick : bricks) { +// if (TankTool.hasHint(this, brick)) +// to = false; +// } +// for (Iron iron : irons) { +// if (TankTool.hasHint(this, iron)) +// to = false; +// } if (to) setY(getY() + getSpeed()); } public void moveleft() { to = true; - for (Demons et : ets) - if (!TankTool.Rush(this, et)) - to = false; - for (Brick brick : bricks) { - if (TankTool.hasHint(this, brick)) - to = false; - } - for (Iron iron : irons) { - if (TankTool.hasHint(this, iron)) + for (EnemyTank et : ets) + if (!TankTool.hasHint(this, et)) to = false; - } +// for (Brick brick : bricks) { +// if (TankTool.hasHint(this, brick)) +// to = false; +// } +// for (Iron iron : irons) { +// if (TankTool.hasHint(this, iron)) +// to = false; +// } if (to) setX(getX() - getSpeed()); } public void moveright() { to = true; - for (Demons et : ets) - if (!TankTool.Rush(this, et)) - to = false; - for (Brick brick : bricks) { - if (TankTool.hasHint(this, brick)) + for (EnemyTank et : ets) + if (!TankTool.hasHint(this, et)) to = false; - } - for (Iron iron : irons) { - if (TankTool.hasHint(this, iron)) - to = false; - } +// for (Brick brick : bricks) { +// if (TankTool.hasHint(this, brick)) +// to = false; +// } +// for (Iron iron : irons) { +// if (TankTool.hasHint(this, iron)) +// to = false; +// } if (to) setX(getX() + getSpeed()); } diff --git a/gui/src/main/java/com/github/kuangcp/tank/v1/Tank.java b/gui/src/main/java/com/github/kuangcp/tank/v1/Tank.java index a171b64f..dda2ae7a 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v1/Tank.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v1/Tank.java @@ -10,23 +10,23 @@ public class Tank { int direct = 0; // 初始方向 int type = 0; // 坦克的种类 int speed = 5; // 前进的步长 - boolean isLive = true;//是否存活 - int Life = 1;//生命值 + boolean alive = true;//是否存活 + int life = 1;//生命值 public int getLife() { - return Life; + return life; } public void setLife(int life) { - Life = life; + this.life = life; } - public boolean getisLive() { - return isLive; + public boolean isAlive() { + return alive; } - public void setLive(boolean isLive) { - this.isLive = isLive; + public void setAlive(boolean isLive) { + this.alive = isLive; } public int getSpeed() { diff --git a/gui/src/main/java/com/github/kuangcp/tank/v1/TankGame1.java b/gui/src/main/java/com/github/kuangcp/tank/v1/TankGame1.java index 9a179665..8134a085 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v1/TankGame1.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v1/TankGame1.java @@ -50,7 +50,7 @@ class MyPanel extends JPanel implements KeyListener { Hero hero = null; int enSize = 3; //敌人的数量 //定义泛型的集合类 - Vector ets = new Vector(); + Vector ets = new Vector(); Vector bricks = new Vector(); //画板的构造函数 放图形的对象 @@ -85,7 +85,7 @@ public void paint(Graphics g) { } //画出敌人坦克 - for (Demons s : ets) { + for (EnemyTank s : ets) { this.drawTank(s.getX(), s.getY(), g, s.getDirect(), s.getType()); } diff --git a/gui/src/main/java/com/github/kuangcp/tank/v2/MyPanel.java b/gui/src/main/java/com/github/kuangcp/tank/v2/MyPanel.java index d3982bb4..c054ac84 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v2/MyPanel.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v2/MyPanel.java @@ -2,7 +2,7 @@ import com.github.kuangcp.tank.v1.Brick; -import com.github.kuangcp.tank.v1.Demons; +import com.github.kuangcp.tank.v1.EnemyTank; import com.github.kuangcp.tank.v1.Hero; import javax.swing.*; @@ -19,7 +19,7 @@ public class MyPanel extends JPanel implements KeyListener, Runnable { Hero hero = null; int enSize = 3; // 敌人的数量 //定义一个 泛型的集合类 表示敌人坦克集合 - Vector ets = new Vector(); + Vector ets = new Vector(); Vector bricks = new Vector(); @@ -72,7 +72,7 @@ public void paint(Graphics g) { //画出敌人坦克 for (int i = 0; i < ets.size(); i++) { - Demons s = ets.get(i); + EnemyTank s = ets.get(i); this.drawTank(s.getX(), s.getY(), g, s.getDirect(), s.getType()); } diff --git a/gui/src/main/java/com/github/kuangcp/tank/v2/Shot.java b/gui/src/main/java/com/github/kuangcp/tank/v2/Shot.java index 9dcefc09..297a1ee6 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v2/Shot.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v2/Shot.java @@ -39,6 +39,8 @@ public void run() { do { try { Thread.sleep(50); //每个子弹发射的延迟运动的时间 + } catch (InterruptedException i) { + break; } catch (Exception e) { log.error("", e); } diff --git a/gui/src/main/java/com/github/kuangcp/tank/v2/TankGame2.java b/gui/src/main/java/com/github/kuangcp/tank/v2/TankGame2.java index 7d0a6cef..c011c969 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v2/TankGame2.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v2/TankGame2.java @@ -1,8 +1,8 @@ package com.github.kuangcp.tank.v2; /** * 能控制坦克运动,有墙的机制 - * - * + *

+ *

* 坦克2.0 版本 目标:按下j键发射直线的子弹 * 子弹是一个对象,线程 */ @@ -12,44 +12,41 @@ @SuppressWarnings("serial") public class TankGame2 extends JFrame { - - public static MyPanel mp = null;//画板 - - -// JS j = null; - @SuppressWarnings("unused") - public static void main(String[] args) { - // TODO Auto-generated method stub - TankGame2 Tank = new TankGame2(); - } - - //最外层JFrame的构造函数 - public TankGame2 (){ - - mp = new MyPanel();//已经成为一个线程 要启动它 - Thread t = new Thread(mp); - t.start(); - + + public static MyPanel mp = null;//画板 + + + // JS j = null; + @SuppressWarnings("unused") + public static void main(String[] args) { + // TODO Auto-generated method stub + TankGame2 Tank = new TankGame2(); + } + + //最外层JFrame的构造函数 + public TankGame2() { + + mp = new MyPanel();//已经成为一个线程 要启动它 + Thread t = new Thread(mp); + t.start(); + // j = new JS(); - -// this.add(j); - this.add(mp); - - //注册键盘监听 - //下面的语句翻译为 :当前类的监听者是mp - this.addKeyListener(mp); -// this.addKeyListener(j); - - this.setLocation(900,200); - this.setSize(500,400); - this.setVisible(true); - this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - } -} +// this.add(j); + this.add(mp); + //注册键盘监听 + //下面的语句翻译为 :当前类的监听者是mp + this.addKeyListener(mp); +// this.addKeyListener(j); + this.setLocation(900, 200); + this.setSize(500, 400); + this.setVisible(true); + this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + } +} ///////////////////////////////////////////////////////////////////////////// diff --git a/gui/src/main/java/com/github/kuangcp/tank/v3/GameMain.java b/gui/src/main/java/com/github/kuangcp/tank/v3/GameMain.java deleted file mode 100644 index a8b4650a..00000000 --- a/gui/src/main/java/com/github/kuangcp/tank/v3/GameMain.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.github.kuangcp.tank.v3; - -public class GameMain { - - public static void main(String[] args) { - TankFrame T = new TankFrame(); - Thread t = new Thread(T); - t.start(); - } -} diff --git a/gui/src/main/java/com/github/kuangcp/tank/v3/KE.java b/gui/src/main/java/com/github/kuangcp/tank/v3/KE.java deleted file mode 100644 index 4e4dbc86..00000000 --- a/gui/src/main/java/com/github/kuangcp/tank/v3/KE.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.github.kuangcp.tank.v3; - -public class KE { - - int keyCode; - - public KE() { - } - - public KE(int k) { - keyCode = k; - } -} diff --git a/gui/src/main/java/com/github/kuangcp/tank/v3/ListenEventGroup.java b/gui/src/main/java/com/github/kuangcp/tank/v3/ListenEventGroup.java new file mode 100644 index 00000000..f177dc78 --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/v3/ListenEventGroup.java @@ -0,0 +1,94 @@ +package com.github.kuangcp.tank.v3; + +import java.awt.event.KeyEvent; + +/** + * @author https://github.com/kuangcp on 2021-09-06 02:58 + * @see MyPanel3#keyPressed + */ +public class ListenEventGroup { + + public static ListenEventGroup instance = new ListenEventGroup(); + + private volatile boolean up; + private volatile boolean down; + private volatile boolean left; + private volatile boolean right; + private volatile boolean shot; + + public void handleRelease(KeyEvent re){ + switch (re.getKeyCode()) { + case KeyEvent.VK_A: + this.setLeft(false); + break; + case KeyEvent.VK_D: + this.setRight(false); + break; + case KeyEvent.VK_S: + this.setDown(false); + break; + case KeyEvent.VK_W: + this.setUp(false); + break; + case KeyEvent.VK_SPACE: + this.setShot(false); + break; + default: + break; + } + } + public boolean isUp() { + return up; + } + + public void setUp(boolean up) { + this.up = up; + } + + public boolean isDown() { + return down; + } + + public void setDown(boolean down) { + this.down = down; + } + + public boolean isLeft() { + return left; + } + + public void setLeft(boolean left) { + this.left = left; + } + + public boolean isRight() { + return right; + } + + public void setRight(boolean right) { + this.right = right; + } + + public boolean isShot() { + return shot; + } + + public void setShot(boolean shot) { + this.shot = shot; + } + + public boolean hasPressMoveEvent() { + return up || down || left || right; + } + + @Override + public String toString() { + return "ListenEventGroup{" + + "up=" + up + + ", down=" + down + + ", left=" + left + + ", right=" + right + + ", shot=" + shot + + '}'; + } +} diff --git a/gui/src/main/java/com/github/kuangcp/tank/v3/MainTankGame.java b/gui/src/main/java/com/github/kuangcp/tank/v3/MainTankGame.java new file mode 100644 index 00000000..d7f57abd --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/v3/MainTankGame.java @@ -0,0 +1,13 @@ +package com.github.kuangcp.tank.v3; + +public class MainTankGame { + + /** + * -Xmx300m -Xms300m -XX:+UseG1GC + */ + public static void main(String[] args) { + Thread t = new Thread(new TankFrame()); + t.setName("mainFrame"); + t.start(); + } +} diff --git a/gui/src/main/java/com/github/kuangcp/tank/v3/MyPanel3.java b/gui/src/main/java/com/github/kuangcp/tank/v3/MyPanel3.java index 2a77987b..513990d1 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v3/MyPanel3.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v3/MyPanel3.java @@ -2,12 +2,15 @@ package com.github.kuangcp.tank.v3; +import com.github.kuangcp.tank.util.TankTool; import com.github.kuangcp.tank.v1.Brick; -import com.github.kuangcp.tank.v1.Demons; +import com.github.kuangcp.tank.v1.EnemyTank; import com.github.kuangcp.tank.v1.Hero; import com.github.kuangcp.tank.v1.Iron; import com.github.kuangcp.tank.v2.Shot; -import com.github.kuangcp.tank.util.TankTool; +import com.github.kuangcp.tank.v3.resource.AvatarMgr; +import com.github.kuangcp.tank.v3.resource.BombMgr; +import com.github.kuangcp.tank.v3.thread.ExitFlagRunnable; import lombok.extern.slf4j.Slf4j; import javax.imageio.ImageIO; @@ -16,7 +19,9 @@ import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.io.IOException; +import java.util.Objects; import java.util.Vector; +import java.util.concurrent.TimeUnit; /** @@ -24,124 +29,120 @@ */ @SuppressWarnings("serial") @Slf4j -public class MyPanel3 extends JPanel implements KeyListener, Runnable { - - //定义我的坦克 - Hero hero = null; - PressedTwo PT = null; - TankFrame T; - static boolean Continue = true;//是否 不是继续上局 - final int Q = (int) (Math.random() * 5); +public class MyPanel3 extends JPanel implements KeyListener, ExitFlagRunnable { + + Hero hero; + PressedTwo pressedTwo; + static boolean resumePlay = true; // 敌人的数量 static int enSize = 10; //定义一个 泛型的集合ets 表示敌人坦克集合 - public Vector ets = new Vector(); - - //定义炸弹爆炸集合 - public Vector bombs = new Vector(); + public Vector enemyList = new Vector<>(); //定义砖块集合 - public Vector bricks = new Vector(); - public Vector irons = new Vector(); + public Vector bricks = new Vector<>(); + public Vector irons = new Vector<>(); //所有按下键的code集合 - // fixme 并发修改问题 - public Vector actions = new Vector<>(); - public static int[][] ETS = new int[12][2]; + public static int[][] enemyTankMap = new int[12][2]; public static int[] myself = new int[6]; - //声明三个图片引用 - Image image1 = null; - Image image2 = null; - Image image3 = null; - Image over = null; - Image[] Me = new Image[5]; - Image Win = null; + + Image overImg = null; + Image winImg = null; + + private volatile boolean exit = false; + + public void exit() { + this.exit = true; + } /** * 画板的构造函数 用来初始化对象 + * 多个敌方坦克应该用集合类,而且要考虑线程安全所以不能用ArrayList只能用Vector */ - //多个敌方坦克应该用集合类,而且要考虑线程安全所以不能用ArrayList只能用Vector - public MyPanel3(TankFrame T) { -// System.out.println("MyPanel3的成员地址"+ets); - this.T = T; -// 创建英雄坦克 - if (Continue) {//正常启动并创建坦克线程 - hero = new Hero(480, 500, 3, ets, bricks, irons);//坐标和步长和敌人坦克集合 + public MyPanel3() { + //创建英雄坦克 + if (resumePlay) {//正常启动并创建坦克线程 + hero = new Hero(480, 500, 3, enemyList, bricks, irons);//坐标和步长和敌人坦克集合 hero.setLife(10);//设置生命值 } else { - hero = new Hero(myself[0], myself[1], 3, ets, bricks, irons); + hero = new Hero(myself[0], myself[1], 3, enemyList, bricks, irons); hero.setLife(myself[2]); hero.setPrize(myself[3]); } //多键监听实现 - PT = new PressedTwo(actions, hero, this); - Thread p = new Thread(PT); + pressedTwo = new PressedTwo(ListenEventGroup.instance, hero, this); + Thread p = new Thread(pressedTwo); + p.setName("keyEventGroup"); p.start(); -// 创建 敌人的坦克 - Demons ett = null; - if (Continue) {//正常启动并创建坦克线程 + // 创建 敌人的坦克 + EnemyTank ett = null; + if (resumePlay) {//正常启动并创建坦克线程 for (int i = 0; i < enSize; i++) { //在四个随机区域产生坦克 switch ((int) (Math.random() * 4)) { case 0: - ett = new Demons(20 + (int) (Math.random() * 30), 20 + (int) (Math.random() * 30), 2, i % 4); - ett.SetInfo(hero, ets, bricks, irons); + ett = new EnemyTank(20 + (int) (Math.random() * 30), 20 + (int) (Math.random() * 30), 2, i % 4); + ett.SetInfo(hero, enemyList, bricks, irons); break; case 1: - ett = new Demons(700 - (int) (Math.random() * 30), 20 + (int) (Math.random() * 30), 2, i % 4); - ett.SetInfo(hero, ets, bricks, irons); + ett = new EnemyTank(700 - (int) (Math.random() * 30), 20 + (int) (Math.random() * 30), 2, i % 4); + ett.SetInfo(hero, enemyList, bricks, irons); break; case 2: - ett = new Demons(20 + (int) (Math.random() * 30), 200 + (int) (Math.random() * 30), 2, i % 4); - ett.SetInfo(hero, ets, bricks, irons); + ett = new EnemyTank(20 + (int) (Math.random() * 30), 200 + (int) (Math.random() * 30), 2, i % 4); + ett.SetInfo(hero, enemyList, bricks, irons); break; case 3: - ett = new Demons(700 - (int) (Math.random() * 30), 200 + (int) (Math.random() * 30), 2, i % 4); - ett.SetInfo(hero, ets, bricks, irons); + ett = new EnemyTank(700 - (int) (Math.random() * 30), 200 + (int) (Math.random() * 30), 2, i % 4); + ett.SetInfo(hero, enemyList, bricks, irons); break; } - //创建敌人坦克 再加入到集合里去 - // ett = new Demons(20+(int)(Math.random()*700),20+(int)(Math.random()*520) ,2 ,i%4,hero,ets,bricks,irons); - //默认方向是上 要调整成下(1) - // et.setDirect(1); - //启动线程 -// System.out.println(ett); + + if (Objects.isNull(ett)) { + continue; + } Thread t = new Thread(ett); + t.setName("enemyThread-" + ett.id); t.start(); //坦克加入集合 - ets.add(ett); + enemyList.add(ett); } -/**进入读取文件步骤*/ - } else {//进入读取步骤 - for (int i = 0; i < ETS.length; i++) {//这里的ETS的length就是二维数组的行数 - if (ETS[i][0] == 0) break; + } else { + /*进入读取文件步骤*/ + for (int i = 0; i < enemyTankMap.length; i++) { + if (enemyTankMap[i][0] == 0) break; - ett = new Demons(ETS[i][0], ETS[i][1], 2, i % 4); - ett.SetInfo(hero, ets, bricks, irons); + ett = new EnemyTank(enemyTankMap[i][0], enemyTankMap[i][1], 2, i % 4); + ett.SetInfo(hero, enemyList, bricks, irons); Thread t = new Thread(ett); + t.setName("enemyThreadF-" + ett.id); t.start(); //坦克加入集合 - ets.add(ett); + enemyList.add(ett); // System.out.print("创建单个坦克地址:"+ett); // System.out.println("_____X="+ett.getX()+"Y="+ett.getY()); // System.out.println("进入了读取数组这里"); } } -// System.out.println("构造器里:MyPanel3的成员地址"+ets); + + //创建砖块 // createB(bricks, 40, 40, 200, 400); // createB(bricks, 200, 40, 400, 100); // createB(bricks, 400, 40, 700, 400); // createB(bricks, 200, 300, 400, 400); // createB(bricks, 40, 40, 700, 400); + //眼睛 createI(irons, 180, 100, 260, 180); createI(irons, 540, 100, 620, 180); // createB(bricks, 200, 120, 240, 160); // createB(bricks, 560, 120, 600, 160); + //鼻子 int m = 400, n = 260; createI(irons, m, n, m + 20, n + 10); @@ -163,31 +164,13 @@ public MyPanel3(TankFrame T) { createI(irons, 330, 410, 480, 430); + try { + BombMgr.instance.initImg(); -//初始化图片 引用到src目录下和包同级的图片 -// image1 = Toolkit.getDefaultToolkit().getImage(Panel.class.getResource("/bomb_1.gif")); - - //放在同级包目录下的图片:导出的时候也能找到 - /** - * for (int i=1;i<3;i++){ - * images[i] = ImageIO.read( - * getClass().getResource("/images/bomb_1.gif")); - * } - * /Game.Tanke/bomb_1.gif - */ + overImg = ImageIO.read(getClass().getResourceAsStream("/images/Over4.jpg")); - // 如果这样传入图片的话,一运行就会抛异常,所以就不会有窗口显示出来,但是在后台跑 - try { - image1 = ImageIO.read(getClass().getResourceAsStream("/images/bomb_1.gif")); - image2 = ImageIO.read(getClass().getResourceAsStream("/images/bomb_2.gif")); - image3 = ImageIO.read(getClass().getResourceAsStream("/images/bomb_3.gif")); - over = ImageIO.read(getClass().getResourceAsStream("/images/Over4.jpg")); - Me[0] = ImageIO.read(getClass().getResourceAsStream("/images/Me.jpg")); - Me[1] = ImageIO.read(getClass().getResourceAsStream("/images/Me2.jpg")); - Me[2] = ImageIO.read(getClass().getResourceAsStream("/images/Me3.jpg")); - Me[3] = ImageIO.read(getClass().getResourceAsStream("/images/Me4.jpg")); - Me[4] = ImageIO.read(getClass().getResourceAsStream("/images/Me5.jpg")); - Win = ImageIO.read(getClass().getResourceAsStream("/images/Win2.jpg")); + AvatarMgr.instance.initImg(); + winImg = ImageIO.read(getClass().getResourceAsStream("/images/Win2.jpg")); } catch (IOException e) { log.error("", e); } @@ -201,7 +184,7 @@ public void paint(Graphics g) { g.fillRect(0, 0, 760, 560);//填满一个黑色矩形,说明了是一整个JPanel在JFrame上的 g.setColor(Color.green); g.drawRect(20, 20, 720, 520); - Shot myShot = null; + Shot myShot; /*画出障碍物__砖__ 铁__*/ @@ -231,22 +214,17 @@ public void paint(Graphics g) { g.fillRect(560, 120, 40, 40); /*画出头像*/ - g.drawImage(Me[Q], 380, 480, 60, 60, this); + g.drawImage(AvatarMgr.instance.curImg, 380, 480, 60, 60, this); /*画出主坦克*/ - if (hero.getisLive()) { - for (Demons et : ets) { - for (int j = 0; j < et.ds.size(); j++) { - if (et.ds.get(j).isLive) { - TankTool.Bong(hero, et.ds.get(j), bombs); - } - } + if (hero.isAlive()) { + for (EnemyTank et : enemyList) { + BombMgr.instance.checkBong(hero, et.ds); } this.drawTank(hero.getX(), hero.getY(), g, hero.getDirect(), hero.getType()); } - -/**画出自己的子弹*/ //画子弹是可以封装成一个方法的 + /*画出自己的子弹*/ //画子弹是可以封装成一个方法的 // 从ss 这个子弹集合中 取出每颗子弹,并画出来 for (int i = 0; i < hero.ss.size(); i++) { myShot = hero.ss.get(i); @@ -258,7 +236,7 @@ public void paint(Graphics g) { } if (myShot.sx < 440 && myShot.sx > 380 && myShot.sy < 540 && myShot.sy > 480) { myShot.isLive = false; - hero.setLive(false); + hero.setAlive(false); } if (hero.ss.get(i) != null && hero.ss.get(i).isLive) {//为什么后面的判断条件 存不存在都会没影响呢 // System.out.println("当前是"+i+"个子弹线程"); @@ -270,11 +248,10 @@ public void paint(Graphics g) { //子弹线程死了 就要把它从集合中删除 hero.ss.remove(myShot); } - } -/**敌人子弹*/ - for (Demons et : ets) { + /*敌人子弹*/ + for (EnemyTank et : enemyList) { for (int i = 0; i < et.ds.size(); i++) { myShot = et.ds.get(i); for (Brick brick : bricks) { @@ -285,61 +262,32 @@ public void paint(Graphics g) { } if (myShot.sx < 440 && myShot.sx > 380 && myShot.sy < 540 && myShot.sy > 480) { myShot.isLive = false; - hero.setLive(false); + hero.setAlive(false); } if (et.ds.get(i) != null && et.ds.get(i).isLive) { - // System.out.println("当前是"+i+"个子弹线程"); g.setColor(Color.cyan); g.draw3DRect(myShot.sx, myShot.sy, 1, 1, false); } if (!myShot.isLive) { - //子弹线程死了 就要把它从集合中删除 et.ds.remove(myShot); } - } } -/** 画出炸弹*/ - for (int i = 0; i < bombs.size(); i++) { - //取出炸弹 - Bomb b = bombs.get(i); -// System.out.println("size = "+bombs.size()); - if (b.life > 10) { - g.drawImage(image1, b.bx, b.by, 30, 30, this); -// if(rr){ -// Audio B = new Audio("./src/RE/GameBegin.wav"); -// B.start(); -// rr = false; -// } - - } else if (b.life > 5) { - g.drawImage(image2, b.bx, b.by, 30, 30, this); - } else { - g.drawImage(image3, b.bx, b.by, 30, 30, this); - } - //让b的生命减少 - b.lifeDown(); - //炸弹的生命值为零 移出集合 - if (b.life == 0) { - bombs.remove(b); - - } -// hero.LifeDown(); -// System.out.println("size = "+bombs.size()); - } -/**画出敌人坦克*/ + BombMgr.instance.drawBomb(g, this); + /*画出敌人坦克*/ // if(Continue){ //坦克少于5个就自动添加4个 - if (ets.size() < 5) { + if (enemyList.size() < 5) { for (int i = 0; i < 4; i++) { - Demons d = new Demons(20 + (int) (Math.random() * 400), 20 + (int) (Math.random() * 300), 2, i % 4); - d.SetInfo(hero, ets, bricks, irons); - Thread R = new Thread(d); - R.start(); - ets.add(d); + EnemyTank d = new EnemyTank(20 + (int) (Math.random() * 400), 20 + (int) (Math.random() * 300), 2, i % 4); + d.SetInfo(hero, enemyList, bricks, irons); + Thread fillThread = new Thread(d); + fillThread.setName("fillEnemy" + d.id); + fillThread.start(); + enemyList.add(d); } } // } @@ -355,81 +303,92 @@ public void paint(Graphics g) { // } // } // System.out.println("画板上画坦克时的坦克地址"+ets); - for (int i = 0; i < ets.size(); i++) { - Demons s = ets.get(i); + for (int i = 0; i < enemyList.size(); i++) { + EnemyTank demon = enemyList.get(i); //存活再画出来 - if (s.getisLive()) { + if (demon.isAlive()) { //坦克间的碰撞 // for(int k=0;k { + try { + TimeUnit.SECONDS.sleep(20); + } catch (InterruptedException e) { + log.error("", e); + } + enemyList.remove(demon); + }); + thread.setName("lazyRemove-" + demon.id); + thread.start(); + } } -//游戏结束的画面 - if (!hero.getisLive()) { - g.drawImage(over, 0, 0, 760, 560, this); + + //游戏结束的画面 + if (!hero.isAlive()) { + g.drawImage(overImg, 0, 0, 760, 560, this); g.drawString("您的总成绩为:" + hero.getPrize(), 320, 500); } -//判断获胜 -// if(hero.getPrize()>=40){ -// g.drawImage(Win,0,0,760,560,this); -// g.drawString("您的总成绩为:"+hero.getPrize(), 320, 500); -// -// } - + //判断获胜 + if (hero.getPrize() >= 40) { + g.drawImage(winImg, 0, 0, 760, 560, this); + g.drawString("您的总成绩为:" + hero.getPrize(), 320, 500); + } } -////////////////////////////////////////////////////////////////////////////////// /** * 画出所有坦克的函数 XY是坦克中心的坐标,不是画图参照点 @@ -437,7 +396,8 @@ public void paint(Graphics g) { public void drawTank(int X, int Y, Graphics g, int direct, int type) { //判断坦克类型 - int x, y;//系统画图函数的参照点 + //系统画图函数的参照点 + int x, y; switch (type) { case 1: g.setColor(Color.cyan); @@ -512,107 +472,44 @@ public void drawTank(int X, int Y, Graphics g, int direct, int type) { } } - //定义变量以便于控制按键的处理 - //把按键的处理做成一个集合,循环遍历,在另一个类里面做 - KE w, a, s, d, j; - int count = 0; - boolean ww = true, aa = true, ss = true, dd = true, jj = true; - /** * 实现了墙 + *

+ * 当按下键盘上的键时监听者的处理函数 */ - //当按下键盘上的键时监听者的处理函数 @Override public void keyPressed(KeyEvent e) { -// 加了if(内层的)限制后 实现了墙的限制(如果是游戏中的道具,该怎么办) - - if (e.getKeyCode() == KeyEvent.VK_A) { - - if (aa) { - a = new KE(KeyEvent.VK_A); - actions.add(a); - count++; - aa = false; - } - + //加了if(内层的)限制后 实现了墙的限制(如果是游戏中的道具,该怎么办) + if (e.getKeyChar() == KeyEvent.VK_SPACE) { + ListenEventGroup.instance.setShot(true); + } + if (ListenEventGroup.instance.hasPressMoveEvent()) { + return; } - if (e.getKeyCode() == KeyEvent.VK_D) { - if (dd) { - d = new KE(KeyEvent.VK_D); - actions.add(d); - count++; - dd = false; - } + if (e.getKeyCode() == KeyEvent.VK_A) { + ListenEventGroup.instance.setLeft(true); + } + if (e.getKeyCode() == KeyEvent.VK_D) { + ListenEventGroup.instance.setRight(true); } if (e.getKeyCode() == KeyEvent.VK_W) { - if (ww) { - w = new KE(KeyEvent.VK_W); - actions.add(w); - count++; - ww = false; - } - - + ListenEventGroup.instance.setUp(true); } if (e.getKeyCode() == KeyEvent.VK_S) { - if (ss) { - s = new KE(KeyEvent.VK_S); - actions.add(s); - count++; - ss = false; - } + ListenEventGroup.instance.setDown(true); } - this.repaint(); - if (e.getKeyChar() == KeyEvent.VK_J) { - if (jj) { - j = new KE(KeyEvent.VK_J); - actions.add(j); - count++; - jj = false; - } - - /**自己做的开火函数 - //hero.drawS(hero.getX(), hero.getY(), hero.getDirect(), hero.g); - */ - } //必须重新绘制窗口,不然上面的方法不能视觉上动起来 this.repaint(); - count = 0; } - //键离开了就把按键从集合中移除 @Override public void keyReleased(KeyEvent re) { - switch (re.getKeyCode()) { - case KeyEvent.VK_A: - actions.remove(a); - aa = true; - break; - case KeyEvent.VK_D: - actions.remove(d); - dd = true; - break; - case KeyEvent.VK_S: - actions.remove(s); - ss = true; - break; - case KeyEvent.VK_W: - actions.remove(w); - ww = true; - break; - case KeyEvent.VK_J: - actions.remove(j); - jj = true; - break; - default: - break; - } - + ListenEventGroup.instance.handleRelease(re); } @Override @@ -649,7 +546,7 @@ public void createI(Vector irons, int startX, int startY, int endX, int en @Override public void run() { //每隔100ms重绘 - while (true) { + while (!exit) { try { Thread.sleep(10); //里面的数字就是刷新时间的间隔 而且每当按下J键就会拉起MyPanel线程 相当于加快了刷新 @@ -665,6 +562,9 @@ public void run() { // break; // } } + + // clean + pressedTwo.exit(); } public static int getEnSize() { diff --git a/gui/src/main/java/com/github/kuangcp/tank/v3/MyPanel302.java b/gui/src/main/java/com/github/kuangcp/tank/v3/MyPanel302.java index e96e7448..423d4f01 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v3/MyPanel302.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v3/MyPanel302.java @@ -1,68 +1,77 @@ /** * 就是一个用来显示属性的画板 - * 不停的刷新 - * JLabel 被add后 就算他发生改变,对之前已经add了的组件 不会有影响 - * 但是可以用setText来进行改变 + * 不停的刷新 + * JLabel 被add后 就算他发生改变,对之前已经add了的组件 不会有影响 + * 但是可以用setText来进行改变 */ package com.github.kuangcp.tank.v3; -import com.github.kuangcp.tank.v1.Demons; +import com.github.kuangcp.tank.v1.EnemyTank; import com.github.kuangcp.tank.v1.Hero; +import com.github.kuangcp.tank.v3.thread.ExitFlagRunnable; import javax.swing.*; import java.awt.*; import java.util.Vector; @SuppressWarnings("serial") -public class MyPanel302 extends JPanel implements Runnable { - - JLabel jl1 = null; - JLabel prizeNo = null; - MyPanel302 mp =null; - Hero hero = null; - Vector ets=null; - - - public MyPanel302(JLabel jl1, Hero hero, Vector ets, JLabel prizeNo){ - this.jl1 = jl1; - this.hero = hero; - this.ets = ets; - this.prizeNo = prizeNo; +public class MyPanel302 extends JPanel implements ExitFlagRunnable { + + JLabel jl1; + JLabel prizeNo; + MyPanel302 mp = null; + Hero hero; + Vector ets; + + private volatile boolean exit = false; + + public void exit() { + this.exit = true; + } + + public MyPanel302(JLabel jl1, Hero hero, Vector ets, JLabel prizeNo) { + this.jl1 = jl1; + this.hero = hero; + this.ets = ets; + this.prizeNo = prizeNo; // jl1 =new JLabel("生命值: "+Hero.Life/3); // MyPanel302 mp = new MyPanel302(); // mp.add(jl1,BorderLayout.SOUTH); - } - - public void paint(Graphics g){ - super.paint(g); - - int X=20,Y=20; - int x,y; + } + + public void paint(Graphics g) { + super.paint(g); + + int X = 20, Y = 20; + int x, y; /**主坦克*/ - g.setColor(Color.yellow); - //向上 - x=X-10;y=Y-15;//把坦克中心坐标换算成系统用来画坦克的坐标 - //(并且全是取的左上角) - - g.fill3DRect(x, y, 5, 30,false); - g.fill3DRect(x+15, y, 5, 30,false); - g.fill3DRect(x+5, y+5, 10, 20,false); - g.fillOval(x+4, y+10, 10, 10); - g.drawLine(x+9, y+15, x+9, y); - + g.setColor(Color.yellow); + //向上 + x = X - 10; + y = Y - 15;//把坦克中心坐标换算成系统用来画坦克的坐标 + //(并且全是取的左上角) + + g.fill3DRect(x, y, 5, 30, false); + g.fill3DRect(x + 15, y, 5, 30, false); + g.fill3DRect(x + 5, y + 5, 10, 20, false); + g.fillOval(x + 4, y + 10, 10, 10); + g.drawLine(x + 9, y + 15, x + 9, y); + /**敌人坦克*/ g.setColor(Color.cyan); - X = 100;Y = 20; - x=X-10;y=Y-15;//把坦克中心坐标换算成系统用来画坦克的坐标 - //(并且全是取的左上角) - - g.fill3DRect(x, y, 5, 30,false); - g.fill3DRect(x+15, y, 5, 30,false); - g.fill3DRect(x+5, y+5, 10, 20,false); - g.fillOval(x+4, y+10, 10, 10); - g.drawLine(x+9, y+15, x+9, y); - + X = 100; + Y = 20; + x = X - 10; + y = Y - 15;//把坦克中心坐标换算成系统用来画坦克的坐标 + //(并且全是取的左上角) + + g.fill3DRect(x, y, 5, 30, false); + g.fill3DRect(x + 15, y, 5, 30, false); + g.fill3DRect(x + 5, y + 5, 10, 20, false); + g.fillOval(x + 4, y + 10, 10, 10); + g.drawLine(x + 9, y + 15, x + 9, y); + // MyPanel3 tank = new MyPanel3();//因为原本的MyPanel是做成了线程 莫名其妙就被调用到了 // //主坦克: // g.setColor(Color.yellow); @@ -70,40 +79,41 @@ public void paint(Graphics g){ // //敌人坦克: // g.setColor(Color.cyan); // tank.drawTank(0, 40, g, 0, 1); - } - public void run() { - while(true){ - /** - * 如果是下面的方法的话,来不及设置成0 就退出线程了,所以把设置0放在了退出线程那里更好 - */ + } + + public void run() { + while (!exit) { + /* + * 如果是下面的方法的话,来不及设置成0 就退出线程了,所以把设置0放在了退出线程那里更好 + */ // if(!hero.getisLive()){ // jl1.setText("生命值: 0"); // }else { // jl1.setText("生命值: "+hero.getLife()); // } - - if(hero.getisLive()){//生命值的自动更改 - jl1.setText(" :"+hero.getLife()+" : "+ets.size()); - }else{ - jl1.setText(" :0"+" : "+ets.size()); - } - prizeNo.setText("战绩:"+hero.getPrize()); + + if (hero.isAlive()) {//生命值的自动更改 + jl1.setText(" :" + hero.getLife() + " : " + ets.size()); + } else { + jl1.setText(" :0" + " : " + ets.size()); + } + prizeNo.setText("战绩:" + hero.getPrize()); // TankGame3.mp3.add(TankGame3.jl1 ); // System.out.println("画板执行了"); - repaint(); - - try { - Thread.sleep(20); - } catch (InterruptedException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - //不加的话线程没有退出 + repaint(); + + try { + Thread.sleep(20); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + //不加的话线程没有退出 // if(hero.getLife() <= 0){ // jl1.setText(" :0"+" : "+ets.size()); // break;//退出线程 // } - } - } + } + } } diff --git a/gui/src/main/java/com/github/kuangcp/tank/v3/PressedTwo.java b/gui/src/main/java/com/github/kuangcp/tank/v3/PressedTwo.java index ba1c1d89..c3547db9 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v3/PressedTwo.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v3/PressedTwo.java @@ -1,84 +1,76 @@ package com.github.kuangcp.tank.v3; +import com.github.kuangcp.tank.constant.DirectType; import com.github.kuangcp.tank.v1.Hero; +import com.github.kuangcp.tank.v3.thread.ExitFlagRunnable; import lombok.extern.slf4j.Slf4j; -import java.awt.event.KeyEvent; -import java.util.Objects; -import java.util.Vector; - /** - * 十分感谢网上这位的算法,使用了之后,坦克根本就不会卡,行云流水般顺畅 * 思路如下: - * 在按下键的pressed函数中创建一个该键值的对象,加入集合中, - * 在离开键Release函数中把离开的键从集合中清除 - * 另开一个线程来一直遍历这个集合,达到同时监控两个键的的动作的效果 + * 在按下键的pressed函数中 激活键 + * 在离开键Release函数中把离开的键从 取消激活 + * 另开一个线程来一直遍历 字段,达到同时监控两个键的的动作的效果 + *

* 实现结果: 一边跑,一边放子弹完全不是事儿,方向的切换也十分流畅 */ @Slf4j -public class PressedTwo implements Runnable { +public class PressedTwo implements ExitFlagRunnable { - Vector actions; Hero hero; MyPanel3 myPanel3; + ListenEventGroup eventGroup; + private volatile boolean exit = false; - public PressedTwo(Vector actions, Hero hero, MyPanel3 myPanel3) { - this.actions = actions; + public PressedTwo(ListenEventGroup eventGroup, Hero hero, MyPanel3 myPanel3) { + this.eventGroup = eventGroup; this.hero = hero; this.myPanel3 = myPanel3; } + public void exit() { + this.exit = true; + } + @Override public void run() { - while (true) { - int round = 0; - for (KE p : actions) { - if (Objects.isNull(p)) { - continue; - } + while (hero.isAlive() && !exit) { + if (eventGroup.hasPressMoveEvent()) { +// log.info("eventGroup={}", eventGroup); + } + if (eventGroup.isLeft()) { + hero.setDirect(DirectType.LEFT); + if ((hero.getX() - 10) > 20) + hero.moveleft(); + } - switch (p.keyCode) { - case KeyEvent.VK_A: - hero.setDirect(2); - if ((hero.getX() - 10) > 20) - hero.moveleft(); - break; - case KeyEvent.VK_D: - hero.setDirect(3); - if ((hero.getX() + 15) < 742) - hero.moveright(); - break; - case KeyEvent.VK_S: - hero.setDirect(1); - if ((hero.getY() - 15) < 515) - hero.movedown(); - break; - case KeyEvent.VK_W: - hero.setDirect(0); - if ((hero.getY() - 13) > 20) - hero.moveup(); - break; - case KeyEvent.VK_J: - hero.shotEnemy(); - break; - default: - break; - } - myPanel3.repaint(); - try { - Thread.sleep(40); - } catch (InterruptedException e) { - log.error("", e); - } + if (eventGroup.isRight()) { + hero.setDirect(DirectType.RIGHT); + if ((hero.getX() + 15) < 742) + hero.moveright(); + } + if (eventGroup.isDown()) { + hero.setDirect(DirectType.DOWN); + if ((hero.getY() - 15) < 515) + hero.movedown(); + } + if (eventGroup.isUp()) { + hero.setDirect(DirectType.UP); + if ((hero.getY() - 13) > 20) + hero.moveup(); } - if (!hero.getisLive()) { - break; + if (eventGroup.isShot()) { + hero.shotEnemy(); } - round++; - if (round > 10000) { - round -= 10000; + + myPanel3.repaint(); + + try { + // 动作的延迟 1000 / 77 fps + Thread.sleep(29); + } catch (InterruptedException e) { + log.error("", e); } } } diff --git a/gui/src/main/java/com/github/kuangcp/tank/v3/MyPanel301.java b/gui/src/main/java/com/github/kuangcp/tank/v3/StageActionPanel.java similarity index 56% rename from gui/src/main/java/com/github/kuangcp/tank/v3/MyPanel301.java rename to gui/src/main/java/com/github/kuangcp/tank/v3/StageActionPanel.java index f3f9b021..120bc1fb 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v3/MyPanel301.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v3/StageActionPanel.java @@ -2,18 +2,21 @@ package com.github.kuangcp.tank.v3; +import com.github.kuangcp.tank.constant.StageCommand; +import com.github.kuangcp.tank.util.Audio; +import com.github.kuangcp.tank.util.Saved; +import com.github.kuangcp.tank.util.Setting; import com.github.kuangcp.tank.v1.Brick; -import com.github.kuangcp.tank.v1.Demons; +import com.github.kuangcp.tank.v1.EnemyTank; import com.github.kuangcp.tank.v1.Hero; import com.github.kuangcp.tank.v1.Iron; import com.github.kuangcp.tank.v2.Shot; -import com.github.kuangcp.tank.util.Audio; -import com.github.kuangcp.tank.util.Saved; -import com.github.kuangcp.tank.util.Setting; +import lombok.extern.slf4j.Slf4j; import javax.swing.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.util.Objects; import java.util.Vector; /** @@ -21,62 +24,46 @@ * 并且监控按钮事件 */ @SuppressWarnings("serial") -public class MyPanel301 extends JPanel implements ActionListener { - - - static Thread t = null; - TankFrame T; +@Slf4j +public class StageActionPanel extends JPanel implements ActionListener { + static Thread actionThread = null; + TankFrame frame; Hero hero; - Vector ets; + Vector ets; Vector irons; Vector bricks; int[][] ETS; int[] myself; - Audio Begin; + Audio beginAudio; @Override public void actionPerformed(ActionEvent ae) { - //监听从按钮发来的事件 - - -// if(ae.getActionCommand().equals("开始")||ae.getActionCommand().equals("Continue")){ //上面是不合理的,会有隐藏的bug在里面,这种做法要抛弃 - if (ae.getActionCommand().equals("开始")) { - System.out.println("开始"); - T.count = 1; - T.mp.Continue = true; - Shot.setSpeed(8); - T.remove(T.jsp1); - t = new Thread(T); - - t.start();//将画板线程开启 - if (Begin != null) { - Begin.setLive(false); - } - Begin = new Audio("./src/RE/GameBegin.wav"); - Begin.start(); + if (ae.getActionCommand().equals(StageCommand.START)) { + this.startGame(); } + if (ae.getActionCommand().equals("结束")) { System.out.println("结束"); System.exit(0); } if (ae.getActionCommand().equals("暂停")) { System.out.println("暂停"); - T.mp.hero.setSpeed(0); + frame.mp.hero.setSpeed(0); Shot.setSpeed(0); - for (int i = 0; i < ets.size(); i++) { - ets.get(i).setSpeed(0); + for (EnemyTank et : ets) { + et.setSpeed(0); } - T.requestFocus(); + frame.requestFocus(); } if (ae.getActionCommand().equals("继续")) { System.out.println("继续"); - T.mp.hero.setSpeed(3); + frame.mp.hero.setSpeed(3); Shot.setSpeed(8); - for (int i = 0; i < ets.size(); i++) { - ets.get(i).setSpeed(2); + for (EnemyTank et : ets) { + et.setSpeed(2); } - T.requestFocus();//把焦点还给JFrame + frame.requestFocus();//把焦点还给JFrame } if (ae.getActionCommand().equals("exit")) { //不能把下面的saveExit口令放到这里来或,会出现先后顺序的一些错误 @@ -99,52 +86,57 @@ public void actionPerformed(ActionEvent ae) { } if (ae.getActionCommand().equals("Continue")) { //重新开启一个画板 - System.out.println("开始"); - T.count = 1; - T.mp.Continue = false; + System.out.println(StageCommand.START); + frame.firstStart = false; + frame.mp.resumePlay = false; Shot.setSpeed(8); - T.remove(T.jsp1); + frame.remove(frame.jsp1); // Saved s = new Saved(ets, hero, bricks, irons,ETS,myself); // s.readAll(); //实现一样的功能还省内存 new Saved(ets, hero, bricks, irons, ETS, myself).readDataBase(); - t = new Thread(T); + actionThread = new Thread(frame); - t.start();//将画板线程开启 + actionThread.start();//将画板线程开启 //读取 // Saved s = new Saved(ets, hero, bricks, irons); // s.readAll(); - Begin = new Audio("./src/RE/GameBegin.wav"); - Begin.start(); + beginAudio = new Audio("./src/RE/GameBegin.wav"); +// beginAudio.start(); } } + private void startGame() { + if (Objects.nonNull(actionThread) && !actionThread.isInterrupted()) { + log.info("clean last stage"); + actionThread.interrupt(); + // TODO clean + for (EnemyTank et : ets) { + et.setAlive(false); + } + } + + frame.firstStart = false; + MyPanel3.resumePlay = true; + Shot.setSpeed(8); + frame.remove(frame.jsp1); + actionThread = new Thread(frame); + actionThread.setName("actionThread"); + actionThread.start();//将画板线程开启 + if (beginAudio != null) { + beginAudio.setLive(false); + } + beginAudio = new Audio("./src/RE/GameBegin.wav"); +// beginAudio.start(); + } - public MyPanel301(TankFrame T, Hero he, Vector ets, Vector bricks, Vector irons, int[][] ETS, int[] myself) { - this.T = T; + public StageActionPanel(TankFrame frame, Hero he, Vector ets, Vector bricks, Vector irons, int[][] ETS, int[] myself) { + this.frame = frame; this.hero = he; this.ets = ets; this.bricks = bricks; this.irons = irons; this.ETS = ETS; this.myself = myself; -// System.out.println("MyPanel301的成员地址"+ets); } -} -//class P extends JPanel{ -// Image over = null; -// public P(){ -// try { -// over = ImageIO.read(getClass().getResource("/images/Over4.jpg")); -// } catch (IOException e) { -// // TODO Auto-generated catch block -// e.printStackTrace(); -// } -// } -// public void paint(Graphics g){ -// super.paint(g); -// -// g.drawImage(over,0,0,760,560,this); -// -// } -//} +} \ No newline at end of file diff --git a/gui/src/main/java/com/github/kuangcp/tank/v3/TankFrame.java b/gui/src/main/java/com/github/kuangcp/tank/v3/TankFrame.java index 81d6167f..e01829e8 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v3/TankFrame.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v3/TankFrame.java @@ -1,7 +1,10 @@ package com.github.kuangcp.tank.v3; +import com.github.kuangcp.tank.constant.StageCommand; + import javax.swing.*; import java.awt.*; +import java.util.Objects; /** * 坦克3.0 版本: @@ -27,17 +30,17 @@ * 11 有游戏的音效(操纵声音文件) */ @SuppressWarnings("serial") -public class TankFrame extends JFrame implements Runnable {//implements Runnable +public class TankFrame extends JFrame implements Runnable { public MyPanel3 mp = null;//坦克的主画板 - public MyPanel301 mp2 = null;//放按钮的画板 - public MyPanel301 mpe1 = null;//对按钮事件监听处理的 + public StageActionPanel mp2 = null;//放按钮的画板 + public StageActionPanel mpe1 = null;//对按钮事件监听处理的 public MyPanel302 mp3 = null; //显示属性的画板 public MyPanel303 Fir; public JButton jb1 = null, jb2 = null, jb3, jb4; //按钮 public JSplitPane jsp1, jsp2;//拆分窗格 public JLabel jl1 = null, jl2 = null, jl3 = null, me = null, prizeNo = null; - public int count = 0; //判断是否首次运行 + public boolean firstStart = true; //判断是否首次运行 //作出我需要的菜单 JMenuBar jmb = null; @@ -53,31 +56,34 @@ public class TankFrame extends JFrame implements Runnable {//implements Runnable //帮助窗口 JMenuItem Help = null; - // public void inter(){ -// t.interrupt(); -// } -//最外层JFrame的构造函数 -// public TankGame3_1(){ public void run() { + if (Objects.nonNull(mp)) { + mp.exit(); + } + if (Objects.nonNull(mp3)) { + mp3.exit(); + } - mp = new MyPanel3(this);//坦克运动的那个画布 - mpe1 = new MyPanel301(this, mp.hero, mp.ets, mp.bricks, mp.irons, mp.ETS, mp.myself);//监听按钮事件的画布对象 - mp2 = new MyPanel301(this, mp.hero, mp.ets, mp.bricks, mp.irons, mp.ETS, mp.myself);//放按钮的画布 + mp = new MyPanel3(); + mpe1 = new StageActionPanel(this, mp.hero, mp.enemyList, mp.bricks, mp.irons, MyPanel3.enemyTankMap, MyPanel3.myself);//监听按钮事件的画布对象 + mp2 = new StageActionPanel(this, mp.hero, mp.enemyList, mp.bricks, mp.irons, MyPanel3.enemyTankMap, MyPanel3.myself);//放按钮的画布 //提示信息 - jl1 = new JLabel(" : " + mp.hero.getLife() + " : " + mp.ets.size()); + jl1 = new JLabel(" : " + mp.hero.getLife() + " : " + mp.enemyList.size()); prizeNo = new JLabel("已击杀 :" + mp.hero.getPrize());//战绩的标签 // jl2 = new JLabel("提示:WSAD-上下左右 J-发子弹"); // jl3 = new JLabel("上面是生命值和敌人数"); me = new JLabel("Myth"); - mp3 = new MyPanel302(jl1, mp.hero, mp.ets, prizeNo);//显示一些属性 + mp3 = new MyPanel302(jl1, mp.hero, mp.enemyList, prizeNo);//显示一些属性 - if (count != 0) { + if (!firstStart) { //已经成为一个线程 要启动它 Thread t = new Thread(mp); + t.setName("windowPanel"); t.start(); Thread t2 = new Thread(mp3); + t2.setName("mp3"); t2.start(); } //创建菜单及菜单选项 @@ -109,7 +115,7 @@ public void run() { jmi2.setMnemonic('E'); //对jmil相应 jmil.addActionListener(mpe1); - jmil.setActionCommand("开始");////// + jmil.setActionCommand(StageCommand.START);////// jm1.add(jmil); // jm1.add(jmi2); @@ -132,10 +138,10 @@ public void run() { jb2.addActionListener(mpe1); //注册监听 jb2.setActionCommand("结束");///////// - if (count == 0) jb1 = new JButton("游戏开始"); + if (firstStart) jb1 = new JButton("游戏开始"); else jb1 = new JButton("重新开始"); jb1.addActionListener(mpe1); //注册监听 - jb1.setActionCommand("开始");/////////// + jb1.setActionCommand(StageCommand.START);/////////// mp2.add(jb1); mp2.add(jb2); @@ -152,11 +158,11 @@ public void run() { mp3.add(me); // jl1 =new JLabel("898908098");//不会对上面造成任何影响 - jsp2 = new JSplitPane(0, mp2, mp3);//水平 + jsp2 = new JSplitPane(JSplitPane.VERTICAL_SPLIT, mp2, mp3);//水平 jsp2.setDividerLocation(150); Fir = new MyPanel303(); - if (count != 0) jsp1 = new JSplitPane(1, mp, jsp2);//垂直 - else jsp1 = new JSplitPane(1, Fir, jsp2); + if (!firstStart) jsp1 = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, mp, jsp2);//垂直 + else jsp1 = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, Fir, jsp2); jsp1.setDividerLocation(760); //把画板加入JFrame @@ -170,22 +176,12 @@ public void run() { this.setJMenuBar(jmb); //焦点跳转 tab切换 this.setFocusable(getFocusTraversalKeysEnabled()); -/**JFrame 窗体的属性*/ - + //JFrame 窗体的属性 this.setTitle("Tank"); this.setLocation(150, 60); this.setSize(1000, 625); this.setVisible(true); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - } - - -// @Override -// public void run() { -// // TODO Auto-generated method stub -// -// } - } \ No newline at end of file diff --git a/gui/src/main/java/com/github/kuangcp/tank/v3/resource/AvatarMgr.java b/gui/src/main/java/com/github/kuangcp/tank/v3/resource/AvatarMgr.java new file mode 100644 index 00000000..0bddcc44 --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/v3/resource/AvatarMgr.java @@ -0,0 +1,33 @@ +package com.github.kuangcp.tank.v3.resource; + +import lombok.extern.slf4j.Slf4j; + +import javax.imageio.ImageIO; +import java.awt.*; +import java.io.IOException; + +/** + * @author https://github.com/kuangcp on 2021-09-11 16:41 + */ +@Slf4j +public class AvatarMgr { + + public static AvatarMgr instance = new AvatarMgr(); + + Image[] avatarArr = new Image[5]; + + public Image curImg = null; + + public void initImg() { + try { + avatarArr[0] = ImageIO.read(getClass().getResourceAsStream("/images/Me.jpg")); + avatarArr[1] = ImageIO.read(getClass().getResourceAsStream("/images/Me2.jpg")); + avatarArr[2] = ImageIO.read(getClass().getResourceAsStream("/images/Me3.jpg")); + avatarArr[3] = ImageIO.read(getClass().getResourceAsStream("/images/Me4.jpg")); + avatarArr[4] = ImageIO.read(getClass().getResourceAsStream("/images/Me5.jpg")); + } catch (IOException e) { + log.error("", e); + } + curImg = avatarArr[(int) (Math.random() * 5)]; + } +} diff --git a/gui/src/main/java/com/github/kuangcp/tank/v3/Bomb.java b/gui/src/main/java/com/github/kuangcp/tank/v3/resource/Bomb.java similarity index 83% rename from gui/src/main/java/com/github/kuangcp/tank/v3/Bomb.java rename to gui/src/main/java/com/github/kuangcp/tank/v3/resource/Bomb.java index fb0d921f..851f141c 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v3/Bomb.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v3/resource/Bomb.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.tank.v3; +package com.github.kuangcp.tank.v3.resource; /** * 1 准备好图片 @@ -11,10 +11,10 @@ */ public class Bomb { - int bx, by; + public int bx, by; //炸弹的生命 - int life = 15; - boolean isLive = true; + public int life = 15; + public boolean isLive = true; public Bomb(int bx, int by) { this.bx = bx; @@ -29,7 +29,6 @@ public void lifeDown() { if (life > 0) { life--; } else { - this.isLive = false; } } diff --git a/gui/src/main/java/com/github/kuangcp/tank/v3/resource/BombMgr.java b/gui/src/main/java/com/github/kuangcp/tank/v3/resource/BombMgr.java new file mode 100644 index 00000000..e55a1f1f --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/v3/resource/BombMgr.java @@ -0,0 +1,131 @@ +package com.github.kuangcp.tank.v3.resource; + +import com.github.kuangcp.tank.constant.DirectType; +import com.github.kuangcp.tank.v1.Hero; +import com.github.kuangcp.tank.v1.Tank; +import com.github.kuangcp.tank.v2.Shot; +import lombok.extern.slf4j.Slf4j; + +import javax.imageio.ImageIO; +import javax.swing.*; +import java.awt.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * @author https://github.com/kuangcp on 2021-09-11 16:28 + */ +@Slf4j +public class BombMgr { + + public static BombMgr instance = new BombMgr(); + + public Image[] bombArr = new Image[3]; + //定义炸弹爆炸集合 + public List bombs = Collections.synchronizedList(new ArrayList<>()); + + public void initImg() { + try { + // 爆炸三阶段 + for (int i = 1; i <= 3; i++) { + bombArr[i - 1] = ImageIO.read(getClass().getResourceAsStream("/images/bomb_" + i + ".gif")); + } +// bombArr[0] = ImageIO.read(getClass().getResourceAsStream("/images/bomb_1.gif")); +// bombArr[1] = ImageIO.read(getClass().getResourceAsStream("/images/bomb_2.gif")); +// bombArr[2] = ImageIO.read(getClass().getResourceAsStream("/images/bomb_3.gif")); + } catch (Exception e) { + log.error("", e); + } + } + + /** + * 画出炸弹 + */ + public void drawBomb(Graphics g, JPanel panel) { + for (int i = 0; i < bombs.size(); i++) { + //取出炸弹 + Bomb b = bombs.get(i); +// System.out.println("size = "+bombs.size()); + if (b.life > 10) { + g.drawImage(BombMgr.instance.bombArr[0], b.bx, b.by, 30, 30, panel); +// if(rr){ +// Audio B = new Audio("./src/RE/GameBegin.wav"); +// B.start(); +// rr = false; +// } + } else if (b.life > 5) { + g.drawImage(BombMgr.instance.bombArr[1], b.bx, b.by, 30, 30, panel); + } else { + g.drawImage(BombMgr.instance.bombArr[2], b.bx, b.by, 30, 30, panel); + } + //让b的生命减少 + b.lifeDown(); + + //炸弹的生命值为零 移出集合 + if (b.life == 0) { + bombs.remove(b); + + } +// hero.LifeDown(); +// System.out.println("size = "+bombs.size()); + } + } + + /** + * 工具类-检测爆炸的函数 + */ + public void checkBong(Tank tank, List shots) { + shots.forEach(v -> checkBong(tank, v)); + } + + private void checkBong(Tank t, Shot s) { + if (!s.isLive) { + return; + } + + switch (t.getDirect()) { + case DirectType.UP: + case DirectType.DOWN: + if (t.getX() - 10 <= s.sx && + t.getX() + 10 >= s.sx && + t.getY() - 15 <= s.sy && + t.getY() + 15 >= s.sy) { + s.isLive = false; + t.setLife(t.getLife() - 1);//生命值减一 + if (t.getLife() == 0) t.setAlive(false); + //创建一个炸弹,放入集合 + Bomb b = new Bomb(t.getX() - 10, t.getY() - 15);//敌方的坐标 + bombs.add(b); + if (t instanceof Hero) { + t.setX(480); + t.setY(500); + t.setDirect(0); + } + } + break; + case DirectType.LEFT: + case DirectType.RIGHT: + if (t.getX() - 15 <= s.sx && + t.getX() + 15 >= s.sx && + t.getY() - 10 <= s.sy && + t.getY() + 10 >= s.sy) { + s.isLive = false; + t.setLife(t.getLife() - 1); + + if (t.getLife() == 0) t.setAlive(false); + + //创建一个炸弹,放入集合 + Bomb b = new Bomb(t.getX() - 15, t.getY() - 10);//敌方的坐标 + bombs.add(b); + if (t instanceof Hero) { + t.setX(480); + t.setY(500); + t.setDirect(0); + } + } + break; + } + } + +} diff --git a/gui/src/main/java/com/github/kuangcp/tank/v3/thread/ExitFlagRunnable.java b/gui/src/main/java/com/github/kuangcp/tank/v3/thread/ExitFlagRunnable.java new file mode 100644 index 00000000..11768d7b --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/v3/thread/ExitFlagRunnable.java @@ -0,0 +1,9 @@ +package com.github.kuangcp.tank.v3.thread; + +/** + * @author https://github.com/kuangcp on 2021-09-11 18:33 + */ +public interface ExitFlagRunnable extends Runnable { + + void exit(); +} diff --git a/java8/src/test/java/com/github/kuangcp/future/CompletableFutureTest.java b/java8/src/test/java/com/github/kuangcp/future/CompletableFutureTest.java index 12df4ecd..23b27af9 100644 --- a/java8/src/test/java/com/github/kuangcp/future/CompletableFutureTest.java +++ b/java8/src/test/java/com/github/kuangcp/future/CompletableFutureTest.java @@ -1,70 +1,109 @@ package com.github.kuangcp.future; -import static org.hamcrest.Matchers.equalTo; -import static org.junit.Assert.assertThat; +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; -import lombok.extern.slf4j.Slf4j; -import org.junit.Test; + +import static org.hamcrest.Matchers.equalTo; +import static org.junit.Assert.assertThat; @Slf4j public class CompletableFutureTest { - @Test - public void testAsync() throws Exception { - CompletableFuture future = CompletableFuture.runAsync(() -> System.out.println("temp")); + @Test + public void testAsync() throws Exception { + CompletableFuture future = CompletableFuture.runAsync(() -> { + log.info("sleep"); + try { + TimeUnit.SECONDS.sleep(2); + } catch (InterruptedException e) { + e.printStackTrace(); + } + log.info("temp"); + }); + + future.get(5, TimeUnit.SECONDS); + if (future.isDone()) { + log.info("complete"); + } + } + + /** + * @see java.util.concurrent.CompletableFuture.asyncPool + */ + @Test + public void testAsyncPoolSize() throws Exception { + List> wait = new ArrayList<>(); + + for (int i = 0; i < 300; i++) { + CompletableFuture future = CompletableFuture.runAsync(() -> { + log.info("sleep"); + try { + TimeUnit.SECONDS.sleep(2); + } catch (InterruptedException e) { + e.printStackTrace(); + } + log.info("temp"); + }); + wait.add(future); + } + for (Future future : wait) { + future.get(); + if (future.isDone()) { + log.info("complete"); + } + } + } + + @Test + public void testFutureNeedOptimize() throws Exception { + ExecutorService executorService = Executors.newFixedThreadPool(10); + int num = 3; + + CompletableFuture func1Future = new CompletableFuture<>(); + executorService.submit(() -> func1Future.complete(func1(num))); + int b = func2(num); + int result = b + func1Future.get(); + System.out.println(result); + assertThat(result, equalTo(9)); + executorService.shutdown(); + } + + @Test + public void testFutureAndCombine() throws Exception { + ExecutorService executorService = Executors.newFixedThreadPool(10); + int num = 3; + + CompletableFuture func1Future = new CompletableFuture<>(); + CompletableFuture func2Future = new CompletableFuture<>(); + + // 只会在两个函数都完成计算后进行 避免get() 产生阻塞,并发的损失和死锁问题 + // CompletableFuture 和 结合器 一起使用 + CompletableFuture resultFuture = func1Future.thenCombine(func2Future, (a, b) -> a + b); + + executorService.submit(() -> func1Future.complete(func1(num))); + executorService.submit(() -> func2Future.complete(func2(num))); + + Integer result = resultFuture.get(); + System.out.println(result); + assertThat(result, equalTo(9)); + + executorService.shutdown(); + } + + private int func1(int value) { + return value + 1; + } - future.get(5, TimeUnit.SECONDS); - if (future.isDone()) { - System.out.println("complete"); + private int func2(int value) { + return value + 2; } - } - - @Test - public void testFutureNeedOptimize() throws Exception { - ExecutorService executorService = Executors.newFixedThreadPool(10); - int num = 3; - - CompletableFuture func1Future = new CompletableFuture<>(); - executorService.submit(() -> func1Future.complete(func1(num))); - int b = func2(num); - int result = b + func1Future.get(); - System.out.println(result); - assertThat(result, equalTo(9)); - executorService.shutdown(); - } - - @Test - public void testFutureAndCombine() throws Exception { - ExecutorService executorService = Executors.newFixedThreadPool(10); - int num = 3; - - CompletableFuture func1Future = new CompletableFuture<>(); - CompletableFuture func2Future = new CompletableFuture<>(); - - // 只会在两个函数都完成计算后进行 避免get() 产生阻塞,并发的损失和死锁问题 - // CompletableFuture 和 结合器 一起使用 - CompletableFuture resultFuture = func1Future.thenCombine(func2Future, (a, b) -> a + b); - - executorService.submit(() -> func1Future.complete(func1(num))); - executorService.submit(() -> func2Future.complete(func2(num))); - - Integer result = resultFuture.get(); - System.out.println(result); - assertThat(result, equalTo(9)); - - executorService.shutdown(); - } - - private int func1(int value) { - return value + 1; - } - - private int func2(int value) { - return value + 2; - } } From 47e8b1a47e9e9fd50747660220016f6fa0f673bf Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Sat, 11 Sep 2021 23:11:38 +0800 Subject: [PATCH 214/476] x) fix delay remove enemy tank --- .../kuangcp/tank/{v1 => domain}/Brick.java | 4 +- .../tank/{v1 => domain}/EnemyTank.java | 55 ++++---- .../kuangcp/tank/{v1 => domain}/Hero.java | 23 ++-- .../kuangcp/tank/{v1 => domain}/Hinder.java | 14 +- .../kuangcp/tank/{v1 => domain}/Iron.java | 2 +- .../kuangcp/tank/{v2 => domain}/Shot.java | 22 +-- .../kuangcp/tank/{v1 => domain}/Tank.java | 6 +- .../HeroInfoPanel.java} | 35 +++-- .../tank/{v3 => panel}/StageActionPanel.java | 36 ++--- .../kuangcp/tank/panel/StarterPanel.java | 40 ++++++ .../TankGroundPanel.java} | 115 ++++++++-------- .../tank/{v3 => }/resource/AvatarMgr.java | 2 +- .../kuangcp/tank/{v3 => }/resource/Bomb.java | 2 +- .../tank/{v3 => }/resource/BombMgr.java | 10 +- .../{v3 => }/thread/ExitFlagRunnable.java | 2 +- .../github/kuangcp/tank/util/ExecutePool.java | 4 + .../PressedTwo.java => util/KeyListener.java} | 17 +-- .../tank/{v3 => util}/ListenEventGroup.java | 6 +- .../com/github/kuangcp/tank/util/Saved.java | 8 +- .../com/github/kuangcp/tank/util/Setting.java | 108 --------------- .../github/kuangcp/tank/util/TankTool.java | 21 ++- .../com/github/kuangcp/tank/v1/TankGame1.java | 4 + .../com/github/kuangcp/tank/v2/MyPanel.java | 11 +- .../v3/{TankFrame.java => MainFrame.java} | 53 ++++---- .../github/kuangcp/tank/v3/MainTankGame.java | 11 +- .../github/kuangcp/tank/v3/MyPanel303.java | 41 ------ .../github/kuangcp/tank/v3/SettingFrame.java | 125 ++++++++++++++++++ gui/src/main/resources/logback.xml | 36 +++++ 28 files changed, 447 insertions(+), 366 deletions(-) rename gui/src/main/java/com/github/kuangcp/tank/{v1 => domain}/Brick.java (82%) rename gui/src/main/java/com/github/kuangcp/tank/{v1 => domain}/EnemyTank.java (92%) rename gui/src/main/java/com/github/kuangcp/tank/{v1 => domain}/Hero.java (90%) rename gui/src/main/java/com/github/kuangcp/tank/{v1 => domain}/Hinder.java (69%) rename gui/src/main/java/com/github/kuangcp/tank/{v1 => domain}/Iron.java (74%) rename gui/src/main/java/com/github/kuangcp/tank/{v2 => domain}/Shot.java (71%) rename gui/src/main/java/com/github/kuangcp/tank/{v1 => domain}/Tank.java (92%) rename gui/src/main/java/com/github/kuangcp/tank/{v3/MyPanel302.java => panel/HeroInfoPanel.java} (85%) rename gui/src/main/java/com/github/kuangcp/tank/{v3 => panel}/StageActionPanel.java (83%) create mode 100644 gui/src/main/java/com/github/kuangcp/tank/panel/StarterPanel.java rename gui/src/main/java/com/github/kuangcp/tank/{v3/MyPanel3.java => panel/TankGroundPanel.java} (86%) rename gui/src/main/java/com/github/kuangcp/tank/{v3 => }/resource/AvatarMgr.java (95%) rename gui/src/main/java/com/github/kuangcp/tank/{v3 => }/resource/Bomb.java (94%) rename gui/src/main/java/com/github/kuangcp/tank/{v3 => }/resource/BombMgr.java (94%) rename gui/src/main/java/com/github/kuangcp/tank/{v3 => }/thread/ExitFlagRunnable.java (76%) rename gui/src/main/java/com/github/kuangcp/tank/{v3/PressedTwo.java => util/KeyListener.java} (80%) rename gui/src/main/java/com/github/kuangcp/tank/{v3 => util}/ListenEventGroup.java (93%) delete mode 100644 gui/src/main/java/com/github/kuangcp/tank/util/Setting.java rename gui/src/main/java/com/github/kuangcp/tank/v3/{TankFrame.java => MainFrame.java} (77%) delete mode 100644 gui/src/main/java/com/github/kuangcp/tank/v3/MyPanel303.java create mode 100644 gui/src/main/java/com/github/kuangcp/tank/v3/SettingFrame.java create mode 100644 gui/src/main/resources/logback.xml diff --git a/gui/src/main/java/com/github/kuangcp/tank/v1/Brick.java b/gui/src/main/java/com/github/kuangcp/tank/domain/Brick.java similarity index 82% rename from gui/src/main/java/com/github/kuangcp/tank/v1/Brick.java rename to gui/src/main/java/com/github/kuangcp/tank/domain/Brick.java index b5cde85f..0a9de77e 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v1/Brick.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/Brick.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.tank.v1; +package com.github.kuangcp.tank.domain; import java.awt.*; @@ -20,7 +20,7 @@ public Brick(int hx, int hy) { public Brick() { } //被舍弃 不使用 -/**画出砖块 20*10大小 为了便于使用 就直接(x,y)到(x,y)画出矩形 虽然实现了但是不利于控制死亡*/ + /*画出砖块 20*10大小 为了便于使用 就直接(x,y)到(x,y)画出矩形 虽然实现了但是不利于控制死亡*/ /*public void d(Graphics g,Vector bricks,int startX,int startY,int endX,int endY){ // g.setColor(Color.LIGHT_GRAY);//钢板的颜色 diff --git a/gui/src/main/java/com/github/kuangcp/tank/v1/EnemyTank.java b/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java similarity index 92% rename from gui/src/main/java/com/github/kuangcp/tank/v1/EnemyTank.java rename to gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java index 564d7e80..f26e9675 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v1/EnemyTank.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java @@ -1,11 +1,10 @@ -package com.github.kuangcp.tank.v1; +package com.github.kuangcp.tank.domain; import com.github.kuangcp.tank.constant.DirectType; import com.github.kuangcp.tank.util.ExecutePool; import com.github.kuangcp.tank.util.TankTool; -import com.github.kuangcp.tank.v2.Shot; import lombok.extern.slf4j.Slf4j; import java.util.Vector; @@ -28,7 +27,7 @@ public class EnemyTank extends Tank implements Runnable { public Vector bricks; public Vector irons; public Shot s = null; - public int maxLiveShot = 7; //子弹线程存活的最大数 + public int maxLiveShot = 10; //子弹线程存活的最大数 public boolean delayRemove = false; Hero hero; @@ -304,20 +303,20 @@ public boolean toUp() { if (y > 30) { to = true; for (EnemyTank et : ets) { - if (!TankTool.hasHint(this, et)) { + if (!TankTool.ablePass(this, et)) { to = (false); break; } } for (Brick brick : bricks) { - if (TankTool.hasHint(this, brick)) + if (TankTool.ablePass(this, brick)) to = false; } for (Iron iron : irons) { - if (TankTool.hasHint(this, iron)) + if (TankTool.ablePass(this, iron)) to = false; } - if (to && TankTool.hasHint(this, hero)) { + if (to && TankTool.ablePass(this, hero)) { y -= speed; try { Thread.sleep(100); @@ -336,20 +335,20 @@ public boolean toDown() { if (y < 530) { to = true; for (EnemyTank et : ets) { - if (!TankTool.hasHint(this, et)) { + if (!TankTool.ablePass(this, et)) { to = (false); break; } } for (Brick brick : bricks) { - if (TankTool.hasHint(this, brick)) + if (TankTool.ablePass(this, brick)) to = false; } for (Iron iron : irons) { - if (TankTool.hasHint(this, iron)) + if (TankTool.ablePass(this, iron)) to = false; } - if (to && TankTool.hasHint(this, hero)) { + if (to && TankTool.ablePass(this, hero)) { y += speed; try { Thread.sleep(100); @@ -369,20 +368,20 @@ public boolean toLeft() { if (x > 30) { to = true; for (EnemyTank et : ets) { - if (!TankTool.hasHint(this, et)) { + if (!TankTool.ablePass(this, et)) { to = false; break; } } for (Brick brick : bricks) { - if (TankTool.hasHint(this, brick)) + if (TankTool.ablePass(this, brick)) to = false; } for (Iron iron : irons) { - if (TankTool.hasHint(this, iron)) + if (TankTool.ablePass(this, iron)) to = false; } - if (to && TankTool.hasHint(this, hero)) { + if (to && TankTool.ablePass(this, hero)) { x -= speed; try { Thread.sleep(100); @@ -401,20 +400,20 @@ public boolean toRight() { if (x < 710) { to = true; for (EnemyTank et : ets) { - if (!TankTool.hasHint(this, et)) { + if (!TankTool.ablePass(this, et)) { to = (false); break; } } for (Brick brick : bricks) { - if (TankTool.hasHint(this, brick)) + if (TankTool.ablePass(this, brick)) to = false; } for (Iron iron : irons) { - if (TankTool.hasHint(this, iron)) + if (TankTool.ablePass(this, iron)) to = false; } - if (to && TankTool.hasHint(this, hero)) { + if (to && TankTool.ablePass(this, hero)) { x += speed; try { Thread.sleep(100); @@ -433,12 +432,20 @@ public boolean toRight() { //重写 int min = 0; + @Override public void run() { // actionModeRun(); run2(); + } + /** + * 必须执行,但是只能推迟到子弹线程结束后回收 + * + * @see com.github.kuangcp.tank.domain.EnemyTank#run 不能置于该方法内 + */ + public void cleanResource() { // 回收 - log.info("{} clean enemy", id); +// log.info("{} clean enemy", id); shotExecutePool.shutdownNow(); } @@ -503,7 +510,7 @@ public void run2() { min++; // if(!bri)this.direct = (int)(Math.random()*4); if (y > 30) { - if (TankTool.hasHint(this, hero) && with) y -= speed; + if (TankTool.ablePass(this, hero) && with) y -= speed; // if(bri)y-=speed; // else {y+=speed;this.direct = 1;} // else continue; @@ -527,7 +534,7 @@ public void run2() { // if(!bri)this.direct = (int)(Math.random()*4); if (y < 530) { - if (TankTool.hasHint(this, hero) && with && bri) y += speed; + if (TankTool.ablePass(this, hero) && with && bri) y += speed; // if(bri) y+=speed; // else {y-=speed;this.direct = 0;} // else continue; @@ -550,7 +557,7 @@ public void run2() { // if(!bri)this.direct = (int)(Math.random()*4); if (x > 30) { - if (TankTool.hasHint(this, hero) && with && bri) x -= speed; + if (TankTool.ablePass(this, hero) && with && bri) x -= speed; // if(bri)x-=speed; // else{x+=speed;this.direct = 3;} // else continue; @@ -573,7 +580,7 @@ public void run2() { // if(!bri)this.direct = (int)(Math.random()*4); if (x < 710) { - if (TankTool.hasHint(this, hero) && with && bri) { + if (TankTool.ablePass(this, hero) && with && bri) { x += speed; } // if(bri)x+=speed; diff --git a/gui/src/main/java/com/github/kuangcp/tank/v1/Hero.java b/gui/src/main/java/com/github/kuangcp/tank/domain/Hero.java similarity index 90% rename from gui/src/main/java/com/github/kuangcp/tank/v1/Hero.java rename to gui/src/main/java/com/github/kuangcp/tank/domain/Hero.java index 2561b81f..ea4fa192 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v1/Hero.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/Hero.java @@ -1,9 +1,8 @@ -package com.github.kuangcp.tank.v1; +package com.github.kuangcp.tank.domain; import com.github.kuangcp.tank.util.ExecutePool; import com.github.kuangcp.tank.util.TankTool; -import com.github.kuangcp.tank.v2.Shot; import java.awt.*; import java.util.Vector; @@ -12,7 +11,7 @@ public class Hero extends Tank { //子弹集合 - public Vector ss = new Vector<>(); + public Vector shotList = new Vector<>(); private final ExecutorService shotExecutePool; private long lastShotNs = 0; private long shotCD = 224_000_000; @@ -82,7 +81,7 @@ public void shotEnemy() { if (lastShotNs != 0 && curNs - lastShotNs < shotCD) { return; } - if (this.ss.size() >= this.maxLiveShot || !this.isAlive()) { + if (this.shotList.size() >= this.maxLiveShot || !this.isAlive()) { return; } @@ -90,19 +89,19 @@ public void shotEnemy() { switch (this.getDirect()) { case 0://0123 代表 上下左右 s = new Shot(this.getX() - 1, this.getY() - 15, 0); - ss.add(s); + shotList.add(s); break; case 1: s = new Shot(this.getX() - 2, this.getY() + 15, 1); - ss.add(s); + shotList.add(s); break; case 2: s = new Shot(this.getX() - 15 - 2, this.getY(), 2); - ss.add(s); + shotList.add(s); break; case 3: s = new Shot(this.getX() + 15 - 2, this.getY() - 1, 3); - ss.add(s); + shotList.add(s); break; } //启动子弹线程 @@ -115,7 +114,7 @@ public void moveup() { to = true; //检测敌方坦克碰撞 for (EnemyTank et : ets) { - if (!TankTool.hasHint(this, et)) + if (!TankTool.ablePass(this, et)) to = false; } //检测障碍物 @@ -135,7 +134,7 @@ public void moveup() { public void movedown() { to = true; for (EnemyTank et : ets) - if (!TankTool.hasHint(this, et)) + if (!TankTool.ablePass(this, et)) to = false; // for (Brick brick : bricks) { // if (TankTool.hasHint(this, brick)) @@ -152,7 +151,7 @@ public void movedown() { public void moveleft() { to = true; for (EnemyTank et : ets) - if (!TankTool.hasHint(this, et)) + if (!TankTool.ablePass(this, et)) to = false; // for (Brick brick : bricks) { // if (TankTool.hasHint(this, brick)) @@ -169,7 +168,7 @@ public void moveleft() { public void moveright() { to = true; for (EnemyTank et : ets) - if (!TankTool.hasHint(this, et)) + if (!TankTool.ablePass(this, et)) to = false; // for (Brick brick : bricks) { // if (TankTool.hasHint(this, brick)) diff --git a/gui/src/main/java/com/github/kuangcp/tank/v1/Hinder.java b/gui/src/main/java/com/github/kuangcp/tank/domain/Hinder.java similarity index 69% rename from gui/src/main/java/com/github/kuangcp/tank/v1/Hinder.java rename to gui/src/main/java/com/github/kuangcp/tank/domain/Hinder.java index 38a311d3..3f04f556 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v1/Hinder.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/Hinder.java @@ -1,14 +1,14 @@ -package com.github.kuangcp.tank.v1; +package com.github.kuangcp.tank.domain; /** * 障碍物的最基本类 */ public abstract class Hinder { int hx, hy;//障碍物坐标 绘图坐标 也就是左上角的坐标 - boolean Live;//生存状态 + boolean alive;//生存状态 public Hinder(int hx, int hy) { - Live = true; + alive = true; this.hx = hx; this.hy = hy; @@ -17,12 +17,12 @@ public Hinder(int hx, int hy) { public Hinder() { } - public Boolean getLive() { - return Live; + public Boolean getAlive() { + return alive; } - public void setLive(Boolean live) { - Live = live; + public void setAlive(Boolean alive) { + this.alive = alive; } public int getHx() { diff --git a/gui/src/main/java/com/github/kuangcp/tank/v1/Iron.java b/gui/src/main/java/com/github/kuangcp/tank/domain/Iron.java similarity index 74% rename from gui/src/main/java/com/github/kuangcp/tank/v1/Iron.java rename to gui/src/main/java/com/github/kuangcp/tank/domain/Iron.java index 11cca711..d74f5d8d 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v1/Iron.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/Iron.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.tank.v1; +package com.github.kuangcp.tank.domain; /** * 铁块 diff --git a/gui/src/main/java/com/github/kuangcp/tank/v2/Shot.java b/gui/src/main/java/com/github/kuangcp/tank/domain/Shot.java similarity index 71% rename from gui/src/main/java/com/github/kuangcp/tank/v2/Shot.java rename to gui/src/main/java/com/github/kuangcp/tank/domain/Shot.java index 297a1ee6..1c86781c 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v2/Shot.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/Shot.java @@ -1,5 +1,6 @@ -package com.github.kuangcp.tank.v2; +package com.github.kuangcp.tank.domain; +import com.github.kuangcp.tank.util.TankTool; import lombok.extern.slf4j.Slf4j; /** @@ -11,7 +12,7 @@ public class Shot implements Runnable { public int sx; public int sy; public int direct; - public static int speed = 8;//如果改动要记得按钮事件里也要改 + public static int speed = 3;//如果改动要记得按钮事件里也要改 public boolean isLive = true;//是否还活着 public static int getSpeed() { @@ -29,21 +30,9 @@ public Shot(int sx, int sy, int direct) { } public void run() { - //延迟子弹发射时间 - try { - Thread.sleep(20); - } catch (Exception e) { - log.error("", e); - } - do { - try { - Thread.sleep(50); //每个子弹发射的延迟运动的时间 - } catch (InterruptedException i) { - break; - } catch (Exception e) { - log.error("", e); - } + // 每个子弹发射的延迟运动的时间 + TankTool.yieldMsTime(55); switch (direct) { //上下左右 @@ -65,6 +54,7 @@ public void run() { if (sx < 20 || sx > 740 || sy < 20 || sy > 540) { this.isLive = false; } + if (sx < 440 && sx > 380 && sy < 540 && sy > 480) { this.isLive = false; } diff --git a/gui/src/main/java/com/github/kuangcp/tank/v1/Tank.java b/gui/src/main/java/com/github/kuangcp/tank/domain/Tank.java similarity index 92% rename from gui/src/main/java/com/github/kuangcp/tank/v1/Tank.java rename to gui/src/main/java/com/github/kuangcp/tank/domain/Tank.java index dda2ae7a..27f933e2 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v1/Tank.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/Tank.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.tank.v1; +package com.github.kuangcp.tank.domain; /** * 最起初的坦克类 @@ -68,10 +68,6 @@ public int getY() { public void setY(int y) { this.y = y; } -// 判断子弹是否击中坦克 -// public void Bong(){ -// -// } // 构造器 public Tank(int x, int y, int speed) { diff --git a/gui/src/main/java/com/github/kuangcp/tank/v3/MyPanel302.java b/gui/src/main/java/com/github/kuangcp/tank/panel/HeroInfoPanel.java similarity index 85% rename from gui/src/main/java/com/github/kuangcp/tank/v3/MyPanel302.java rename to gui/src/main/java/com/github/kuangcp/tank/panel/HeroInfoPanel.java index 423d4f01..3d9cb44c 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v3/MyPanel302.java +++ b/gui/src/main/java/com/github/kuangcp/tank/panel/HeroInfoPanel.java @@ -1,26 +1,26 @@ -/** - * 就是一个用来显示属性的画板 - * 不停的刷新 - * JLabel 被add后 就算他发生改变,对之前已经add了的组件 不会有影响 - * 但是可以用setText来进行改变 - */ -package com.github.kuangcp.tank.v3; +package com.github.kuangcp.tank.panel; -import com.github.kuangcp.tank.v1.EnemyTank; -import com.github.kuangcp.tank.v1.Hero; -import com.github.kuangcp.tank.v3.thread.ExitFlagRunnable; +import com.github.kuangcp.tank.domain.EnemyTank; +import com.github.kuangcp.tank.domain.Hero; +import com.github.kuangcp.tank.thread.ExitFlagRunnable; +import lombok.extern.slf4j.Slf4j; import javax.swing.*; import java.awt.*; import java.util.Vector; +/** + * 就是一个用来显示属性的画板 + * JLabel 被add后 就算他发生改变,对之前已经add了的组件 不会有影响 + * 但是可以用setText来进行改变 + */ +@Slf4j @SuppressWarnings("serial") -public class MyPanel302 extends JPanel implements ExitFlagRunnable { +public class HeroInfoPanel extends JPanel implements ExitFlagRunnable { JLabel jl1; JLabel prizeNo; - MyPanel302 mp = null; Hero hero; Vector ets; @@ -30,7 +30,7 @@ public void exit() { this.exit = true; } - public MyPanel302(JLabel jl1, Hero hero, Vector ets, JLabel prizeNo) { + public HeroInfoPanel(JLabel jl1, Hero hero, Vector ets, JLabel prizeNo) { this.jl1 = jl1; this.hero = hero; this.ets = ets; @@ -45,7 +45,7 @@ public void paint(Graphics g) { int X = 20, Y = 20; int x, y; -/**主坦克*/ + /*主坦克*/ g.setColor(Color.yellow); //向上 x = X - 10; @@ -58,7 +58,7 @@ public void paint(Graphics g) { g.fillOval(x + 4, y + 10, 10, 10); g.drawLine(x + 9, y + 15, x + 9, y); -/**敌人坦克*/ + /*敌人坦克*/ g.setColor(Color.cyan); X = 100; Y = 20; @@ -103,10 +103,9 @@ public void run() { repaint(); try { - Thread.sleep(20); + Thread.sleep(200); } catch (InterruptedException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + log.error("", e); } //不加的话线程没有退出 // if(hero.getLife() <= 0){ diff --git a/gui/src/main/java/com/github/kuangcp/tank/v3/StageActionPanel.java b/gui/src/main/java/com/github/kuangcp/tank/panel/StageActionPanel.java similarity index 83% rename from gui/src/main/java/com/github/kuangcp/tank/v3/StageActionPanel.java rename to gui/src/main/java/com/github/kuangcp/tank/panel/StageActionPanel.java index 120bc1fb..69eb8011 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v3/StageActionPanel.java +++ b/gui/src/main/java/com/github/kuangcp/tank/panel/StageActionPanel.java @@ -1,19 +1,21 @@ -package com.github.kuangcp.tank.v3; +package com.github.kuangcp.tank.panel; import com.github.kuangcp.tank.constant.StageCommand; +import com.github.kuangcp.tank.domain.Brick; +import com.github.kuangcp.tank.domain.EnemyTank; +import com.github.kuangcp.tank.domain.Hero; +import com.github.kuangcp.tank.domain.Iron; +import com.github.kuangcp.tank.domain.Shot; import com.github.kuangcp.tank.util.Audio; import com.github.kuangcp.tank.util.Saved; -import com.github.kuangcp.tank.util.Setting; -import com.github.kuangcp.tank.v1.Brick; -import com.github.kuangcp.tank.v1.EnemyTank; -import com.github.kuangcp.tank.v1.Hero; -import com.github.kuangcp.tank.v1.Iron; -import com.github.kuangcp.tank.v2.Shot; +import com.github.kuangcp.tank.v3.MainFrame; +import com.github.kuangcp.tank.v3.SettingFrame; import lombok.extern.slf4j.Slf4j; import javax.swing.*; +import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.Objects; @@ -27,7 +29,7 @@ @Slf4j public class StageActionPanel extends JPanel implements ActionListener { static Thread actionThread = null; - TankFrame frame; + MainFrame frame; Hero hero; Vector ets; Vector irons; @@ -40,7 +42,7 @@ public class StageActionPanel extends JPanel implements ActionListener { public void actionPerformed(ActionEvent ae) { //上面是不合理的,会有隐藏的bug在里面,这种做法要抛弃 if (ae.getActionCommand().equals(StageCommand.START)) { - this.startGame(); + this.startNewStage(); } if (ae.getActionCommand().equals("结束")) { @@ -73,7 +75,7 @@ public void actionPerformed(ActionEvent ae) { } if (ae.getActionCommand().equals("Help")) { - Setting se = new Setting(hero); + SettingFrame.activeFocus(hero); } if (ae.getActionCommand().equals("saveExit")) { System.out.println("按下了 保存并退出 按钮"); @@ -88,16 +90,18 @@ public void actionPerformed(ActionEvent ae) { //重新开启一个画板 System.out.println(StageCommand.START); frame.firstStart = false; - frame.mp.resumePlay = false; + TankGroundPanel.newStage = false; Shot.setSpeed(8); frame.remove(frame.jsp1); // Saved s = new Saved(ets, hero, bricks, irons,ETS,myself); // s.readAll(); //实现一样的功能还省内存 new Saved(ets, hero, bricks, irons, ETS, myself).readDataBase(); - actionThread = new Thread(frame); - actionThread.start();//将画板线程开启 + EventQueue.invokeLater(frame); +// actionThread = new Thread(frame); +// actionThread.start(); + //读取 // Saved s = new Saved(ets, hero, bricks, irons); // s.readAll(); @@ -106,7 +110,7 @@ public void actionPerformed(ActionEvent ae) { } } - private void startGame() { + private void startNewStage() { if (Objects.nonNull(actionThread) && !actionThread.isInterrupted()) { log.info("clean last stage"); actionThread.interrupt(); @@ -117,7 +121,7 @@ private void startGame() { } frame.firstStart = false; - MyPanel3.resumePlay = true; + TankGroundPanel.newStage = true; Shot.setSpeed(8); frame.remove(frame.jsp1); actionThread = new Thread(frame); @@ -130,7 +134,7 @@ private void startGame() { // beginAudio.start(); } - public StageActionPanel(TankFrame frame, Hero he, Vector ets, Vector bricks, Vector irons, int[][] ETS, int[] myself) { + public StageActionPanel(MainFrame frame, Hero he, Vector ets, Vector bricks, Vector irons, int[][] ETS, int[] myself) { this.frame = frame; this.hero = he; this.ets = ets; diff --git a/gui/src/main/java/com/github/kuangcp/tank/panel/StarterPanel.java b/gui/src/main/java/com/github/kuangcp/tank/panel/StarterPanel.java new file mode 100644 index 00000000..79d96b41 --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/panel/StarterPanel.java @@ -0,0 +1,40 @@ +package com.github.kuangcp.tank.panel; + +import lombok.extern.slf4j.Slf4j; + +import javax.imageio.ImageIO; +import javax.swing.*; +import java.awt.*; +import java.io.IOException; + +/** + * 绘制开始游戏的界面 + *

+ * 如果要实现闪烁效果 就实现接口,定义一个变量,run里面++, + * 设置变量满足什么条件再画出图片 + */ +@Slf4j +public class StarterPanel extends JPanel { + + public Image First; + + public StarterPanel() { + try { + First = ImageIO.read(getClass().getResource("/images/Tank.jpg")); + } catch (IOException e) { + log.error("", e); + } + } + + public void paint(Graphics g) { + g.drawImage(First, 0, 0, 760, 560, this); + + //直接设置字符串,不用图片 + Font my = new Font("微软雅黑", Font.BOLD, 15); + g.setFont(my); + +// String s = "永无终止"; +// g.setColor(Color.lightGray); +// g.drawString(s, 50, 500); + } +} diff --git a/gui/src/main/java/com/github/kuangcp/tank/v3/MyPanel3.java b/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java similarity index 86% rename from gui/src/main/java/com/github/kuangcp/tank/v3/MyPanel3.java rename to gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java index 513990d1..414f3c2c 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v3/MyPanel3.java +++ b/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java @@ -1,26 +1,29 @@ -package com.github.kuangcp.tank.v3; +package com.github.kuangcp.tank.panel; +import com.github.kuangcp.tank.util.ExecutePool; import com.github.kuangcp.tank.util.TankTool; -import com.github.kuangcp.tank.v1.Brick; -import com.github.kuangcp.tank.v1.EnemyTank; -import com.github.kuangcp.tank.v1.Hero; -import com.github.kuangcp.tank.v1.Iron; -import com.github.kuangcp.tank.v2.Shot; -import com.github.kuangcp.tank.v3.resource.AvatarMgr; -import com.github.kuangcp.tank.v3.resource.BombMgr; -import com.github.kuangcp.tank.v3.thread.ExitFlagRunnable; +import com.github.kuangcp.tank.domain.Brick; +import com.github.kuangcp.tank.domain.EnemyTank; +import com.github.kuangcp.tank.domain.Hero; +import com.github.kuangcp.tank.domain.Iron; +import com.github.kuangcp.tank.domain.Shot; +import com.github.kuangcp.tank.util.ListenEventGroup; +import com.github.kuangcp.tank.util.KeyListener; +import com.github.kuangcp.tank.resource.AvatarMgr; +import com.github.kuangcp.tank.resource.BombMgr; +import com.github.kuangcp.tank.thread.ExitFlagRunnable; import lombok.extern.slf4j.Slf4j; import javax.imageio.ImageIO; import javax.swing.*; import java.awt.*; import java.awt.event.KeyEvent; -import java.awt.event.KeyListener; import java.io.IOException; import java.util.Objects; import java.util.Vector; +import java.util.concurrent.ExecutorService; import java.util.concurrent.TimeUnit; @@ -29,13 +32,13 @@ */ @SuppressWarnings("serial") @Slf4j -public class MyPanel3 extends JPanel implements KeyListener, ExitFlagRunnable { +public class TankGroundPanel extends JPanel implements java.awt.event.KeyListener, ExitFlagRunnable { - Hero hero; - PressedTwo pressedTwo; - static boolean resumePlay = true; + public Hero hero; + public KeyListener keyListener; + public static boolean newStage = true; // 敌人的数量 - static int enSize = 10; + public static int enSize = 10; //定义一个 泛型的集合ets 表示敌人坦克集合 public Vector enemyList = new Vector<>(); @@ -45,7 +48,7 @@ public class MyPanel3 extends JPanel implements KeyListener, ExitFlagRunnable { //所有按下键的code集合 public static int[][] enemyTankMap = new int[12][2]; public static int[] myself = new int[6]; - + private static final ExecutorService delayPool = ExecutePool.buildFixedPool("enemyDelayRemove", 3); Image overImg = null; Image winImg = null; @@ -60,9 +63,9 @@ public void exit() { * 画板的构造函数 用来初始化对象 * 多个敌方坦克应该用集合类,而且要考虑线程安全所以不能用ArrayList只能用Vector */ - public MyPanel3() { + public TankGroundPanel() { //创建英雄坦克 - if (resumePlay) {//正常启动并创建坦克线程 + if (newStage) {//正常启动并创建坦克线程 hero = new Hero(480, 500, 3, enemyList, bricks, irons);//坐标和步长和敌人坦克集合 hero.setLife(10);//设置生命值 } else { @@ -72,14 +75,14 @@ public MyPanel3() { } //多键监听实现 - pressedTwo = new PressedTwo(ListenEventGroup.instance, hero, this); - Thread p = new Thread(pressedTwo); + keyListener = new KeyListener(ListenEventGroup.instance, hero, this); + Thread p = new Thread(keyListener); p.setName("keyEventGroup"); p.start(); // 创建 敌人的坦克 EnemyTank ett = null; - if (resumePlay) {//正常启动并创建坦克线程 + if (newStage) {//正常启动并创建坦克线程 for (int i = 0; i < enSize; i++) { //在四个随机区域产生坦克 switch ((int) (Math.random() * 4)) { @@ -191,7 +194,7 @@ public void paint(Graphics g) { g.setColor(new Color(200, 200, 200)); for (int i = 0; i < irons.size(); i++) { Iron ir = irons.get(i); - if (ir.getLive()) { + if (ir.getAlive()) { g.fill3DRect(ir.getHx(), ir.getHy(), 20, 10, false); } else { irons.remove(ir); @@ -201,7 +204,7 @@ public void paint(Graphics g) { g.setColor(new Color(190, 60, 50)); for (int i = 0; i < bricks.size(); i++) { Brick bs = bricks.get(i); - if (bs.getLive()) { + if (bs.getAlive()) { g.fill3DRect(bs.getHx(), bs.getHy(), 20, 10, false); } else { bricks.remove(bs); @@ -226,8 +229,8 @@ public void paint(Graphics g) { /*画出自己的子弹*/ //画子弹是可以封装成一个方法的 // 从ss 这个子弹集合中 取出每颗子弹,并画出来 - for (int i = 0; i < hero.ss.size(); i++) { - myShot = hero.ss.get(i); + for (int i = 0; i < hero.shotList.size(); i++) { + myShot = hero.shotList.get(i); for (Brick brick : bricks) { TankTool.judgeHint(myShot, brick); } @@ -238,15 +241,13 @@ public void paint(Graphics g) { myShot.isLive = false; hero.setAlive(false); } - if (hero.ss.get(i) != null && hero.ss.get(i).isLive) {//为什么后面的判断条件 存不存在都会没影响呢 -// System.out.println("当前是"+i+"个子弹线程"); + if (hero.shotList.get(i) != null && hero.shotList.get(i).isLive) { g.draw3DRect(myShot.sx, myShot.sy, 1, 1, false); - - // g.draw3DRect(Hero.ss.get(i).sx, Hero.ss.get(i).sy, 1, 1, false); } + + //子弹线程死了 就要把它从集合中删除 if (!myShot.isLive) { - //子弹线程死了 就要把它从集合中删除 - hero.ss.remove(myShot); + hero.shotList.remove(myShot); } } @@ -278,7 +279,6 @@ public void paint(Graphics g) { BombMgr.instance.drawBomb(g, this); /*画出敌人坦克*/ -// if(Continue){ //坦克少于5个就自动添加4个 if (enemyList.size() < 5) { for (int i = 0; i < 4; i++) { @@ -290,7 +290,6 @@ public void paint(Graphics g) { enemyList.add(d); } } -// } // for(int i=0;i { + delayPool.execute(() -> { try { - TimeUnit.SECONDS.sleep(20); + TimeUnit.SECONDS.sleep(8); } catch (InterruptedException e) { log.error("", e); } enemyList.remove(demon); + demon.cleanResource(); }); - thread.setName("lazyRemove-" + demon.id); - thread.start(); } } @@ -545,26 +539,35 @@ public void createI(Vector irons, int startX, int startY, int endX, int en //画板做成线程 画布进行刷新 @Override public void run() { + int maxFPS = 160; + long fpsTime = (long) ((1000.0 / maxFPS) * 1000000); + + long now = System.nanoTime(); //每隔100ms重绘 while (!exit) { - try { - Thread.sleep(10); - //里面的数字就是刷新时间的间隔 而且每当按下J键就会拉起MyPanel线程 相当于加快了刷新 - } catch (Exception e) { - log.error("", e); - } + now = System.nanoTime(); +// try { +// Thread.sleep(10); +// //里面的数字就是刷新时间的间隔 而且每当按下J键就会拉起MyPanel线程 相当于加快了刷新 +// } catch (Exception e) { +// log.error("", e); +// } // 进行重绘 this.repaint(); + final long waste = System.nanoTime() - now; + if (waste > fpsTime) { + continue; + } - // 这里的中断,只是关掉了刷新,但是后台的坦克,子弹线程还在跑 - // if(!hero.getisLive()){ - // break; - // } + TankTool.yieldTime(fpsTime - waste, TimeUnit.NANOSECONDS); + if (!hero.isAlive()) { + break; + } } - // clean - pressedTwo.exit(); + // clean listener + keyListener.exit(); } public static int getEnSize() { @@ -572,6 +575,6 @@ public static int getEnSize() { } public static void setEnSize(int enSize) { - MyPanel3.enSize = enSize; + TankGroundPanel.enSize = enSize; } } \ No newline at end of file diff --git a/gui/src/main/java/com/github/kuangcp/tank/v3/resource/AvatarMgr.java b/gui/src/main/java/com/github/kuangcp/tank/resource/AvatarMgr.java similarity index 95% rename from gui/src/main/java/com/github/kuangcp/tank/v3/resource/AvatarMgr.java rename to gui/src/main/java/com/github/kuangcp/tank/resource/AvatarMgr.java index 0bddcc44..3fb1d90e 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v3/resource/AvatarMgr.java +++ b/gui/src/main/java/com/github/kuangcp/tank/resource/AvatarMgr.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.tank.v3.resource; +package com.github.kuangcp.tank.resource; import lombok.extern.slf4j.Slf4j; diff --git a/gui/src/main/java/com/github/kuangcp/tank/v3/resource/Bomb.java b/gui/src/main/java/com/github/kuangcp/tank/resource/Bomb.java similarity index 94% rename from gui/src/main/java/com/github/kuangcp/tank/v3/resource/Bomb.java rename to gui/src/main/java/com/github/kuangcp/tank/resource/Bomb.java index 851f141c..ae312aa3 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v3/resource/Bomb.java +++ b/gui/src/main/java/com/github/kuangcp/tank/resource/Bomb.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.tank.v3.resource; +package com.github.kuangcp.tank.resource; /** * 1 准备好图片 diff --git a/gui/src/main/java/com/github/kuangcp/tank/v3/resource/BombMgr.java b/gui/src/main/java/com/github/kuangcp/tank/resource/BombMgr.java similarity index 94% rename from gui/src/main/java/com/github/kuangcp/tank/v3/resource/BombMgr.java rename to gui/src/main/java/com/github/kuangcp/tank/resource/BombMgr.java index e55a1f1f..a2fb1152 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v3/resource/BombMgr.java +++ b/gui/src/main/java/com/github/kuangcp/tank/resource/BombMgr.java @@ -1,9 +1,9 @@ -package com.github.kuangcp.tank.v3.resource; +package com.github.kuangcp.tank.resource; import com.github.kuangcp.tank.constant.DirectType; -import com.github.kuangcp.tank.v1.Hero; -import com.github.kuangcp.tank.v1.Tank; -import com.github.kuangcp.tank.v2.Shot; +import com.github.kuangcp.tank.domain.Hero; +import com.github.kuangcp.tank.domain.Tank; +import com.github.kuangcp.tank.domain.Shot; import lombok.extern.slf4j.Slf4j; import javax.imageio.ImageIO; @@ -76,7 +76,7 @@ public void drawBomb(Graphics g, JPanel panel) { * 工具类-检测爆炸的函数 */ public void checkBong(Tank tank, List shots) { - shots.forEach(v -> checkBong(tank, v)); + shots.forEach(v -> this.checkBong(tank, v)); } private void checkBong(Tank t, Shot s) { diff --git a/gui/src/main/java/com/github/kuangcp/tank/v3/thread/ExitFlagRunnable.java b/gui/src/main/java/com/github/kuangcp/tank/thread/ExitFlagRunnable.java similarity index 76% rename from gui/src/main/java/com/github/kuangcp/tank/v3/thread/ExitFlagRunnable.java rename to gui/src/main/java/com/github/kuangcp/tank/thread/ExitFlagRunnable.java index 11768d7b..eb27f65d 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v3/thread/ExitFlagRunnable.java +++ b/gui/src/main/java/com/github/kuangcp/tank/thread/ExitFlagRunnable.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.tank.v3.thread; +package com.github.kuangcp.tank.thread; /** * @author https://github.com/kuangcp on 2021-09-11 18:33 diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/ExecutePool.java b/gui/src/main/java/com/github/kuangcp/tank/util/ExecutePool.java index c7eeab85..d5923ef5 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/util/ExecutePool.java +++ b/gui/src/main/java/com/github/kuangcp/tank/util/ExecutePool.java @@ -13,6 +13,10 @@ public class ExecutePool { public static final AtomicLong totalCounter = new AtomicLong(); + /** + * @param prefix eg: shot + * @param coreSize 核心线程数 + */ public static ExecutorService buildFixedPool(String prefix, int coreSize) { return new ThreadPoolExecutor(coreSize, coreSize, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(), diff --git a/gui/src/main/java/com/github/kuangcp/tank/v3/PressedTwo.java b/gui/src/main/java/com/github/kuangcp/tank/util/KeyListener.java similarity index 80% rename from gui/src/main/java/com/github/kuangcp/tank/v3/PressedTwo.java rename to gui/src/main/java/com/github/kuangcp/tank/util/KeyListener.java index c3547db9..9230463e 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v3/PressedTwo.java +++ b/gui/src/main/java/com/github/kuangcp/tank/util/KeyListener.java @@ -1,9 +1,10 @@ -package com.github.kuangcp.tank.v3; +package com.github.kuangcp.tank.util; import com.github.kuangcp.tank.constant.DirectType; -import com.github.kuangcp.tank.v1.Hero; -import com.github.kuangcp.tank.v3.thread.ExitFlagRunnable; +import com.github.kuangcp.tank.domain.Hero; +import com.github.kuangcp.tank.panel.TankGroundPanel; +import com.github.kuangcp.tank.thread.ExitFlagRunnable; import lombok.extern.slf4j.Slf4j; /** @@ -15,17 +16,17 @@ * 实现结果: 一边跑,一边放子弹完全不是事儿,方向的切换也十分流畅 */ @Slf4j -public class PressedTwo implements ExitFlagRunnable { +public class KeyListener implements ExitFlagRunnable { Hero hero; - MyPanel3 myPanel3; + TankGroundPanel tankGroundPanel; ListenEventGroup eventGroup; private volatile boolean exit = false; - public PressedTwo(ListenEventGroup eventGroup, Hero hero, MyPanel3 myPanel3) { + public KeyListener(ListenEventGroup eventGroup, Hero hero, TankGroundPanel tankGroundPanel) { this.eventGroup = eventGroup; this.hero = hero; - this.myPanel3 = myPanel3; + this.tankGroundPanel = tankGroundPanel; } public void exit() { @@ -64,7 +65,7 @@ public void run() { hero.shotEnemy(); } - myPanel3.repaint(); + tankGroundPanel.repaint(); try { // 动作的延迟 1000 / 77 fps diff --git a/gui/src/main/java/com/github/kuangcp/tank/v3/ListenEventGroup.java b/gui/src/main/java/com/github/kuangcp/tank/util/ListenEventGroup.java similarity index 93% rename from gui/src/main/java/com/github/kuangcp/tank/v3/ListenEventGroup.java rename to gui/src/main/java/com/github/kuangcp/tank/util/ListenEventGroup.java index f177dc78..dd8de1b4 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v3/ListenEventGroup.java +++ b/gui/src/main/java/com/github/kuangcp/tank/util/ListenEventGroup.java @@ -1,10 +1,12 @@ -package com.github.kuangcp.tank.v3; +package com.github.kuangcp.tank.util; + +import com.github.kuangcp.tank.panel.TankGroundPanel; import java.awt.event.KeyEvent; /** * @author https://github.com/kuangcp on 2021-09-06 02:58 - * @see MyPanel3#keyPressed + * @see TankGroundPanel#keyPressed */ public class ListenEventGroup { diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/Saved.java b/gui/src/main/java/com/github/kuangcp/tank/util/Saved.java index fdeedfe8..25b11cd7 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/util/Saved.java +++ b/gui/src/main/java/com/github/kuangcp/tank/util/Saved.java @@ -1,9 +1,9 @@ package com.github.kuangcp.tank.util; -import com.github.kuangcp.tank.v1.Brick; -import com.github.kuangcp.tank.v1.EnemyTank; -import com.github.kuangcp.tank.v1.Hero; -import com.github.kuangcp.tank.v1.Iron; +import com.github.kuangcp.tank.domain.Brick; +import com.github.kuangcp.tank.domain.EnemyTank; +import com.github.kuangcp.tank.domain.Hero; +import com.github.kuangcp.tank.domain.Iron; import java.io.BufferedReader; import java.io.BufferedWriter; diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/Setting.java b/gui/src/main/java/com/github/kuangcp/tank/util/Setting.java deleted file mode 100644 index 7849a988..00000000 --- a/gui/src/main/java/com/github/kuangcp/tank/util/Setting.java +++ /dev/null @@ -1,108 +0,0 @@ -package com.github.kuangcp.tank.util; - - -import com.github.kuangcp.tank.constant.SettingCommand; -import com.github.kuangcp.tank.v1.Hero; -import com.github.kuangcp.tank.v2.Shot; -import com.github.kuangcp.tank.v3.MyPanel3; - -import javax.imageio.ImageIO; -import javax.swing.*; -import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.image.BufferedImage; -import java.io.IOException; - -public class Setting extends JFrame implements ActionListener { - - JPanel up, ss; - JLabel U; - JButton[] Plus = new JButton[4]; - JButton[] Cut = new JButton[4]; - JLabel[] Kind = new JLabel[4]; - Hero hero; - - public Setting(Hero hero) { - this.hero = hero; - try { - final BufferedImage iconImg = ImageIO.read(getClass().getResourceAsStream("/images/Me4.jpg")); - U = new JLabel(new ImageIcon(iconImg)); - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - - for (int i = 0; i < 4; i++) { - Plus[i] = new JButton("+"); - Cut[i] = new JButton("-"); - } - Kind[0] = new JLabel("子弹速度");//+Shot.getSpeed() - Kind[1] = new JLabel("̹坦克速度");//+hero.getSpeed() - Kind[2] = new JLabel("坦克生命");//+this.hero.getLife() - Kind[3] = new JLabel("敌人数量");//+MyPanel3.getEnSize() - - up = new JPanel(); - ss = new JPanel(); - - up.add(U); - - ss.setLayout(new GridLayout(4, 3, 0, 0)); - for (int i = 0; i < 4; i++) { - ss.add(Kind[i]); - ss.add(Plus[i]); - ss.add(Cut[i]); - Plus[i].addActionListener(this); - Cut[i].addActionListener(this); - } - Plus[0].setActionCommand(SettingCommand.SHOT_SPEED_INCREMENT); - Plus[1].setActionCommand(SettingCommand.TANK_SPEED_INCREMENT); - Plus[2].setActionCommand(SettingCommand.TANK_HP_INCREMENT); - Plus[3].setActionCommand(SettingCommand.DEMONS_COUNT_INCREMENT); - - Cut[0].setActionCommand(SettingCommand.SHOT_SPEED_DECREMENT); - Cut[1].setActionCommand(SettingCommand.TANK_SPEED_DECREMENT); - Cut[2].setActionCommand(SettingCommand.TANK_HP_DECREMENT); - Cut[3].setActionCommand(SettingCommand.DEMONS_COUNT_DECREMENT); - - this.add(up, BorderLayout.NORTH); - this.add(ss, BorderLayout.CENTER); - this.setLocation(800, 300); - this.setSize(260, 280); - this.setTitle("Setting"); -// this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - this.setVisible(true); - } - - @Override - public void actionPerformed(ActionEvent event) { - if (event.getActionCommand().equals(SettingCommand.SHOT_SPEED_INCREMENT)) { - Shot.setSpeed(Shot.getSpeed() + 1); - } - if (event.getActionCommand().equals(SettingCommand.SHOT_SPEED_DECREMENT)) { - Shot.setSpeed(Shot.getSpeed() - 1); - } - - if (event.getActionCommand().equals(SettingCommand.TANK_SPEED_INCREMENT)) { - hero.addSpeed(1); - } - if (event.getActionCommand().equals(SettingCommand.TANK_SPEED_DECREMENT)) { - hero.addSpeed(-1); - } - - if (event.getActionCommand().equals(SettingCommand.TANK_HP_INCREMENT)) { - this.hero.setLife(this.hero.getLife() + 1); - } - if (event.getActionCommand().equals(SettingCommand.TANK_HP_DECREMENT)) { - this.hero.setLife(this.hero.getLife() - 1); - } - - if (event.getActionCommand().equals(SettingCommand.DEMONS_COUNT_INCREMENT)) { - MyPanel3.setEnSize(MyPanel3.getEnSize() + 1); - } - if (event.getActionCommand().equals(SettingCommand.DEMONS_COUNT_DECREMENT)) { - MyPanel3.setEnSize(MyPanel3.getEnSize() - 1); - } - - } -} \ No newline at end of file diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/TankTool.java b/gui/src/main/java/com/github/kuangcp/tank/util/TankTool.java index 5c9d8520..d0eac6c7 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/util/TankTool.java +++ b/gui/src/main/java/com/github/kuangcp/tank/util/TankTool.java @@ -1,12 +1,13 @@ package com.github.kuangcp.tank.util; import com.github.kuangcp.tank.constant.DirectType; -import com.github.kuangcp.tank.v1.Brick; -import com.github.kuangcp.tank.v1.Hinder; -import com.github.kuangcp.tank.v1.Tank; -import com.github.kuangcp.tank.v2.Shot; +import com.github.kuangcp.tank.domain.Brick; +import com.github.kuangcp.tank.domain.Hinder; +import com.github.kuangcp.tank.domain.Shot; +import com.github.kuangcp.tank.domain.Tank; import lombok.extern.slf4j.Slf4j; +import java.util.Objects; import java.util.concurrent.TimeUnit; @Slf4j @@ -15,7 +16,13 @@ public class TankTool { /** * 碰撞检测函数 坦克之间 */ - public static boolean hasHint(Tank me, Tank you) { + public static boolean ablePass(Tank me, Tank you) { + if (Objects.isNull(me) || Objects.isNull(you)) { + return true; + } + if (!me.isAlive() || !you.isAlive()) { + return true; + } boolean flag = true; switch (you.getDirect()) {//对方 上下 case 0: @@ -159,7 +166,7 @@ public static boolean hasHint(Tank me, Tank you) { /** * 碰撞检测函数 坦克 和 障碍物 */ - public static boolean hasHint(Tank t, Hinder h) { + public static boolean ablePass(Tank t, Hinder h) { int hx = 20, hy = 10; switch (t.getDirect()) { case DirectType.UP: @@ -207,7 +214,7 @@ public static void judgeHint(Shot s, Hinder h) { s.isLive = false; if (h instanceof Brick) { - h.setLive(false); + h.setAlive(false); } } } diff --git a/gui/src/main/java/com/github/kuangcp/tank/v1/TankGame1.java b/gui/src/main/java/com/github/kuangcp/tank/v1/TankGame1.java index 8134a085..3cf49902 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v1/TankGame1.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v1/TankGame1.java @@ -5,6 +5,10 @@ * 3 加上对坦克运动的边界限定 */ +import com.github.kuangcp.tank.domain.Brick; +import com.github.kuangcp.tank.domain.EnemyTank; +import com.github.kuangcp.tank.domain.Hero; + import javax.swing.*; import java.awt.*; import java.awt.event.KeyEvent; diff --git a/gui/src/main/java/com/github/kuangcp/tank/v2/MyPanel.java b/gui/src/main/java/com/github/kuangcp/tank/v2/MyPanel.java index c054ac84..f8f00f14 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v2/MyPanel.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v2/MyPanel.java @@ -1,9 +1,10 @@ package com.github.kuangcp.tank.v2; -import com.github.kuangcp.tank.v1.Brick; -import com.github.kuangcp.tank.v1.EnemyTank; -import com.github.kuangcp.tank.v1.Hero; +import com.github.kuangcp.tank.domain.Brick; +import com.github.kuangcp.tank.domain.EnemyTank; +import com.github.kuangcp.tank.domain.Hero; +import com.github.kuangcp.tank.domain.Shot; import javax.swing.*; import java.awt.*; @@ -53,8 +54,8 @@ public void paint(Graphics g) { // 从ss 这个子弹集合中 取出每颗子弹,并画出来 - for (int i = 0; i < this.hero.ss.size(); i++) { - Shot myShot = hero.ss.get(i); + for (int i = 0; i < this.hero.shotList.size(); i++) { + Shot myShot = hero.shotList.get(i); // 以下代码只能画出一颗子弹 不完善 if (myShot != null) { g.draw3DRect(hero.s.sx, hero.s.sy, 1, 1, false); diff --git a/gui/src/main/java/com/github/kuangcp/tank/v3/TankFrame.java b/gui/src/main/java/com/github/kuangcp/tank/v3/MainFrame.java similarity index 77% rename from gui/src/main/java/com/github/kuangcp/tank/v3/TankFrame.java rename to gui/src/main/java/com/github/kuangcp/tank/v3/MainFrame.java index e01829e8..9badbf1e 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v3/TankFrame.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v3/MainFrame.java @@ -1,6 +1,11 @@ package com.github.kuangcp.tank.v3; import com.github.kuangcp.tank.constant.StageCommand; +import com.github.kuangcp.tank.panel.TankGroundPanel; +import com.github.kuangcp.tank.panel.HeroInfoPanel; +import com.github.kuangcp.tank.panel.StageActionPanel; +import com.github.kuangcp.tank.panel.StarterPanel; +import lombok.extern.slf4j.Slf4j; import javax.swing.*; import java.awt.*; @@ -29,15 +34,16 @@ * 10 可以继续上局游戏开始玩 * 11 有游戏的音效(操纵声音文件) */ +@Slf4j @SuppressWarnings("serial") -public class TankFrame extends JFrame implements Runnable { +public class MainFrame extends JFrame implements Runnable { - public MyPanel3 mp = null;//坦克的主画板 + public TankGroundPanel mp = null;//坦克的主画板 public StageActionPanel mp2 = null;//放按钮的画板 public StageActionPanel mpe1 = null;//对按钮事件监听处理的 - public MyPanel302 mp3 = null; //显示属性的画板 - public MyPanel303 Fir; - public JButton jb1 = null, jb2 = null, jb3, jb4; //按钮 + public HeroInfoPanel mp3 = null; //显示属性的画板 + public StarterPanel Fir; + public JButton startBtn = null, jb2 = null, jb3, jb4; //按钮 public JSplitPane jsp1, jsp2;//拆分窗格 public JLabel jl1 = null, jl2 = null, jl3 = null, me = null, prizeNo = null; public boolean firstStart = true; //判断是否首次运行 @@ -57,6 +63,7 @@ public class TankFrame extends JFrame implements Runnable { JMenuItem Help = null; public void run() { + final long start = System.currentTimeMillis(); if (Objects.nonNull(mp)) { mp.exit(); } @@ -64,18 +71,16 @@ public void run() { mp3.exit(); } - mp = new MyPanel3(); - mpe1 = new StageActionPanel(this, mp.hero, mp.enemyList, mp.bricks, mp.irons, MyPanel3.enemyTankMap, MyPanel3.myself);//监听按钮事件的画布对象 - mp2 = new StageActionPanel(this, mp.hero, mp.enemyList, mp.bricks, mp.irons, MyPanel3.enemyTankMap, MyPanel3.myself);//放按钮的画布 + mp = new TankGroundPanel(); + mpe1 = new StageActionPanel(this, mp.hero, mp.enemyList, mp.bricks, mp.irons, TankGroundPanel.enemyTankMap, TankGroundPanel.myself);//监听按钮事件的画布对象 + mp2 = new StageActionPanel(this, mp.hero, mp.enemyList, mp.bricks, mp.irons, TankGroundPanel.enemyTankMap, TankGroundPanel.myself);//放按钮的画布 //提示信息 jl1 = new JLabel(" : " + mp.hero.getLife() + " : " + mp.enemyList.size()); prizeNo = new JLabel("已击杀 :" + mp.hero.getPrize());//战绩的标签 -// jl2 = new JLabel("提示:WSAD-上下左右 J-发子弹"); -// jl3 = new JLabel("上面是生命值和敌人数"); me = new JLabel("Myth"); - mp3 = new MyPanel302(jl1, mp.hero, mp.enemyList, prizeNo);//显示一些属性 + mp3 = new HeroInfoPanel(jl1, mp.hero, mp.enemyList, prizeNo);//显示一些属性 if (!firstStart) { //已经成为一个线程 要启动它 @@ -86,6 +91,7 @@ public void run() { t2.setName("mp3"); t2.start(); } + //创建菜单及菜单选项 jmb = new JMenuBar(); jm1 = new JMenu("Game(G)"); @@ -128,22 +134,21 @@ public void run() { jmb.add(jm2); jb3 = new JButton("暂停游戏"); - jb3.addActionListener(mpe1); //注册监听 - jb3.setActionCommand("暂停");///////// + jb3.addActionListener(mpe1); //注册监听 + jb3.setActionCommand("暂停"); jb4 = new JButton("继续游戏"); - jb4.addActionListener(mpe1); //注册监听 - jb4.setActionCommand("继续");///////// + jb4.addActionListener(mpe1); //注册监听 + jb4.setActionCommand("继续"); jb2 = new JButton("退出游戏"); - jb2.addActionListener(mpe1); //注册监听 - jb2.setActionCommand("结束");///////// + jb2.addActionListener(mpe1); //注册监听 + jb2.setActionCommand("结束"); - if (firstStart) jb1 = new JButton("游戏开始"); - else jb1 = new JButton("重新开始"); - jb1.addActionListener(mpe1); //注册监听 - jb1.setActionCommand(StageCommand.START);/////////// + startBtn = new JButton(firstStart ? "游戏开始" : "重新开始"); + startBtn.addActionListener(mpe1); + startBtn.setActionCommand(StageCommand.START); - mp2.add(jb1); + mp2.add(startBtn); mp2.add(jb2); mp2.add(jb3); mp2.add(jb4); @@ -160,7 +165,7 @@ public void run() { jsp2 = new JSplitPane(JSplitPane.VERTICAL_SPLIT, mp2, mp3);//水平 jsp2.setDividerLocation(150); - Fir = new MyPanel303(); + Fir = new StarterPanel(); if (!firstStart) jsp1 = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, mp, jsp2);//垂直 else jsp1 = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, Fir, jsp2); jsp1.setDividerLocation(760); @@ -181,7 +186,9 @@ public void run() { this.setTitle("Tank"); this.setLocation(150, 60); this.setSize(1000, 625); + log.info("{} pre visible", (System.currentTimeMillis() - start)); this.setVisible(true); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + log.info("{} total", (System.currentTimeMillis() - start)); } } \ No newline at end of file diff --git a/gui/src/main/java/com/github/kuangcp/tank/v3/MainTankGame.java b/gui/src/main/java/com/github/kuangcp/tank/v3/MainTankGame.java index d7f57abd..8a5542c8 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v3/MainTankGame.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v3/MainTankGame.java @@ -1,13 +1,18 @@ package com.github.kuangcp.tank.v3; +import lombok.extern.slf4j.Slf4j; + +import java.awt.*; + +@Slf4j public class MainTankGame { /** * -Xmx300m -Xms300m -XX:+UseG1GC + *

+ * https://stackoverflow.com/questions/6736906/why-does-java-swings-setvisible-take-so-long */ public static void main(String[] args) { - Thread t = new Thread(new TankFrame()); - t.setName("mainFrame"); - t.start(); + EventQueue.invokeLater(new MainFrame()); } } diff --git a/gui/src/main/java/com/github/kuangcp/tank/v3/MyPanel303.java b/gui/src/main/java/com/github/kuangcp/tank/v3/MyPanel303.java deleted file mode 100644 index cd8423d0..00000000 --- a/gui/src/main/java/com/github/kuangcp/tank/v3/MyPanel303.java +++ /dev/null @@ -1,41 +0,0 @@ -/** - * 绘制开始游戏的界面 - * - * 如果要实现闪烁效果 就实现接口,定义一个变量,run里面++, - * 设置变量满足什么条件再画出图片 - * - */ -package com.github.kuangcp.tank.v3; - -import javax.imageio.ImageIO; -import javax.swing.*; -import java.awt.*; -import java.io.IOException; - -public class MyPanel303 extends JPanel { - - Image First; - - public MyPanel303(){ - try { - First = ImageIO.read(getClass().getResource("/images/Tank.jpg")); - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - public void paint(Graphics g){ - g.drawImage(First, 0, 0, 760,560,this); - - //直接设置字符串,不用图片 - Font my = new Font("微软雅黑", Font.BOLD,15); - g.setFont(my); -// String s = "击中40辆坦克可以获胜"; - String s = "永无终止"; - g.setColor(Color.lightGray); - g.drawString(s, 50, 500); - - - } - -} diff --git a/gui/src/main/java/com/github/kuangcp/tank/v3/SettingFrame.java b/gui/src/main/java/com/github/kuangcp/tank/v3/SettingFrame.java new file mode 100644 index 00000000..192f7086 --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/v3/SettingFrame.java @@ -0,0 +1,125 @@ +package com.github.kuangcp.tank.v3; + +import com.github.kuangcp.tank.constant.SettingCommand; +import com.github.kuangcp.tank.domain.Hero; +import com.github.kuangcp.tank.domain.Shot; +import com.github.kuangcp.tank.panel.TankGroundPanel; +import lombok.extern.slf4j.Slf4j; + +import javax.imageio.ImageIO; +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.util.Objects; + +@Slf4j +public class SettingFrame extends JFrame implements ActionListener { + + public static SettingFrame instance = null; + + JPanel up, ss; + JLabel U; + JButton[] incrArr = new JButton[4]; + JButton[] decrArr = new JButton[4]; + JLabel[] labelArr = new JLabel[4]; + Hero hero; + + public static synchronized void activeFocus(Hero hero) { + final SettingFrame frame = getInstance(hero); + frame.setVisible(true); + } + + private static synchronized SettingFrame getInstance(Hero hero) { + if (Objects.isNull(instance)) { + instance = new SettingFrame(hero); + } + instance.hero = hero; + return instance; + } + + public SettingFrame(Hero hero) { + this.hero = hero; + try { + final BufferedImage iconImg = ImageIO.read(getClass().getResourceAsStream("/images/Me4.jpg")); + U = new JLabel(new ImageIcon(iconImg)); + } catch (IOException e) { + log.error("", e); + } + + for (int i = 0; i < 4; i++) { + incrArr[i] = new JButton("+"); + decrArr[i] = new JButton("-"); + } + labelArr[0] = new JLabel("子弹速度");//+Shot.getSpeed() + labelArr[1] = new JLabel("̹坦克速度");//+hero.getSpeed() + labelArr[2] = new JLabel("坦克生命");//+this.hero.getLife() + labelArr[3] = new JLabel("敌人数量");//+MyPanel3.getEnSize() + + up = new JPanel(); + ss = new JPanel(); + + up.add(U); + + ss.setLayout(new GridLayout(4, 3, 0, 0)); + for (int i = 0; i < 4; i++) { + ss.add(labelArr[i]); + ss.add(incrArr[i]); + ss.add(decrArr[i]); + incrArr[i].addActionListener(this); + decrArr[i].addActionListener(this); + } + + incrArr[0].setActionCommand(SettingCommand.SHOT_SPEED_INCREMENT); + incrArr[1].setActionCommand(SettingCommand.TANK_SPEED_INCREMENT); + incrArr[2].setActionCommand(SettingCommand.TANK_HP_INCREMENT); + incrArr[3].setActionCommand(SettingCommand.DEMONS_COUNT_INCREMENT); + + decrArr[0].setActionCommand(SettingCommand.SHOT_SPEED_DECREMENT); + decrArr[1].setActionCommand(SettingCommand.TANK_SPEED_DECREMENT); + decrArr[2].setActionCommand(SettingCommand.TANK_HP_DECREMENT); + decrArr[3].setActionCommand(SettingCommand.DEMONS_COUNT_DECREMENT); + + this.add(up, BorderLayout.NORTH); + this.add(ss, BorderLayout.CENTER); + this.setLocation(800, 300); + this.setSize(260, 280); + this.setTitle("Setting"); +// this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + this.setVisible(true); + } + + @Override + public void actionPerformed(ActionEvent event) { + if (event.getActionCommand().equals(SettingCommand.SHOT_SPEED_INCREMENT)) { + Shot.setSpeed(Shot.getSpeed() + 1); + } + if (event.getActionCommand().equals(SettingCommand.SHOT_SPEED_DECREMENT)) { + Shot.setSpeed(Shot.getSpeed() - 1); + } + + if (event.getActionCommand().equals(SettingCommand.TANK_SPEED_INCREMENT)) { + hero.addSpeed(1); + } + if (event.getActionCommand().equals(SettingCommand.TANK_SPEED_DECREMENT)) { + hero.addSpeed(-1); + } + + if (event.getActionCommand().equals(SettingCommand.TANK_HP_INCREMENT)) { + this.hero.setLife(this.hero.getLife() + 1); + } + if (event.getActionCommand().equals(SettingCommand.TANK_HP_DECREMENT)) { + this.hero.setLife(this.hero.getLife() - 1); + } + + if (event.getActionCommand().equals(SettingCommand.DEMONS_COUNT_INCREMENT)) { + TankGroundPanel.setEnSize(TankGroundPanel.getEnSize() + 1); + } + if (event.getActionCommand().equals(SettingCommand.DEMONS_COUNT_DECREMENT)) { + TankGroundPanel.setEnSize(TankGroundPanel.getEnSize() - 1); + } + + } +} \ No newline at end of file diff --git a/gui/src/main/resources/logback.xml b/gui/src/main/resources/logback.xml new file mode 100644 index 00000000..65aa0863 --- /dev/null +++ b/gui/src/main/resources/logback.xml @@ -0,0 +1,36 @@ + + + + + + + + + %d{HH:mm:ss.SSS} %highlight(%-5level) [%thread] %logger{30} %highlight(%M):%yellow(%-3L) %msg%n + + + + DEBUG + + + + + + ${log.base}debug.log + true + + ${log.base}debug.%d{yyyy-MM-dd}.log + + + %d{MM-dd HH:mm:ss.SSS} |-%-5level |%thread| %logger{36}:%L - %msg%n + + + DEBUG + + + + + + + + \ No newline at end of file From 9ef1136164a48a1a500577ab4ecb5d67ac3920a2 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Sat, 11 Sep 2021 23:20:23 +0800 Subject: [PATCH 215/476] x) old version runnable --- .../tank/v1/{TankGame1.java => MyPanel.java} | 104 +----------------- .../github/kuangcp/tank/v1/TankGameV1.java | 34 ++++++ .../com/github/kuangcp/tank/v2/MyPanel.java | 99 ++--------------- .../com/github/kuangcp/tank/v2/TankGame2.java | 47 -------- 4 files changed, 49 insertions(+), 235 deletions(-) rename gui/src/main/java/com/github/kuangcp/tank/v1/{TankGame1.java => MyPanel.java} (66%) create mode 100644 gui/src/main/java/com/github/kuangcp/tank/v1/TankGameV1.java diff --git a/gui/src/main/java/com/github/kuangcp/tank/v1/TankGame1.java b/gui/src/main/java/com/github/kuangcp/tank/v1/MyPanel.java similarity index 66% rename from gui/src/main/java/com/github/kuangcp/tank/v1/TankGame1.java rename to gui/src/main/java/com/github/kuangcp/tank/v1/MyPanel.java index 3cf49902..4da03965 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v1/TankGame1.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v1/MyPanel.java @@ -1,9 +1,4 @@ package com.github.kuangcp.tank.v1; -/** - * 1 绘画出坦克 并且能做到对它的移动操作 - * 2 移动最好封装成函数(面向对象思想)这是坦克的行为属性 可扩展 - * 3 加上对坦克运动的边界限定 - */ import com.github.kuangcp.tank.domain.Brick; import com.github.kuangcp.tank.domain.EnemyTank; @@ -15,70 +10,27 @@ import java.awt.event.KeyListener; import java.util.Vector; +/** + * @author https://github.com/kuangcp on 2021-09-11 23:15 + */ @SuppressWarnings("serial") -public class TankGame1 extends JFrame { - - - MyPanel mp = null; - - @SuppressWarnings("unused") - public static void main(String[] args) { - // TODO Auto-generated method stub - TankGame1 Tank = new TankGame1(); - } - - //最外层JFrame的构造函数 - public TankGame1() { - - - mp = new MyPanel(); - - this.add(mp); - //注册键盘监听 - //下面的语句翻译为 :当前类的监;听者是mp - this.addKeyListener(mp); - - this.setLocation(900, 200); - this.setSize(500, 400); - this.setVisible(true); - this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - } - -} - -@SuppressWarnings("serial") -///////////////////////////////////////////////////////////////////////////// class MyPanel extends JPanel implements KeyListener { //定义我的坦克 Hero hero = null; int enSize = 3; //敌人的数量 //定义泛型的集合类 - Vector ets = new Vector(); - Vector bricks = new Vector(); + Vector ets = new Vector<>(); + Vector bricks = new Vector<>(); //画板的构造函数 放图形的对象 public MyPanel() { - //多个敌方坦克应该用集合类,而且要考虑线程安全所以不能用ArrayList只能用Vector - //实例化英雄坦克 -// hero = new Hero(10,200,10,ets,bricks);//坐标和步长 - - - //初始化敌人的坦克 - /*for (int i=0;i(), new Vector<>(), new Vector<>()); g.fillRect(0, 0, 500, 400); //调用函数绘画出主坦克 @@ -170,80 +122,36 @@ public void drawTank(int X, int Y, Graphics g, int direct, int type) { public void keyPressed(KeyEvent e) { //加了if条件后 实现了墙的限制(如果是游戏中的道具,该怎么办) if (e.getKeyCode() == KeyEvent.VK_A) { - // System.out.print("左"); hero.setDirect(2); if ((hero.getX() - 10) > 0) hero.moveleft(); } if (e.getKeyCode() == KeyEvent.VK_D) { - // System.out.print("右"); hero.setDirect(3); if ((hero.getX() + 15) < 405) hero.moveright(); } if (e.getKeyCode() == KeyEvent.VK_W) { - // System.out.print("上"); hero.setDirect(0); if ((hero.getY() - 13) > 0) hero.moveup(); } if (e.getKeyCode() == KeyEvent.VK_S) { - // System.out.print("下"); hero.setDirect(1); if ((hero.getY() - 15) < 275) hero.movedown(); } - this.repaint(); - //必须重新绘制窗口,不然上面的方法不能视觉上动起来 this.repaint(); - } public void keyReleased(KeyEvent arg0) { - // TODO Auto-generated method stub - } public void keyTyped(KeyEvent arg0) { - // TODO Auto-generated method stub - } } -/* -//为了能一边走一边发子弹而设计的专门处理按下j键子弹发射的类 -class JS extends JPanel implements KeyListener{ - - Hero hero=null; - public JS(){} - public JS(Hero h){ - hero = h; - } - public void keyTyped(KeyEvent e) { - // TODO Auto-generated method stub - - } - - public void keyPressed(KeyEvent e) { - // TODO Auto-generated method stub - if (e.getKeyChar() == KeyEvent.VK_J){ - //开火 - hero.shotEnemy(); - } - - //必须重新绘制窗口,不然上面的方法不能视觉上动起来 - this.repaint(); - - } - - public void keyReleased(KeyEvent e) { - // TODO Auto-generated method stub - - } - -} -*/ \ No newline at end of file diff --git a/gui/src/main/java/com/github/kuangcp/tank/v1/TankGameV1.java b/gui/src/main/java/com/github/kuangcp/tank/v1/TankGameV1.java new file mode 100644 index 00000000..356983fe --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/v1/TankGameV1.java @@ -0,0 +1,34 @@ +package com.github.kuangcp.tank.v1; + +import javax.swing.*; + +/** + * 1 绘画出坦克 并且能做到对它的移动操作 + * 2 移动最好封装成函数(面向对象思想)这是坦克的行为属性 可扩展 + * 3 加上对坦克运动的边界限定 + */ +@SuppressWarnings("serial") +public class TankGameV1 extends JFrame { + + MyPanel mp; + + @SuppressWarnings("unused") + public static void main(String[] args) { + TankGameV1 Tank = new TankGameV1(); + } + + //最外层JFrame的构造函数 + public TankGameV1() { + mp = new MyPanel(); + + this.add(mp); + //注册键盘监听 + //下面的语句翻译为 :当前类的监;听者是mp + this.addKeyListener(mp); + + this.setLocation(900, 200); + this.setSize(500, 400); + this.setVisible(true); + this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + } +} \ No newline at end of file diff --git a/gui/src/main/java/com/github/kuangcp/tank/v2/MyPanel.java b/gui/src/main/java/com/github/kuangcp/tank/v2/MyPanel.java index f8f00f14..79bd80a9 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v2/MyPanel.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v2/MyPanel.java @@ -1,7 +1,6 @@ package com.github.kuangcp.tank.v2; -import com.github.kuangcp.tank.domain.Brick; import com.github.kuangcp.tank.domain.EnemyTank; import com.github.kuangcp.tank.domain.Hero; import com.github.kuangcp.tank.domain.Shot; @@ -18,42 +17,25 @@ public class MyPanel extends JPanel implements KeyListener, Runnable { //定义我的坦克 Hero hero = null; - int enSize = 3; // 敌人的数量 //定义一个 泛型的集合类 表示敌人坦克集合 - Vector ets = new Vector(); - Vector bricks = new Vector(); - + Vector ets = new Vector<>(); //画板的构造函数 放图形的对象 public MyPanel() { - //多个敌方坦克应该用集合类,而且要考虑线程安全所以不能用ArrayList只能用Vector - //实例化英雄坦克 -// hero = new Hero(10,200,10,ets,bricks);//坐标和步长 -// JS j = new JS(hero); - - - //初始化敌人的坦克 - /*for (int i=0;i(), new Vector<>(), new Vector<>()); + g.fillRect(0, 0, 500, 400); -//调用函数绘画出主坦克 + //调用函数绘画出主坦克 this.drawTank(hero.getX(), hero.getY(), g, hero.getDirect(), hero.getType()); - -// 从ss 这个子弹集合中 取出每颗子弹,并画出来 + //从ss 这个子弹集合中 取出每颗子弹,并画出来 for (int i = 0; i < this.hero.shotList.size(); i++) { Shot myShot = hero.shotList.get(i); // 以下代码只能画出一颗子弹 不完善 @@ -62,22 +44,11 @@ public void paint(Graphics g) { } } - - //自己做的函数 完全跑不起来 - /* - if (hero.s!=null ){//&& e.getKeyCode()==KeyEvent.VK_J - this.drawS(hero.s.sx, hero.s.sy,hero.getDirect(),g); - g.draw3DRect(hero.s.sx, hero.s.sy, 1, 1, false); - }*/ - - //画出敌人坦克 - for (int i = 0; i < ets.size(); i++) { - EnemyTank s = ets.get(i); + for (EnemyTank s : ets) { this.drawTank(s.getX(), s.getY(), g, s.getDirect(), s.getType()); } - - // 画出砖块 + //画出砖块 g.setColor(Color.yellow); g.drawRect(0, 0, 400, 300); for (int j = 0; j < 20; j++) { @@ -86,42 +57,6 @@ public void paint(Graphics g) { } - - /*//画出会动的子弹的函数 自己 - public void drawS(int sx,int sy,int direct,Graphics g){ - switch (direct){//上下左右 - case 0: - while (sy>0){ - g.draw3DRect(sx, sy, 1, 1, false); - sy-=3; - repaint(); - } - break; - case 1: - while (sy<300){ - g.draw3DRect(sx, sy, 1, 1, false); - sy+=3; - repaint(); - } - break; - case 2: - while (sx>0){ - g.draw3DRect(sx, sy, 1, 1, false); - sx-=3; - repaint(); - } - break; - case 3: - while (sx<400){ - g.draw3DRect(sx, sy, 1, 1, false); - sx+=3; - repaint(); - } - break; - } - repaint(); - } - */ //画出坦克的函数 XY是坦克中心的坐标,不是画图参照点 public void drawTank(int X, int Y, Graphics g, int direct, int type) { //判断坦克类型 @@ -192,39 +127,32 @@ public void keyPressed(KeyEvent e) { // 加了if条件后 实现了墙的限制(如果是游戏中的道具,该怎么办) if (e.getKeyCode() == KeyEvent.VK_A) { - // System.out.print("左"); hero.setDirect(2); if ((hero.getX() - 10) > 0) hero.moveleft(); } if (e.getKeyCode() == KeyEvent.VK_D) { - // System.out.print("右"); hero.setDirect(3); if ((hero.getX() + 15) < 405) hero.moveright(); } if (e.getKeyCode() == KeyEvent.VK_W) { - // System.out.print("上"); hero.setDirect(0); if ((hero.getY() - 13) > 0) hero.moveup(); } if (e.getKeyCode() == KeyEvent.VK_S) { - // System.out.print("下"); hero.setDirect(1); if ((hero.getY() - 15) < 275) hero.movedown(); } this.repaint(); -// 判断玩家是否按下 J键 //开火 if (e.getKeyChar() == KeyEvent.VK_J) { System.out.println("按下 开火键"); -// 自己做的开火函数 -// hero.drawS(hero.getX(), hero.getY(), hero.getDirect(), hero.g); hero.shotEnemy(); repaint(); } @@ -236,31 +164,22 @@ public void keyPressed(KeyEvent e) { } public void keyReleased(KeyEvent arg0) { - // TODO Auto-generated method stub - } public void keyTyped(KeyEvent arg0) { - // TODO Auto-generated method stub - } //画板做成线程 画布进行刷新 public void run() { - // TODO Auto-generated method stub //每隔100ms重绘 while (true) { - try { Thread.sleep(100); } catch (Exception e) { } - //单纯是为了测试,不需要这句话来耗资源 -// System.out.println("Panel的一个线程睡觉"); -// 进行重绘 + //进行重绘 this.repaint(); } - } } \ No newline at end of file diff --git a/gui/src/main/java/com/github/kuangcp/tank/v2/TankGame2.java b/gui/src/main/java/com/github/kuangcp/tank/v2/TankGame2.java index c011c969..c3777f1f 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v2/TankGame2.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v2/TankGame2.java @@ -12,75 +12,28 @@ @SuppressWarnings("serial") public class TankGame2 extends JFrame { - public static MyPanel mp = null;//画板 - - // JS j = null; @SuppressWarnings("unused") public static void main(String[] args) { - // TODO Auto-generated method stub TankGame2 Tank = new TankGame2(); } //最外层JFrame的构造函数 public TankGame2() { - mp = new MyPanel();//已经成为一个线程 要启动它 Thread t = new Thread(mp); t.start(); -// j = new JS(); - -// this.add(j); this.add(mp); //注册键盘监听 //下面的语句翻译为 :当前类的监听者是mp this.addKeyListener(mp); -// this.addKeyListener(j); this.setLocation(900, 200); this.setSize(500, 400); this.setVisible(true); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } - -} - - -///////////////////////////////////////////////////////////////////////////// - -/* -//为了能一边走一边发子弹而设计的专门处理按下j键子弹发射的类 -class JS extends JPanel implements KeyListener{ - - Hero hero=null; - public JS(){} - public JS(Hero h){ - hero = h; - } - public void keyTyped(KeyEvent e) { - // TODO Auto-generated method stub - - } - - public void keyPressed(KeyEvent e) { - // TODO Auto-generated method stub - if (e.getKeyChar() == KeyEvent.VK_J){ - //开火 - hero.shotEnemy(); - } - - //必须重新绘制窗口,不然上面的方法不能视觉上动起来 - this.repaint(); - - } - - public void keyReleased(KeyEvent e) { - // TODO Auto-generated method stub - - } - } -*/ From ba50e562ff5ee9d61ccc653d7dc9c2bb6d552664 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Sun, 12 Sep 2021 02:47:32 +0800 Subject: [PATCH 216/476] *) extract play ground --- .../github/kuangcp/tank/domain/EnemyTank.java | 114 +++++++++------- .../com/github/kuangcp/tank/domain/Hero.java | 126 ++++-------------- .../github/kuangcp/tank/domain/Hinder.java | 6 +- .../com/github/kuangcp/tank/domain/Tank.java | 30 ++++- .../kuangcp/tank/panel/HeroInfoPanel.java | 31 +++-- .../kuangcp/tank/panel/StageActionPanel.java | 35 +++-- .../kuangcp/tank/panel/TankGroundPanel.java | 67 ++++++---- .../github/kuangcp/tank/resource/BombMgr.java | 11 +- .../github/kuangcp/tank/util/ExecutePool.java | 4 + .../github/kuangcp/tank/util/KeyListener.java | 24 ++-- .../kuangcp/tank/util/ListenEventGroup.java | 36 +++-- .../com/github/kuangcp/tank/util/Saved.java | 10 +- .../github/kuangcp/tank/util/TankTool.java | 44 +++--- .../com/github/kuangcp/tank/v1/MyPanel.java | 14 +- .../com/github/kuangcp/tank/v2/MyPanel.java | 12 +- .../com/github/kuangcp/tank/v3/MainFrame.java | 30 +++-- 16 files changed, 303 insertions(+), 291 deletions(-) diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java b/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java index f26e9675..9dba9ebd 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java @@ -7,8 +7,11 @@ import com.github.kuangcp.tank.util.TankTool; import lombok.extern.slf4j.Slf4j; -import java.util.Vector; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; import java.util.concurrent.ExecutorService; +import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.atomic.AtomicLong; /** @@ -21,20 +24,24 @@ public class EnemyTank extends Tank implements Runnable { private static final AtomicLong counter = new AtomicLong(); // 敌人 id public long id; - public Vector ds = new Vector<>();//子弹集合 + + public List shotList = Collections.synchronizedList(new ArrayList<>());//子弹集合 + private long lastShotMs = 0; + private long shotCDMs = 168; private final ExecutorService shotExecutePool; - public Vector ets; - public Vector bricks; - public Vector irons; + + public List ets; + public List bricks; + public List irons; public Shot s = null; - public int maxLiveShot = 10; //子弹线程存活的最大数 + public int maxLiveShot = 3; //子弹线程存活的最大数 public boolean delayRemove = false; Hero hero; - boolean with = true; //同类之间有无重叠(true是可以往前走) + boolean overlap = true; //同类之间允许重叠 boolean bri = true; - boolean to = true; + boolean ableMove = true; //继承了属性,即使直接使用父类的构造器,构造器也一定要显式声明 public EnemyTank(int x, int y, int speed, int direct) { @@ -43,24 +50,25 @@ public EnemyTank(int x, int y, int speed, int direct) { this.direct = direct; this.speed = speed; this.alive = true; + this.life = ThreadLocalRandom.current().nextInt(3) + 1; this.id = counter.addAndGet(1); - this.shotExecutePool = ExecutePool.buildFixedPool("enemyShot-" + id, this.maxLiveShot / 2); + this.shotExecutePool = ExecutePool.buildFixedPool("enemyShot-" + id, this.maxLiveShot); // log.info("create new Demons. {}", id); } - public void SetInfo(Hero hero, Vector ets, Vector bricks, Vector irons) { + public void SetInfo(Hero hero, List ets, List bricks, List irons) { this.hero = hero; this.ets = ets; this.bricks = bricks; this.irons = irons; } - public boolean isWith() { - return with; + public boolean isOverlap() { + return overlap; } - public void setWith(boolean with) { - this.with = with; + public void setOverlap(boolean overlap) { + this.overlap = overlap; } public boolean isBri() { @@ -83,33 +91,39 @@ public void setBri(boolean bri) { */ public void shotEnemy() { //判断坦克方向来 初始化子弹的起始发射位置 - if (!this.alive || ds.size() >= this.maxLiveShot) { + final long nowMs = System.currentTimeMillis(); + if (lastShotMs != 0 && nowMs - lastShotMs < shotCDMs) { + return; + } + if (this.shotList.size() >= this.maxLiveShot || !this.isAlive()) { return; } + switch (this.getDirect()) { case 0: {//0123 代表 上下左右 s = new Shot(this.getX() - 1, this.getY() - 15, 0); - ds.add(s); + shotList.add(s); break; } case 1: { s = new Shot(this.getX() - 2, this.getY() + 15, 1); - ds.add(s); + shotList.add(s); break; } case 2: { s = new Shot(this.getX() - 15 - 2, this.getY(), 2); - ds.add(s); + shotList.add(s); break; } case 3: { s = new Shot(this.getX() + 15 - 2, this.getY() - 1, 3); - ds.add(s); + shotList.add(s); break; } } //启动子弹线程 shotExecutePool.execute(s); + lastShotMs = nowMs; } // public void run (){ @@ -301,22 +315,22 @@ public boolean TouchOther() { //只是移动一步的函数 public boolean toUp() { if (y > 30) { - to = true; + ableMove = true; for (EnemyTank et : ets) { if (!TankTool.ablePass(this, et)) { - to = (false); + ableMove = (false); break; } } for (Brick brick : bricks) { if (TankTool.ablePass(this, brick)) - to = false; + ableMove = false; } for (Iron iron : irons) { if (TankTool.ablePass(this, iron)) - to = false; + ableMove = false; } - if (to && TankTool.ablePass(this, hero)) { + if (ableMove && TankTool.ablePass(this, hero)) { y -= speed; try { Thread.sleep(100); @@ -333,22 +347,22 @@ public boolean toUp() { public boolean toDown() { if (y < 530) { - to = true; + ableMove = true; for (EnemyTank et : ets) { if (!TankTool.ablePass(this, et)) { - to = (false); + ableMove = (false); break; } } for (Brick brick : bricks) { if (TankTool.ablePass(this, brick)) - to = false; + ableMove = false; } for (Iron iron : irons) { if (TankTool.ablePass(this, iron)) - to = false; + ableMove = false; } - if (to && TankTool.ablePass(this, hero)) { + if (ableMove && TankTool.ablePass(this, hero)) { y += speed; try { Thread.sleep(100); @@ -366,22 +380,22 @@ public boolean toDown() { public boolean toLeft() { if (x > 30) { - to = true; + ableMove = true; for (EnemyTank et : ets) { if (!TankTool.ablePass(this, et)) { - to = false; + ableMove = false; break; } } for (Brick brick : bricks) { if (TankTool.ablePass(this, brick)) - to = false; + ableMove = false; } for (Iron iron : irons) { if (TankTool.ablePass(this, iron)) - to = false; + ableMove = false; } - if (to && TankTool.ablePass(this, hero)) { + if (ableMove && TankTool.ablePass(this, hero)) { x -= speed; try { Thread.sleep(100); @@ -398,22 +412,22 @@ public boolean toLeft() { public boolean toRight() { if (x < 710) { - to = true; + ableMove = true; for (EnemyTank et : ets) { if (!TankTool.ablePass(this, et)) { - to = (false); + ableMove = (false); break; } } for (Brick brick : bricks) { if (TankTool.ablePass(this, brick)) - to = false; + ableMove = false; } for (Iron iron : irons) { if (TankTool.ablePass(this, iron)) - to = false; + ableMove = false; } - if (to && TankTool.ablePass(this, hero)) { + if (ableMove && TankTool.ablePass(this, hero)) { x += speed; try { Thread.sleep(100); @@ -436,6 +450,13 @@ public boolean toRight() { public void run() { // actionModeRun(); run2(); + + if (this.isAbort()) { + for (Shot d : this.shotList) { + d.isLive = false; + } + shotExecutePool.shutdown(); + } } /** @@ -510,7 +531,7 @@ public void run2() { min++; // if(!bri)this.direct = (int)(Math.random()*4); if (y > 30) { - if (TankTool.ablePass(this, hero) && with) y -= speed; + if (TankTool.ablePass(this, hero) && overlap) y -= speed; // if(bri)y-=speed; // else {y+=speed;this.direct = 1;} // else continue; @@ -534,18 +555,15 @@ public void run2() { // if(!bri)this.direct = (int)(Math.random()*4); if (y < 530) { - if (TankTool.ablePass(this, hero) && with && bri) y += speed; + if (TankTool.ablePass(this, hero) && overlap && bri) y += speed; // if(bri) y+=speed; // else {y-=speed;this.direct = 0;} // else continue; else break; - if (min % 27 == 0) + if (min % 27 == 0) { this.shotEnemy(); - try { - Thread.sleep(50); - } catch (Exception e) { - log.error("", e); } + TankTool.yieldMsTime(50); } } @@ -557,7 +575,7 @@ public void run2() { // if(!bri)this.direct = (int)(Math.random()*4); if (x > 30) { - if (TankTool.ablePass(this, hero) && with && bri) x -= speed; + if (TankTool.ablePass(this, hero) && overlap && bri) x -= speed; // if(bri)x-=speed; // else{x+=speed;this.direct = 3;} // else continue; @@ -580,7 +598,7 @@ public void run2() { // if(!bri)this.direct = (int)(Math.random()*4); if (x < 710) { - if (TankTool.ablePass(this, hero) && with && bri) { + if (TankTool.ablePass(this, hero) && overlap && bri) { x += speed; } // if(bri)x+=speed; diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/Hero.java b/gui/src/main/java/com/github/kuangcp/tank/domain/Hero.java index ea4fa192..82642009 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/domain/Hero.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/Hero.java @@ -2,7 +2,6 @@ import com.github.kuangcp.tank.util.ExecutePool; -import com.github.kuangcp.tank.util.TankTool; import java.awt.*; import java.util.Vector; @@ -13,29 +12,21 @@ public class Hero extends Tank { //子弹集合 public Vector shotList = new Vector<>(); private final ExecutorService shotExecutePool; - private long lastShotNs = 0; - private long shotCD = 224_000_000; + private long lastShotMs = 0; + private long shotCDMs = 268; - public Shot s = null;//子弹 + public Shot shot = null;//子弹 public Graphics g; //画笔不可少 - public boolean flag = false;//是否击中 - static int round = 0; - public Vector ets;//只是一个指针(引用) - public Vector bricks; - public Vector irons; - public boolean to = true; private int prize = 0;//击敌个数 public int maxLiveShot = 8;//主坦克子弹线程存活的最大数 int speed = 3; - static int Life = 10; - public int getLife() { - return Life; + return life; } public void setLife(int life) { - Life = life; + this.life = life; } public int getSpeed() { @@ -63,22 +54,14 @@ public void addPrize(int delta) { } - public Hero(int x, int y, int speed, Vector ets, Vector bricks, Vector irons) { + public Hero(int x, int y, int speed) { super(x, y, speed); -// this.Life = 10; -// 要给g分配内存 初始化对象 -// g = new Graphics(); -// g.setColor(Color.yellow); - this.ets = ets; - this.bricks = bricks; - this.irons = irons; - this.shotExecutePool = ExecutePool.buildFixedPool("heroShot", maxLiveShot); } public void shotEnemy() { - final long curNs = System.nanoTime(); - if (lastShotNs != 0 && curNs - lastShotNs < shotCD) { + final long nowMs = System.currentTimeMillis(); + if (lastShotMs != 0 && nowMs - lastShotMs < shotCDMs) { return; } if (this.shotList.size() >= this.maxLiveShot || !this.isAlive()) { @@ -88,97 +71,40 @@ public void shotEnemy() { //判断坦克方向来 初始化子弹的起始发射位置 switch (this.getDirect()) { case 0://0123 代表 上下左右 - s = new Shot(this.getX() - 1, this.getY() - 15, 0); - shotList.add(s); + shot = new Shot(this.getX() - 1, this.getY() - 15, 0); + shotList.add(shot); break; case 1: - s = new Shot(this.getX() - 2, this.getY() + 15, 1); - shotList.add(s); + shot = new Shot(this.getX() - 2, this.getY() + 15, 1); + shotList.add(shot); break; case 2: - s = new Shot(this.getX() - 15 - 2, this.getY(), 2); - shotList.add(s); + shot = new Shot(this.getX() - 15 - 2, this.getY(), 2); + shotList.add(shot); break; case 3: - s = new Shot(this.getX() + 15 - 2, this.getY() - 1, 3); - shotList.add(s); + shot = new Shot(this.getX() + 15 - 2, this.getY() - 1, 3); + shotList.add(shot); break; } //启动子弹线程 - shotExecutePool.execute(s); - lastShotNs = curNs; + shotExecutePool.execute(shot); + lastShotMs = nowMs; } - //移动的函数 - public void moveup() { - to = true; - //检测敌方坦克碰撞 - for (EnemyTank et : ets) { - if (!TankTool.ablePass(this, et)) - to = false; - } - //检测障碍物 -// for (Brick brick : bricks) { -// if (TankTool.hasHint(this, brick)) -// to = false; -// } -// for (Iron iron : irons) { -// if (TankTool.hasHint(this, iron)) -// to = false; -// } - if (to) { - setY(getY() - getSpeed()); - } + public void moveUp() { + setY(getY() - getSpeed()); } - public void movedown() { - to = true; - for (EnemyTank et : ets) - if (!TankTool.ablePass(this, et)) - to = false; -// for (Brick brick : bricks) { -// if (TankTool.hasHint(this, brick)) -// to = false; -// } -// for (Iron iron : irons) { -// if (TankTool.hasHint(this, iron)) -// to = false; -// } - if (to) - setY(getY() + getSpeed()); + public void moveDown() { + setY(getY() + getSpeed()); } - public void moveleft() { - to = true; - for (EnemyTank et : ets) - if (!TankTool.ablePass(this, et)) - to = false; -// for (Brick brick : bricks) { -// if (TankTool.hasHint(this, brick)) -// to = false; -// } -// for (Iron iron : irons) { -// if (TankTool.hasHint(this, iron)) -// to = false; -// } - if (to) - setX(getX() - getSpeed()); + public void moveLeft() { + setX(getX() - getSpeed()); } - public void moveright() { - to = true; - for (EnemyTank et : ets) - if (!TankTool.ablePass(this, et)) - to = false; -// for (Brick brick : bricks) { -// if (TankTool.hasHint(this, brick)) -// to = false; -// } -// for (Iron iron : irons) { -// if (TankTool.hasHint(this, iron)) -// to = false; -// } - if (to) - setX(getX() + getSpeed()); + public void moveRight() { + setX(getX() + getSpeed()); } } diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/Hinder.java b/gui/src/main/java/com/github/kuangcp/tank/domain/Hinder.java index 3f04f556..d8622ccd 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/domain/Hinder.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/Hinder.java @@ -4,14 +4,14 @@ * 障碍物的最基本类 */ public abstract class Hinder { - int hx, hy;//障碍物坐标 绘图坐标 也就是左上角的坐标 - boolean alive;//生存状态 + + int hx, hy;//障碍物绘图坐标 左上角顶点 的坐标 + boolean alive;//存活状态 public Hinder(int hx, int hy) { alive = true; this.hx = hx; this.hy = hy; - } public Hinder() { diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/Tank.java b/gui/src/main/java/com/github/kuangcp/tank/domain/Tank.java index 27f933e2..a2c0c1ea 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/domain/Tank.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/Tank.java @@ -10,9 +10,13 @@ public class Tank { int direct = 0; // 初始方向 int type = 0; // 坦克的种类 int speed = 5; // 前进的步长 - boolean alive = true;//是否存活 + boolean alive = true;// 是否存活 + boolean abort = false; // 重开一局等外部终止因素 int life = 1;//生命值 + int halfWidth = 10; + int halfHeight = 15; + public int getLife() { return life; } @@ -21,6 +25,14 @@ public void setLife(int life) { this.life = life; } + public boolean isAbort() { + return abort; + } + + public void setAbort(boolean abort) { + this.abort = abort; + } + public boolean isAlive() { return alive; } @@ -57,6 +69,22 @@ public int getX() { return x; } + public int getHalfWidth() { + return halfWidth; + } + + public void setHalfWidth(int halfWidth) { + this.halfWidth = halfWidth; + } + + public int getHalfHeight() { + return halfHeight; + } + + public void setHalfHeight(int halfHeight) { + this.halfHeight = halfHeight; + } + public void setX(int x) { this.x = x; } diff --git a/gui/src/main/java/com/github/kuangcp/tank/panel/HeroInfoPanel.java b/gui/src/main/java/com/github/kuangcp/tank/panel/HeroInfoPanel.java index 3d9cb44c..7df2ff77 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/panel/HeroInfoPanel.java +++ b/gui/src/main/java/com/github/kuangcp/tank/panel/HeroInfoPanel.java @@ -2,13 +2,15 @@ import com.github.kuangcp.tank.domain.EnemyTank; -import com.github.kuangcp.tank.domain.Hero; import com.github.kuangcp.tank.thread.ExitFlagRunnable; +import com.github.kuangcp.tank.util.TankTool; +import com.github.kuangcp.tank.v3.PlayStageMgr; import lombok.extern.slf4j.Slf4j; import javax.swing.*; import java.awt.*; -import java.util.Vector; +import java.util.List; +import java.util.Objects; /** * 就是一个用来显示属性的画板 @@ -21,8 +23,7 @@ public class HeroInfoPanel extends JPanel implements ExitFlagRunnable { JLabel jl1; JLabel prizeNo; - Hero hero; - Vector ets; + List ets; private volatile boolean exit = false; @@ -30,9 +31,8 @@ public void exit() { this.exit = true; } - public HeroInfoPanel(JLabel jl1, Hero hero, Vector ets, JLabel prizeNo) { + public HeroInfoPanel(JLabel jl1, List ets, JLabel prizeNo) { this.jl1 = jl1; - this.hero = hero; this.ets = ets; this.prizeNo = prizeNo; // jl1 =new JLabel("生命值: "+Hero.Life/3); @@ -43,6 +43,10 @@ public HeroInfoPanel(JLabel jl1, Hero hero, Vector ets, JLabel prizeN public void paint(Graphics g) { super.paint(g); + if (Objects.isNull(PlayStageMgr.instance) || !PlayStageMgr.instance.startLogic) { + return; + } + int X = 20, Y = 20; int x, y; /*主坦克*/ @@ -83,6 +87,10 @@ public void paint(Graphics g) { public void run() { while (!exit) { + TankTool.yieldMsTime(500); + if (!PlayStageMgr.hasStart()) { + continue; + } /* * 如果是下面的方法的话,来不及设置成0 就退出线程了,所以把设置0放在了退出线程那里更好 */ @@ -92,21 +100,16 @@ public void run() { // jl1.setText("生命值: "+hero.getLife()); // } - if (hero.isAlive()) {//生命值的自动更改 - jl1.setText(" :" + hero.getLife() + " : " + ets.size()); + if (PlayStageMgr.instance.hero.isAlive()) {//生命值的自动更改 + jl1.setText(" :" + PlayStageMgr.instance.hero.getLife() + " : " + ets.size()); } else { jl1.setText(" :0" + " : " + ets.size()); } - prizeNo.setText("战绩:" + hero.getPrize()); + prizeNo.setText("战绩:" + PlayStageMgr.instance.hero.getPrize()); // TankGame3.mp3.add(TankGame3.jl1 ); // System.out.println("画板执行了"); repaint(); - try { - Thread.sleep(200); - } catch (InterruptedException e) { - log.error("", e); - } //不加的话线程没有退出 // if(hero.getLife() <= 0){ // jl1.setText(" :0"+" : "+ets.size()); diff --git a/gui/src/main/java/com/github/kuangcp/tank/panel/StageActionPanel.java b/gui/src/main/java/com/github/kuangcp/tank/panel/StageActionPanel.java index 69eb8011..5ced7c7b 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/panel/StageActionPanel.java +++ b/gui/src/main/java/com/github/kuangcp/tank/panel/StageActionPanel.java @@ -11,6 +11,7 @@ import com.github.kuangcp.tank.util.Audio; import com.github.kuangcp.tank.util.Saved; import com.github.kuangcp.tank.v3.MainFrame; +import com.github.kuangcp.tank.v3.PlayStageMgr; import com.github.kuangcp.tank.v3.SettingFrame; import lombok.extern.slf4j.Slf4j; @@ -18,8 +19,8 @@ import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.util.List; import java.util.Objects; -import java.util.Vector; /** * 用来放按钮的面板 @@ -31,9 +32,9 @@ public class StageActionPanel extends JPanel implements ActionListener { static Thread actionThread = null; MainFrame frame; Hero hero; - Vector ets; - Vector irons; - Vector bricks; + List ets; + List irons; + List bricks; int[][] ETS; int[] myself; Audio beginAudio; @@ -41,6 +42,7 @@ public class StageActionPanel extends JPanel implements ActionListener { @Override public void actionPerformed(ActionEvent ae) { //上面是不合理的,会有隐藏的bug在里面,这种做法要抛弃 + if (ae.getActionCommand().equals(StageCommand.START)) { this.startNewStage(); } @@ -51,7 +53,7 @@ public void actionPerformed(ActionEvent ae) { } if (ae.getActionCommand().equals("暂停")) { System.out.println("暂停"); - frame.mp.hero.setSpeed(0); + frame.groundPanel.hero.setSpeed(0); Shot.setSpeed(0); for (EnemyTank et : ets) { et.setSpeed(0); @@ -60,7 +62,7 @@ public void actionPerformed(ActionEvent ae) { } if (ae.getActionCommand().equals("继续")) { System.out.println("继续"); - frame.mp.hero.setSpeed(3); + frame.groundPanel.hero.setSpeed(3); Shot.setSpeed(8); for (EnemyTank et : ets) { et.setSpeed(2); @@ -112,11 +114,13 @@ public void actionPerformed(ActionEvent ae) { private void startNewStage() { if (Objects.nonNull(actionThread) && !actionThread.isInterrupted()) { + PlayStageMgr.instance.markStopLogic(); log.info("clean last stage"); actionThread.interrupt(); // TODO clean for (EnemyTank et : ets) { et.setAlive(false); + et.setAbort(true); } } @@ -124,17 +128,22 @@ private void startNewStage() { TankGroundPanel.newStage = true; Shot.setSpeed(8); frame.remove(frame.jsp1); - actionThread = new Thread(frame); + + log.info("start new stage frame thread"); + actionThread = new Thread(() -> { + frame.run(); + if (beginAudio != null) { + beginAudio.setLive(false); + } + beginAudio = new Audio("./src/RE/GameBegin.wav"); +// beginAudio.start(); + frame.groundPanel.startNewStage(); + }); actionThread.setName("actionThread"); actionThread.start();//将画板线程开启 - if (beginAudio != null) { - beginAudio.setLive(false); - } - beginAudio = new Audio("./src/RE/GameBegin.wav"); -// beginAudio.start(); } - public StageActionPanel(MainFrame frame, Hero he, Vector ets, Vector bricks, Vector irons, int[][] ETS, int[] myself) { + public StageActionPanel(MainFrame frame, Hero he, java.util.List ets, java.util.List bricks, List irons, int[][] ETS, int[] myself) { this.frame = frame; this.hero = he; this.ets = ets; diff --git a/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java b/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java index 414f3c2c..748ac8bd 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java +++ b/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java @@ -2,18 +2,19 @@ package com.github.kuangcp.tank.panel; -import com.github.kuangcp.tank.util.ExecutePool; -import com.github.kuangcp.tank.util.TankTool; import com.github.kuangcp.tank.domain.Brick; import com.github.kuangcp.tank.domain.EnemyTank; import com.github.kuangcp.tank.domain.Hero; import com.github.kuangcp.tank.domain.Iron; import com.github.kuangcp.tank.domain.Shot; -import com.github.kuangcp.tank.util.ListenEventGroup; -import com.github.kuangcp.tank.util.KeyListener; import com.github.kuangcp.tank.resource.AvatarMgr; import com.github.kuangcp.tank.resource.BombMgr; import com.github.kuangcp.tank.thread.ExitFlagRunnable; +import com.github.kuangcp.tank.util.ExecutePool; +import com.github.kuangcp.tank.util.KeyListener; +import com.github.kuangcp.tank.util.ListenEventGroup; +import com.github.kuangcp.tank.util.TankTool; +import com.github.kuangcp.tank.v3.PlayStageMgr; import lombok.extern.slf4j.Slf4j; import javax.imageio.ImageIO; @@ -21,9 +22,10 @@ import java.awt.*; import java.awt.event.KeyEvent; import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; import java.util.Objects; -import java.util.Vector; -import java.util.concurrent.ExecutorService; import java.util.concurrent.TimeUnit; @@ -34,21 +36,21 @@ @Slf4j public class TankGroundPanel extends JPanel implements java.awt.event.KeyListener, ExitFlagRunnable { - public Hero hero; + public volatile Hero hero; public KeyListener keyListener; public static boolean newStage = true; // 敌人的数量 public static int enSize = 10; //定义一个 泛型的集合ets 表示敌人坦克集合 - public Vector enemyList = new Vector<>(); + public List enemyList = Collections.synchronizedList(new ArrayList<>()); //定义砖块集合 - public Vector bricks = new Vector<>(); - public Vector irons = new Vector<>(); + public List bricks = Collections.synchronizedList(new ArrayList<>()); + public List irons = Collections.synchronizedList(new ArrayList<>()); + //所有按下键的code集合 public static int[][] enemyTankMap = new int[12][2]; public static int[] myself = new int[6]; - private static final ExecutorService delayPool = ExecutePool.buildFixedPool("enemyDelayRemove", 3); Image overImg = null; Image winImg = null; @@ -63,17 +65,21 @@ public void exit() { * 画板的构造函数 用来初始化对象 * 多个敌方坦克应该用集合类,而且要考虑线程安全所以不能用ArrayList只能用Vector */ - public TankGroundPanel() { + public void startNewStage() { +// log.info("start new Stage"); //创建英雄坦克 if (newStage) {//正常启动并创建坦克线程 - hero = new Hero(480, 500, 3, enemyList, bricks, irons);//坐标和步长和敌人坦克集合 + hero = new Hero(480, 500, 3);//坐标和步长和敌人坦克集合 hero.setLife(10);//设置生命值 } else { - hero = new Hero(myself[0], myself[1], 3, enemyList, bricks, irons); + hero = new Hero(myself[0], myself[1], 3); hero.setLife(myself[2]); hero.setPrize(myself[3]); } + log.info("hero={}", hero); + PlayStageMgr.init(hero, enemyList, bricks, irons); + //多键监听实现 keyListener = new KeyListener(ListenEventGroup.instance, hero, this); Thread p = new Thread(keyListener); @@ -136,7 +142,7 @@ public TankGroundPanel() { //创建砖块 // createB(bricks, 40, 40, 200, 400); // createB(bricks, 200, 40, 400, 100); -// createB(bricks, 400, 40, 700, 400); +// createB(bricks, 400, 40, 700, 400); // createB(bricks, 200, 300, 400, 400); // createB(bricks, 40, 40, 700, 400); @@ -177,12 +183,17 @@ public TankGroundPanel() { } catch (IOException e) { log.error("", e); } + PlayStageMgr.instance.markStartLogic(); } @Override public void paint(Graphics g) { /*画出坦克运动区域 */ super.paint(g); + if (!PlayStageMgr.hasStart() || Objects.isNull(hero)) { + return; + } + g.setColor(new Color(0, 0, 0)); g.fillRect(0, 0, 760, 560);//填满一个黑色矩形,说明了是一整个JPanel在JFrame上的 g.setColor(Color.green); @@ -221,14 +232,14 @@ public void paint(Graphics g) { /*画出主坦克*/ if (hero.isAlive()) { for (EnemyTank et : enemyList) { - BombMgr.instance.checkBong(hero, et.ds); + BombMgr.instance.checkBong(hero, et.shotList); } this.drawTank(hero.getX(), hero.getY(), g, hero.getDirect(), hero.getType()); } - /*画出自己的子弹*/ //画子弹是可以封装成一个方法的 - // 从ss 这个子弹集合中 取出每颗子弹,并画出来 + // 画出自己的子弹画子弹是可以封装成一个方法的 + // 从ss 这个子弹集合中 取出每颗子弹,并画出来 for (int i = 0; i < hero.shotList.size(); i++) { myShot = hero.shotList.get(i); for (Brick brick : bricks) { @@ -242,7 +253,8 @@ public void paint(Graphics g) { hero.setAlive(false); } if (hero.shotList.get(i) != null && hero.shotList.get(i).isLive) { - g.draw3DRect(myShot.sx, myShot.sy, 1, 1, false); + g.setColor(Color.YELLOW); + g.draw3DRect(myShot.sx, myShot.sy, 5, 5, false); } //子弹线程死了 就要把它从集合中删除 @@ -253,8 +265,8 @@ public void paint(Graphics g) { /*敌人子弹*/ for (EnemyTank et : enemyList) { - for (int i = 0; i < et.ds.size(); i++) { - myShot = et.ds.get(i); + for (int i = 0; i < et.shotList.size(); i++) { + myShot = et.shotList.get(i); for (Brick brick : bricks) { TankTool.judgeHint(myShot, brick); } @@ -265,13 +277,13 @@ public void paint(Graphics g) { myShot.isLive = false; hero.setAlive(false); } - if (et.ds.get(i) != null && et.ds.get(i).isLive) { + if (et.shotList.get(i) != null && et.shotList.get(i).isLive) { g.setColor(Color.cyan); g.draw3DRect(myShot.sx, myShot.sy, 1, 1, false); } if (!myShot.isLive) { - et.ds.remove(myShot); + et.shotList.remove(myShot); } } } @@ -359,7 +371,7 @@ public void paint(Graphics g) { continue; } demon.delayRemove = true; - delayPool.execute(() -> { + ExecutePool.delayPool.execute(() -> { try { TimeUnit.SECONDS.sleep(8); } catch (InterruptedException e) { @@ -388,7 +400,6 @@ public void paint(Graphics g) { * 画出所有坦克的函数 XY是坦克中心的坐标,不是画图参照点 */ public void drawTank(int X, int Y, Graphics g, int direct, int type) { - //判断坦克类型 //系统画图函数的参照点 int x, y; @@ -513,7 +524,7 @@ public void keyTyped(KeyEvent arg0) { /** * 创建 一块矩形的砖(20*10) 的函数 */ - public void createB(Vector bricks, int startX, int startY, int endX, int endY) { + public void createB(List bricks, int startX, int startY, int endX, int endY) { for (int i = startX; i < endX; i += 20) { for (int j = startY; j < endY; j += 10) { Brick bs = new Brick(i, j); @@ -526,7 +537,7 @@ public void createB(Vector bricks, int startX, int startY, int endX, int /** * 创建铁块(20*10) */ - public void createI(Vector irons, int startX, int startY, int endX, int endY) { + public void createI(List irons, int startX, int startY, int endX, int endY) { for (int i = startX; i < endX; i += 20) { for (int j = startY; j < endY; j += 10) { Iron bs = new Iron(i, j); @@ -561,7 +572,7 @@ public void run() { } TankTool.yieldTime(fpsTime - waste, TimeUnit.NANOSECONDS); - if (!hero.isAlive()) { + if (Objects.nonNull(hero) && !hero.isAlive()) { break; } } diff --git a/gui/src/main/java/com/github/kuangcp/tank/resource/BombMgr.java b/gui/src/main/java/com/github/kuangcp/tank/resource/BombMgr.java index a2fb1152..0a316e09 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/resource/BombMgr.java +++ b/gui/src/main/java/com/github/kuangcp/tank/resource/BombMgr.java @@ -65,9 +65,7 @@ public void drawBomb(Graphics g, JPanel panel) { //炸弹的生命值为零 移出集合 if (b.life == 0) { bombs.remove(b); - } -// hero.LifeDown(); // System.out.println("size = "+bombs.size()); } } @@ -92,8 +90,13 @@ private void checkBong(Tank t, Shot s) { t.getY() - 15 <= s.sy && t.getY() + 15 >= s.sy) { s.isLive = false; - t.setLife(t.getLife() - 1);//生命值减一 - if (t.getLife() == 0) t.setAlive(false); + + t.setLife(t.getLife() - 1); + + if (t.getLife() <= 0) { + t.setAlive(false); + } + //创建一个炸弹,放入集合 Bomb b = new Bomb(t.getX() - 10, t.getY() - 15);//敌方的坐标 bombs.add(b); diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/ExecutePool.java b/gui/src/main/java/com/github/kuangcp/tank/util/ExecutePool.java index d5923ef5..dbb4640c 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/util/ExecutePool.java +++ b/gui/src/main/java/com/github/kuangcp/tank/util/ExecutePool.java @@ -11,6 +11,10 @@ */ public class ExecutePool { + public static final ExecutorService delayPool = ExecutePool.buildFixedPool("enemyDelay", 4); + + public static final ExecutorService shotPool = ExecutePool.buildFixedPool("enemyShot", 30); + public static final AtomicLong totalCounter = new AtomicLong(); /** diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/KeyListener.java b/gui/src/main/java/com/github/kuangcp/tank/util/KeyListener.java index 9230463e..0093af40 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/util/KeyListener.java +++ b/gui/src/main/java/com/github/kuangcp/tank/util/KeyListener.java @@ -5,6 +5,7 @@ import com.github.kuangcp.tank.domain.Hero; import com.github.kuangcp.tank.panel.TankGroundPanel; import com.github.kuangcp.tank.thread.ExitFlagRunnable; +import com.github.kuangcp.tank.v3.PlayStageMgr; import lombok.extern.slf4j.Slf4j; /** @@ -39,26 +40,33 @@ public void run() { if (eventGroup.hasPressMoveEvent()) { // log.info("eventGroup={}", eventGroup); } + if (eventGroup.isLeft()) { hero.setDirect(DirectType.LEFT); - if ((hero.getX() - 10) > 20) - hero.moveleft(); + if ((hero.getX() - 10) > 20 && PlayStageMgr.instance.ableToMove(hero)) { + hero.moveLeft(); + } } if (eventGroup.isRight()) { hero.setDirect(DirectType.RIGHT); - if ((hero.getX() + 15) < 742) - hero.moveright(); + if ((hero.getX() + 15) < 742 && PlayStageMgr.instance.ableToMove(hero)) { + hero.moveRight(); + } } + if (eventGroup.isDown()) { hero.setDirect(DirectType.DOWN); - if ((hero.getY() - 15) < 515) - hero.movedown(); + if ((hero.getY() - 15) < 515 && PlayStageMgr.instance.ableToMove(hero)) { + hero.moveDown(); + } } + if (eventGroup.isUp()) { hero.setDirect(DirectType.UP); - if ((hero.getY() - 13) > 20) - hero.moveup(); + if ((hero.getY() - 13) > 20 && PlayStageMgr.instance.ableToMove(hero)) { + hero.moveUp(); + } } if (eventGroup.isShot()) { diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/ListenEventGroup.java b/gui/src/main/java/com/github/kuangcp/tank/util/ListenEventGroup.java index dd8de1b4..c7c56b76 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/util/ListenEventGroup.java +++ b/gui/src/main/java/com/github/kuangcp/tank/util/ListenEventGroup.java @@ -3,6 +3,9 @@ import com.github.kuangcp.tank.panel.TankGroundPanel; import java.awt.event.KeyEvent; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; /** * @author https://github.com/kuangcp on 2021-09-06 02:58 @@ -18,27 +21,20 @@ public class ListenEventGroup { private volatile boolean right; private volatile boolean shot; - public void handleRelease(KeyEvent re){ - switch (re.getKeyCode()) { - case KeyEvent.VK_A: - this.setLeft(false); - break; - case KeyEvent.VK_D: - this.setRight(false); - break; - case KeyEvent.VK_S: - this.setDown(false); - break; - case KeyEvent.VK_W: - this.setUp(false); - break; - case KeyEvent.VK_SPACE: - this.setShot(false); - break; - default: - break; - } + private final Map actionMap = new HashMap<>(); + + public ListenEventGroup() { + actionMap.put(KeyEvent.VK_A, () -> this.left = false); + actionMap.put(KeyEvent.VK_D, () -> this.right = false); + actionMap.put(KeyEvent.VK_S, () -> this.down = false); + actionMap.put(KeyEvent.VK_W, () -> this.up = false); + actionMap.put(KeyEvent.VK_SPACE, () -> this.shot = false); + } + + public void handleRelease(KeyEvent re) { + Optional.ofNullable(actionMap.get(re.getKeyCode())).ifPresent(Runnable::run); } + public boolean isUp() { return up; } diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/Saved.java b/gui/src/main/java/com/github/kuangcp/tank/util/Saved.java index 25b11cd7..24683c58 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/util/Saved.java +++ b/gui/src/main/java/com/github/kuangcp/tank/util/Saved.java @@ -18,7 +18,7 @@ import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; -import java.util.Vector; +import java.util.List; /** * 用文件系统实现保存数据的功能,真繁琐 @@ -30,14 +30,14 @@ * 用数据库实现了续上局及其存盘退出,比文件的操作简单多了。。。 */ public class Saved { - private Vector ets; private Hero hero; - private Vector irons; - private Vector bricks; + private List ets; + private List irons; + private List bricks; int[][] ETS; int[] myself; - public Saved(Vector ets, Hero hero, Vector bricks, Vector irons, int[][] ETS, int[] myself) { + public Saved(List ets, Hero hero, List bricks, List irons, int[][] ETS, int[] myself) { this.ets = ets; this.hero = hero; this.bricks = bricks; diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/TankTool.java b/gui/src/main/java/com/github/kuangcp/tank/util/TankTool.java index d0eac6c7..9b1eade4 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/util/TankTool.java +++ b/gui/src/main/java/com/github/kuangcp/tank/util/TankTool.java @@ -23,6 +23,7 @@ public static boolean ablePass(Tank me, Tank you) { if (!me.isAlive() || !you.isAlive()) { return true; } + boolean flag = true; switch (you.getDirect()) {//对方 上下 case 0: @@ -168,38 +169,41 @@ public static boolean ablePass(Tank me, Tank you) { */ public static boolean ablePass(Tank t, Hinder h) { int hx = 20, hy = 10; + final int halfHeight = t.getHalfHeight(); + final int halfWidth = t.getHalfWidth(); + switch (t.getDirect()) { case DirectType.UP: - if (t.getX() - 10 >= h.getHx() && t.getX() - 10 <= h.getHx() + hx - && t.getY() - 15 >= h.getHy() && t.getY() - 15 <= h.getHy() + hy + if (t.getX() - halfWidth >= h.getHx() && t.getX() - halfWidth <= h.getHx() + hx + && t.getY() - halfHeight >= h.getHy() && t.getY() - halfHeight <= h.getHy() + hy || t.getX() >= h.getHx() && t.getX() <= h.getHx() + hx - && t.getY() - 15 >= h.getHy() && t.getY() - 15 <= h.getHy() + hy - || t.getX() + 10 >= h.getHx() && t.getX() + 10 <= h.getHx() + hx - && t.getY() - 15 >= h.getHy() && t.getY() - 15 <= h.getHy() + hy) + && t.getY() - halfHeight >= h.getHy() && t.getY() - halfHeight <= h.getHy() + hy + || t.getX() + halfWidth >= h.getHx() && t.getX() + halfWidth <= h.getHx() + hx + && t.getY() - halfHeight >= h.getHy() && t.getY() - halfHeight <= h.getHy() + hy) return true; case DirectType.DOWN: - if (t.getX() - 10 >= h.getHx() && t.getX() - 10 <= h.getHx() + hx - && t.getY() + 15 >= h.getHy() && t.getY() + 15 <= h.getHy() + hy + if (t.getX() - halfWidth >= h.getHx() && t.getX() - halfWidth <= h.getHx() + hx + && t.getY() + halfHeight >= h.getHy() && t.getY() + halfHeight <= h.getHy() + hy || t.getX() >= h.getHx() && t.getX() <= h.getHx() + hx - && t.getY() + 15 >= h.getHy() && t.getY() + 15 <= h.getHy() + hy - || t.getX() + 10 >= h.getHx() && t.getX() + 10 <= h.getHx() + hx - && t.getY() + 15 >= h.getHy() && t.getY() + 15 <= h.getHy() + hy) + && t.getY() + halfHeight >= h.getHy() && t.getY() + halfHeight <= h.getHy() + hy + || t.getX() + halfWidth >= h.getHx() && t.getX() + halfWidth <= h.getHx() + hx + && t.getY() + halfHeight >= h.getHy() && t.getY() + halfHeight <= h.getHy() + hy) return true; case DirectType.LEFT: - if (t.getX() - 15 >= h.getHx() && t.getX() - 15 <= h.getHx() + hx - && t.getY() - 10 >= h.getHy() && t.getY() - 10 <= h.getHy() + hy - || t.getX() - 15 >= h.getHx() && t.getX() - 15 <= h.getHx() + hx + if (t.getX() - halfHeight >= h.getHx() && t.getX() - halfHeight <= h.getHx() + hx + && t.getY() - halfWidth >= h.getHy() && t.getY() - halfWidth <= h.getHy() + hy + || t.getX() - halfHeight >= h.getHx() && t.getX() - halfHeight <= h.getHx() + hx && t.getY() >= h.getHy() && t.getY() <= h.getHy() + hy - || t.getX() - 15 >= h.getHx() && t.getX() - 15 <= h.getHx() + hx - && t.getY() + 10 >= h.getHy() && t.getY() + 10 <= h.getHy() + hy) + || t.getX() - halfHeight >= h.getHx() && t.getX() - halfHeight <= h.getHx() + hx + && t.getY() + halfWidth >= h.getHy() && t.getY() + halfWidth <= h.getHy() + hy) return true; case DirectType.RIGHT: - if (t.getX() + 15 >= h.getHx() - 2 && t.getX() + 15 <= h.getHx() + hx - && t.getY() + 10 >= h.getHy() && t.getY() + 10 <= h.getHy() + hy - || t.getX() + 15 >= h.getHx() - 2 && t.getX() + 15 <= h.getHx() + hx + if (t.getX() + halfHeight >= h.getHx() - 2 && t.getX() + halfHeight <= h.getHx() + hx + && t.getY() + halfWidth >= h.getHy() && t.getY() + halfWidth <= h.getHy() + hy + || t.getX() + halfHeight >= h.getHx() - 2 && t.getX() + halfHeight <= h.getHx() + hx && t.getY() >= h.getHy() && t.getY() <= h.getHy() + hy - || t.getX() + 15 >= h.getHx() - 2 && t.getX() + 15 <= h.getHx() + hx - && t.getY() - 10 >= h.getHy() && t.getY() - 10 <= h.getHy() + hy) + || t.getX() + halfHeight >= h.getHx() - 2 && t.getX() + halfHeight <= h.getHx() + hx + && t.getY() - halfWidth >= h.getHy() && t.getY() - halfWidth <= h.getHy() + hy) return true; } return false; diff --git a/gui/src/main/java/com/github/kuangcp/tank/v1/MyPanel.java b/gui/src/main/java/com/github/kuangcp/tank/v1/MyPanel.java index 4da03965..a354d680 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v1/MyPanel.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v1/MyPanel.java @@ -30,14 +30,14 @@ public MyPanel() { //重写paint public void paint(Graphics g) { super.paint(g); - hero = new Hero(20,20,4,new Vector<>(), new Vector<>(), new Vector<>()); + hero = new Hero(20,20,4); g.fillRect(0, 0, 500, 400); //调用函数绘画出主坦克 this.drawTank(hero.getX(), hero.getY(), g, hero.getDirect(), hero.getType()); //画出子弹 - if (hero.s != null) { - g.draw3DRect(hero.s.sx, hero.s.sy, 1, 1, false); + if (hero.shot != null) { + g.draw3DRect(hero.shot.sx, hero.shot.sy, 1, 1, false); } //画出敌人坦克 @@ -124,25 +124,25 @@ public void keyPressed(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_A) { hero.setDirect(2); if ((hero.getX() - 10) > 0) - hero.moveleft(); + hero.moveLeft(); } if (e.getKeyCode() == KeyEvent.VK_D) { hero.setDirect(3); if ((hero.getX() + 15) < 405) - hero.moveright(); + hero.moveRight(); } if (e.getKeyCode() == KeyEvent.VK_W) { hero.setDirect(0); if ((hero.getY() - 13) > 0) - hero.moveup(); + hero.moveUp(); } if (e.getKeyCode() == KeyEvent.VK_S) { hero.setDirect(1); if ((hero.getY() - 15) < 275) - hero.movedown(); + hero.moveDown(); } //必须重新绘制窗口,不然上面的方法不能视觉上动起来 diff --git a/gui/src/main/java/com/github/kuangcp/tank/v2/MyPanel.java b/gui/src/main/java/com/github/kuangcp/tank/v2/MyPanel.java index 79bd80a9..77c951de 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v2/MyPanel.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v2/MyPanel.java @@ -28,7 +28,7 @@ public MyPanel() { public void paint(Graphics g) { super.paint(g); - hero = new Hero(20,20,4,new Vector<>(), new Vector<>(), new Vector<>()); + hero = new Hero(20, 20, 4); g.fillRect(0, 0, 500, 400); @@ -40,7 +40,7 @@ public void paint(Graphics g) { Shot myShot = hero.shotList.get(i); // 以下代码只能画出一颗子弹 不完善 if (myShot != null) { - g.draw3DRect(hero.s.sx, hero.s.sy, 1, 1, false); + g.draw3DRect(hero.shot.sx, hero.shot.sy, 1, 1, false); } } @@ -129,25 +129,25 @@ public void keyPressed(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_A) { hero.setDirect(2); if ((hero.getX() - 10) > 0) - hero.moveleft(); + hero.moveLeft(); } if (e.getKeyCode() == KeyEvent.VK_D) { hero.setDirect(3); if ((hero.getX() + 15) < 405) - hero.moveright(); + hero.moveRight(); } if (e.getKeyCode() == KeyEvent.VK_W) { hero.setDirect(0); if ((hero.getY() - 13) > 0) - hero.moveup(); + hero.moveUp(); } if (e.getKeyCode() == KeyEvent.VK_S) { hero.setDirect(1); if ((hero.getY() - 15) < 275) - hero.movedown(); + hero.moveDown(); } this.repaint(); diff --git a/gui/src/main/java/com/github/kuangcp/tank/v3/MainFrame.java b/gui/src/main/java/com/github/kuangcp/tank/v3/MainFrame.java index 9badbf1e..afaf26c0 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v3/MainFrame.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v3/MainFrame.java @@ -38,7 +38,7 @@ @SuppressWarnings("serial") public class MainFrame extends JFrame implements Runnable { - public TankGroundPanel mp = null;//坦克的主画板 + public volatile TankGroundPanel groundPanel = null;//坦克的主画板 public StageActionPanel mp2 = null;//放按钮的画板 public StageActionPanel mpe1 = null;//对按钮事件监听处理的 public HeroInfoPanel mp3 = null; //显示属性的画板 @@ -64,27 +64,27 @@ public class MainFrame extends JFrame implements Runnable { public void run() { final long start = System.currentTimeMillis(); - if (Objects.nonNull(mp)) { - mp.exit(); + if (Objects.nonNull(groundPanel)) { + groundPanel.exit(); } if (Objects.nonNull(mp3)) { mp3.exit(); } - mp = new TankGroundPanel(); - mpe1 = new StageActionPanel(this, mp.hero, mp.enemyList, mp.bricks, mp.irons, TankGroundPanel.enemyTankMap, TankGroundPanel.myself);//监听按钮事件的画布对象 - mp2 = new StageActionPanel(this, mp.hero, mp.enemyList, mp.bricks, mp.irons, TankGroundPanel.enemyTankMap, TankGroundPanel.myself);//放按钮的画布 + groundPanel = new TankGroundPanel(); + mpe1 = new StageActionPanel(this, groundPanel.hero, groundPanel.enemyList, groundPanel.bricks, groundPanel.irons, TankGroundPanel.enemyTankMap, TankGroundPanel.myself);//监听按钮事件的画布对象 + mp2 = new StageActionPanel(this, groundPanel.hero, groundPanel.enemyList, groundPanel.bricks, groundPanel.irons, TankGroundPanel.enemyTankMap, TankGroundPanel.myself);//放按钮的画布 //提示信息 - jl1 = new JLabel(" : " + mp.hero.getLife() + " : " + mp.enemyList.size()); - prizeNo = new JLabel("已击杀 :" + mp.hero.getPrize());//战绩的标签 + jl1 = new JLabel(" : : " + groundPanel.enemyList.size()); + prizeNo = new JLabel("已击杀 :");//战绩的标签 me = new JLabel("Myth"); - mp3 = new HeroInfoPanel(jl1, mp.hero, mp.enemyList, prizeNo);//显示一些属性 + mp3 = new HeroInfoPanel(jl1, groundPanel.enemyList, prizeNo);//显示一些属性 if (!firstStart) { //已经成为一个线程 要启动它 - Thread t = new Thread(mp); + Thread t = new Thread(groundPanel); t.setName("windowPanel"); t.start(); Thread t2 = new Thread(mp3); @@ -166,7 +166,7 @@ public void run() { jsp2 = new JSplitPane(JSplitPane.VERTICAL_SPLIT, mp2, mp3);//水平 jsp2.setDividerLocation(150); Fir = new StarterPanel(); - if (!firstStart) jsp1 = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, mp, jsp2);//垂直 + if (!firstStart) jsp1 = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, groundPanel, jsp2);//垂直 else jsp1 = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, Fir, jsp2); jsp1.setDividerLocation(760); @@ -177,7 +177,7 @@ public void run() { //注册键盘监听 //下面的语句翻译为 :当前类的监听者是mp - this.addKeyListener(mp); + this.addKeyListener(groundPanel); this.setJMenuBar(jmb); //焦点跳转 tab切换 this.setFocusable(getFocusTraversalKeysEnabled()); @@ -186,9 +186,11 @@ public void run() { this.setTitle("Tank"); this.setLocation(150, 60); this.setSize(1000, 625); - log.info("{} pre visible", (System.currentTimeMillis() - start)); + + final long beforeVisible = System.currentTimeMillis(); this.setVisible(true); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - log.info("{} total", (System.currentTimeMillis() - start)); + final long now = System.currentTimeMillis(); + log.info("total:{} visible:{}", (now - start), (now - beforeVisible)); } } \ No newline at end of file From 7c4e77b5c739f19e5cdab56eab3fa63f91a14e02 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Sun, 12 Sep 2021 03:07:20 +0800 Subject: [PATCH 217/476] *) use same pool --- .../github/kuangcp/tank/domain/EnemyTank.java | 19 ++----------------- .../kuangcp/tank/panel/StageActionPanel.java | 9 +++++++++ .../kuangcp/tank/panel/TankGroundPanel.java | 2 +- .../github/kuangcp/tank/util/ExecutePool.java | 5 ++++- 4 files changed, 16 insertions(+), 19 deletions(-) diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java b/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java index 9dba9ebd..2d8c76a8 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java @@ -10,7 +10,6 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.concurrent.ExecutorService; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.atomic.AtomicLong; @@ -28,13 +27,12 @@ public class EnemyTank extends Tank implements Runnable { public List shotList = Collections.synchronizedList(new ArrayList<>());//子弹集合 private long lastShotMs = 0; private long shotCDMs = 168; - private final ExecutorService shotExecutePool; public List ets; public List bricks; public List irons; public Shot s = null; - public int maxLiveShot = 3; //子弹线程存活的最大数 + public static int maxLiveShot = 3; //子弹线程存活的最大数 public boolean delayRemove = false; Hero hero; @@ -52,7 +50,6 @@ public EnemyTank(int x, int y, int speed, int direct) { this.alive = true; this.life = ThreadLocalRandom.current().nextInt(3) + 1; this.id = counter.addAndGet(1); - this.shotExecutePool = ExecutePool.buildFixedPool("enemyShot-" + id, this.maxLiveShot); // log.info("create new Demons. {}", id); } @@ -122,7 +119,7 @@ public void shotEnemy() { } } //启动子弹线程 - shotExecutePool.execute(s); + ExecutePool.shotPool.execute(s); lastShotMs = nowMs; } @@ -455,21 +452,9 @@ public void run() { for (Shot d : this.shotList) { d.isLive = false; } - shotExecutePool.shutdown(); } } - /** - * 必须执行,但是只能推迟到子弹线程结束后回收 - * - * @see com.github.kuangcp.tank.domain.EnemyTank#run 不能置于该方法内 - */ - public void cleanResource() { - // 回收 -// log.info("{} clean enemy", id); - shotExecutePool.shutdownNow(); - } - // 运动 public void actionModeRun() { while (true) { diff --git a/gui/src/main/java/com/github/kuangcp/tank/panel/StageActionPanel.java b/gui/src/main/java/com/github/kuangcp/tank/panel/StageActionPanel.java index 5ced7c7b..fa2d155c 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/panel/StageActionPanel.java +++ b/gui/src/main/java/com/github/kuangcp/tank/panel/StageActionPanel.java @@ -9,6 +9,7 @@ import com.github.kuangcp.tank.domain.Iron; import com.github.kuangcp.tank.domain.Shot; import com.github.kuangcp.tank.util.Audio; +import com.github.kuangcp.tank.util.ExecutePool; import com.github.kuangcp.tank.util.Saved; import com.github.kuangcp.tank.v3.MainFrame; import com.github.kuangcp.tank.v3.PlayStageMgr; @@ -21,6 +22,7 @@ import java.awt.event.ActionListener; import java.util.List; import java.util.Objects; +import java.util.concurrent.ThreadPoolExecutor; /** * 用来放按钮的面板 @@ -113,6 +115,13 @@ public void actionPerformed(ActionEvent ae) { } private void startNewStage() { + + // 重新设置线程池大小 + final ThreadPoolExecutor pool = (ThreadPoolExecutor) ExecutePool.shotPool; + final int poolSize = (int) Math.max(TankGroundPanel.getEnSize() * EnemyTank.maxLiveShot * 0.7, 10); + pool.setCorePoolSize(poolSize); + pool.setMaximumPoolSize(poolSize); + if (Objects.nonNull(actionThread) && !actionThread.isInterrupted()) { PlayStageMgr.instance.markStopLogic(); log.info("clean last stage"); diff --git a/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java b/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java index 748ac8bd..a8bb8711 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java +++ b/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java @@ -254,7 +254,7 @@ public void paint(Graphics g) { } if (hero.shotList.get(i) != null && hero.shotList.get(i).isLive) { g.setColor(Color.YELLOW); - g.draw3DRect(myShot.sx, myShot.sy, 5, 5, false); + g.draw3DRect(myShot.sx, myShot.sy, 3, 3, false); } //子弹线程死了 就要把它从集合中删除 diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/ExecutePool.java b/gui/src/main/java/com/github/kuangcp/tank/util/ExecutePool.java index dbb4640c..b420a1f3 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/util/ExecutePool.java +++ b/gui/src/main/java/com/github/kuangcp/tank/util/ExecutePool.java @@ -13,7 +13,10 @@ public class ExecutePool { public static final ExecutorService delayPool = ExecutePool.buildFixedPool("enemyDelay", 4); - public static final ExecutorService shotPool = ExecutePool.buildFixedPool("enemyShot", 30); + /** + * 敌人全部的子弹线程 + */ + public static final ExecutorService shotPool = ExecutePool.buildFixedPool("enemyShot", 5); public static final AtomicLong totalCounter = new AtomicLong(); From 4d320ab05e7b5275efb2d6c13854ffadf17891e0 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Sun, 12 Sep 2021 03:48:20 +0800 Subject: [PATCH 218/476] *) compare quasar --- dependency.gradle | 9 +++ gui/build.gradle | 13 +++++ .../github/kuangcp/tank/domain/EnemyTank.java | 3 + .../kuangcp/tank/panel/StageActionPanel.java | 14 ++++- .../kuangcp/tank/panel/TankGroundPanel.java | 3 +- .../github/kuangcp/tank/util/ExecutePool.java | 7 +++ .../github/kuangcp/tank/v3/SettingFrame.java | 1 + .../kuangcp/tank/v3/PlayStageMgrTest.java | 57 +++++++++++++++++++ 8 files changed, 102 insertions(+), 5 deletions(-) create mode 100644 gui/src/test/java/com/github/kuangcp/tank/v3/PlayStageMgrTest.java diff --git a/dependency.gradle b/dependency.gradle index c2502e2d..df80d7ca 100644 --- a/dependency.gradle +++ b/dependency.gradle @@ -100,5 +100,14 @@ ext { , "cglib-3.2.4" : "cglib:cglib:3.2.4" , "asm-3.1" : "asm:asm:3.1" , "asm-5.1" : "org.ow2.asm:asm:5.1" + + // only support JDK10 +// , "quasar-core" : "co.paralleluniverse:quasar-core:0.8.0" +// , "quasar-actors" : "co.paralleluniverse:quasar-actors:0.8.0" +// , "quasar-reactive-streams" : "co.paralleluniverse:quasar-reactive-streams:0.8.0", + + , "quasar-core" : "co.paralleluniverse:quasar-core:0.7.4:jdk8" + , "quasar-actors" : "co.paralleluniverse:quasar-actors:0.7.4:jdk8" + , "quasar-reactive-streams" : "co.paralleluniverse:quasar-reactive-streams:0.7.4:jdk8" ] } \ No newline at end of file diff --git a/gui/build.gradle b/gui/build.gradle index 2b3c7453..9937d9b8 100644 --- a/gui/build.gradle +++ b/gui/build.gradle @@ -3,8 +3,21 @@ plugins { } dependencies { + implementation libs['junit'] implementation libs['kcp-core'] +// implementation libs['quasar-core'] } +//configurations { +// quasar +//} +//task runQuasar { +// jvmArgs "-javaagent:${configurations.quasar.iterator().next()}" +//} +//run.dependsOn runQuasar + +//tasks.withType(JavaExec){ +// jvmArgs "-javaagent:${configurations.quasar.iterator().next()}" +//} tasks.register('tank') { dependsOn build diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java b/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java index 2d8c76a8..7ff08e94 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java @@ -120,6 +120,9 @@ public void shotEnemy() { } //启动子弹线程 ExecutePool.shotPool.execute(s); +// ExecutePool.forkJoinPool.execute(s); +// ExecutePool.shotScheduler.getExecutor().execute(s); + lastShotMs = nowMs; } diff --git a/gui/src/main/java/com/github/kuangcp/tank/panel/StageActionPanel.java b/gui/src/main/java/com/github/kuangcp/tank/panel/StageActionPanel.java index fa2d155c..b31df0cb 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/panel/StageActionPanel.java +++ b/gui/src/main/java/com/github/kuangcp/tank/panel/StageActionPanel.java @@ -115,17 +115,25 @@ public void actionPerformed(ActionEvent ae) { } private void startNewStage() { - + + final int totalMaxShots = TankGroundPanel.getEnSize() * EnemyTank.maxLiveShot; + // 重新设置线程池大小 final ThreadPoolExecutor pool = (ThreadPoolExecutor) ExecutePool.shotPool; - final int poolSize = (int) Math.max(TankGroundPanel.getEnSize() * EnemyTank.maxLiveShot * 0.7, 10); + final int poolSize = (int) Math.max(TankGroundPanel.getEnSize() * EnemyTank.maxLiveShot * 0.4, 10); pool.setCorePoolSize(poolSize); pool.setMaximumPoolSize(poolSize); + // 协程 +// final int poolSize = (int) Math.max(totalMaxShots * 0.4, 10); +// ExecutePool.shotScheduler.getForkJoinPool().shutdownNow(); +// ExecutePool.shotScheduler = new FiberForkJoinScheduler("enemyShot", poolSize, null, false); + if (Objects.nonNull(actionThread) && !actionThread.isInterrupted()) { PlayStageMgr.instance.markStopLogic(); log.info("clean last stage"); actionThread.interrupt(); + PlayStageMgr.instance.hero.setAlive(false); // TODO clean for (EnemyTank et : ets) { et.setAlive(false); @@ -138,7 +146,7 @@ private void startNewStage() { Shot.setSpeed(8); frame.remove(frame.jsp1); - log.info("start new stage frame thread"); + log.info("start new stage frame thread, shot:{}", totalMaxShots); actionThread = new Thread(() -> { frame.run(); if (beginAudio != null) { diff --git a/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java b/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java index a8bb8711..4a4a7846 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java +++ b/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java @@ -40,7 +40,7 @@ public class TankGroundPanel extends JPanel implements java.awt.event.KeyListene public KeyListener keyListener; public static boolean newStage = true; // 敌人的数量 - public static int enSize = 10; + public static int enSize = 9; //定义一个 泛型的集合ets 表示敌人坦克集合 public List enemyList = Collections.synchronizedList(new ArrayList<>()); @@ -378,7 +378,6 @@ public void paint(Graphics g) { log.error("", e); } enemyList.remove(demon); - demon.cleanResource(); }); } } diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/ExecutePool.java b/gui/src/main/java/com/github/kuangcp/tank/util/ExecutePool.java index b420a1f3..8447623d 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/util/ExecutePool.java +++ b/gui/src/main/java/com/github/kuangcp/tank/util/ExecutePool.java @@ -18,6 +18,13 @@ public class ExecutePool { */ public static final ExecutorService shotPool = ExecutePool.buildFixedPool("enemyShot", 5); + // ForkJoin +// public static final ForkJoinPool forkJoinPool = new ForkJoinPool(65); + + // http://docs.paralleluniverse.co/quasar/ 协程池 + // 在当前场景下,没有优势,底层一样使用 类似JDK的ForkJoinPool实现, 对比50敌人150子弹情况下一样启动了63个真实线程,和上述没区别 +// public static FiberForkJoinScheduler shotScheduler = new FiberForkJoinScheduler("enemyShot", 20, null, false); + public static final AtomicLong totalCounter = new AtomicLong(); /** diff --git a/gui/src/main/java/com/github/kuangcp/tank/v3/SettingFrame.java b/gui/src/main/java/com/github/kuangcp/tank/v3/SettingFrame.java index 192f7086..6269b44c 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v3/SettingFrame.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v3/SettingFrame.java @@ -116,6 +116,7 @@ public void actionPerformed(ActionEvent event) { if (event.getActionCommand().equals(SettingCommand.DEMONS_COUNT_INCREMENT)) { TankGroundPanel.setEnSize(TankGroundPanel.getEnSize() + 1); + log.info("{}", TankGroundPanel.getEnSize()); } if (event.getActionCommand().equals(SettingCommand.DEMONS_COUNT_DECREMENT)) { TankGroundPanel.setEnSize(TankGroundPanel.getEnSize() - 1); diff --git a/gui/src/test/java/com/github/kuangcp/tank/v3/PlayStageMgrTest.java b/gui/src/test/java/com/github/kuangcp/tank/v3/PlayStageMgrTest.java new file mode 100644 index 00000000..f2f5893a --- /dev/null +++ b/gui/src/test/java/com/github/kuangcp/tank/v3/PlayStageMgrTest.java @@ -0,0 +1,57 @@ +//package com.github.kuangcp.tank.v3; +// +//import co.paralleluniverse.fibers.DefaultFiberScheduler; +//import co.paralleluniverse.fibers.FiberScheduler; +//import org.junit.Test; +// +//import java.util.concurrent.CountDownLatch; +//import java.util.concurrent.TimeUnit; +// +///** +// * @author https://github.com/kuangcp on 2021-09-12 02:05 +// */ +//public class PlayStageMgrTest { +// +// @Test +// public void testFiber() throws Exception { +// int max = 200; +// final CountDownLatch latch = new CountDownLatch(max); +// +// final long start = System.currentTimeMillis(); +// final FiberScheduler scheduler = DefaultFiberScheduler.getInstance(); +// +//// final FiberForkJoinScheduler fiberForkJoinScheduler = new FiberForkJoinScheduler("xx", 3, null, false); +//// fiberForkJoinScheduler.getForkJoinPool().shutdown(); +// for (int i = 0; i < max; i++) { +// int finalI = i; +// try { +// +// final Runnable runnable = () -> { +// try { +// TimeUnit.SECONDS.sleep(3); +// } catch (InterruptedException e) { +// e.printStackTrace(); +// } +// System.out.println("Inside fiber coroutine..." + finalI); +// latch.countDown(); +// }; +// +//// dd.execute(runnable); +// scheduler.getExecutor().execute(runnable); +//// new Fiber(runnable).start(); +// } catch (Exception e) { +// } +// } +// +// +// latch.await(); +// System.out.println("---------------------"); +// System.out.println("---------------------"); +// System.out.println("---------------------"); +// System.out.println("---------------------"); +// System.out.println("---------------------"); +// System.out.println("---------------------"); +// System.out.println(System.currentTimeMillis() - start); +// +// } +//} \ No newline at end of file From ef166ff078af1a7ea802aba347420f77ef05f172 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Sun, 12 Sep 2021 03:50:29 +0800 Subject: [PATCH 219/476] +) state mgr --- .../github/kuangcp/tank/v3/PlayStageMgr.java | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 gui/src/main/java/com/github/kuangcp/tank/v3/PlayStageMgr.java diff --git a/gui/src/main/java/com/github/kuangcp/tank/v3/PlayStageMgr.java b/gui/src/main/java/com/github/kuangcp/tank/v3/PlayStageMgr.java new file mode 100644 index 00000000..dd2304bf --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/v3/PlayStageMgr.java @@ -0,0 +1,63 @@ +package com.github.kuangcp.tank.v3; + +import com.github.kuangcp.tank.domain.Brick; +import com.github.kuangcp.tank.domain.EnemyTank; +import com.github.kuangcp.tank.domain.Hero; +import com.github.kuangcp.tank.domain.Iron; +import com.github.kuangcp.tank.util.TankTool; +import lombok.extern.slf4j.Slf4j; + +import java.util.List; +import java.util.Objects; + +/** + * @author https://github.com/kuangcp on 2021-09-11 23:24 + */ +@Slf4j +public class PlayStageMgr { + + public static PlayStageMgr instance = null; + + public Hero hero; + public boolean startLogic = false; + + static int round = 0; + public List enemyTanks; + public List bricks; // 砖 + public List irons; // 铁 + + public static boolean hasStart() { + return Objects.nonNull(PlayStageMgr.instance) && PlayStageMgr.instance.startLogic; + } + + public void markStopLogic() { + this.startLogic = false; + } + + public void markStartLogic() { + this.startLogic = true; + round++; + log.info("start round {}", round); + } + + public static void init(Hero hero, List ets, List bricks, List irons) { + instance = new PlayStageMgr(hero, ets, bricks, irons); + } + + private PlayStageMgr(Hero hero, List enemyTanks, List bricks, List irons) { + this.hero = hero; + this.enemyTanks = enemyTanks; + this.bricks = bricks; + this.irons = irons; + } + +// public boolean ableToMove(Hero hero) { +// return ets.stream().allMatch(v -> TankTool.ablePass(hero, v)) +// && bricks.stream().allMatch(v -> TankTool.ablePass(hero, v)) +// && irons.stream().allMatch(v -> TankTool.ablePass(hero, v)); +// } + + public boolean ableToMove(Hero hero) { + return enemyTanks.stream().allMatch(v -> TankTool.ablePass(hero, v)); + } +} From 06816eb22146a8caf06a6f975297537401278a5a Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Mon, 13 Sep 2021 01:53:04 +0800 Subject: [PATCH 220/476] *) resource load once --- .../java/com/github/kuangcp/tank/Readme.md | 3 ++ .../tank/{resource => domain}/Bomb.java | 2 +- .../github/kuangcp/tank/domain/EnemyTank.java | 46 ------------------ .../tank/{resource => mgr}/BombMgr.java | 32 +++++------- .../kuangcp/tank/panel/HeroInfoPanel.java | 2 +- .../kuangcp/tank/panel/TankGroundPanel.java | 29 +++-------- .../tank/resource/AbstractImgListMgr.java | 38 +++++++++++++++ .../kuangcp/tank/resource/AvatarImgMgr.java | 19 ++++++++ .../kuangcp/tank/resource/AvatarMgr.java | 33 ------------- .../kuangcp/tank/resource/OverImgMgr.java | 16 ++++++ .../kuangcp/tank/resource/ResourceMgr.java | 20 ++++++++ .../kuangcp/tank/resource/WinImgMgr.java | 19 ++++++++ .../com/github/kuangcp/tank/v3/MainFrame.java | 10 +++- .../github/kuangcp/tank/v3/PlayStageMgr.java | 7 ++- gui/src/main/resources/images/bomb_1.gif | Bin 0 -> 15486 bytes gui/src/main/resources/images/bomb_2.gif | Bin 0 -> 6337 bytes gui/src/main/resources/images/bomb_3.gif | Bin 0 -> 2722 bytes gui/src/main/resources/logback.xml | 3 +- 18 files changed, 152 insertions(+), 127 deletions(-) create mode 100644 gui/src/main/java/com/github/kuangcp/tank/Readme.md rename gui/src/main/java/com/github/kuangcp/tank/{resource => domain}/Bomb.java (94%) rename gui/src/main/java/com/github/kuangcp/tank/{resource => mgr}/BombMgr.java (76%) create mode 100644 gui/src/main/java/com/github/kuangcp/tank/resource/AbstractImgListMgr.java create mode 100644 gui/src/main/java/com/github/kuangcp/tank/resource/AvatarImgMgr.java delete mode 100644 gui/src/main/java/com/github/kuangcp/tank/resource/AvatarMgr.java create mode 100644 gui/src/main/java/com/github/kuangcp/tank/resource/OverImgMgr.java create mode 100644 gui/src/main/java/com/github/kuangcp/tank/resource/ResourceMgr.java create mode 100644 gui/src/main/java/com/github/kuangcp/tank/resource/WinImgMgr.java create mode 100644 gui/src/main/resources/images/bomb_1.gif create mode 100644 gui/src/main/resources/images/bomb_2.gif create mode 100644 gui/src/main/resources/images/bomb_3.gif diff --git a/gui/src/main/java/com/github/kuangcp/tank/Readme.md b/gui/src/main/java/com/github/kuangcp/tank/Readme.md new file mode 100644 index 00000000..a86c1eb5 --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/Readme.md @@ -0,0 +1,3 @@ +gradle tank + + -javaagent:/path/to/quasar-core-0.7.4-jdk8.jar diff --git a/gui/src/main/java/com/github/kuangcp/tank/resource/Bomb.java b/gui/src/main/java/com/github/kuangcp/tank/domain/Bomb.java similarity index 94% rename from gui/src/main/java/com/github/kuangcp/tank/resource/Bomb.java rename to gui/src/main/java/com/github/kuangcp/tank/domain/Bomb.java index ae312aa3..d6bf3ab9 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/resource/Bomb.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/Bomb.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.tank.resource; +package com.github.kuangcp.tank.domain; /** * 1 准备好图片 diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java b/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java index 7ff08e94..2f7f9411 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java @@ -126,52 +126,6 @@ public void shotEnemy() { lastShotMs = nowMs; } -// public void run (){ -//// synchronized (this) { -// while(true){ -// this.direct = ((int)(Math.random()*100))%4; -//// System.out.println(this.direct); -// -// switch(this.getDirect()){ -// case 0:{ -// for(int i=0;i<8;i++) if(this.toUp());else break; -//// System.out.println(this.direct); -// if(this.y%25==0)this.shotEnemy(); -// break; -// } -// case 1:{ -// for(int i=0;i<8;i++) if(this.toDown());else break; -// if(this.y%25==0)this.shotEnemy(); -// break; -// } -// case 2:{ -// for(int i=0;i<8;i++) if(this.toLeft());else break; -// if(this.y%25==0)this.shotEnemy(); -// break; -// } -// case 3:{ -// for(int i=0;i<8;i++) if(this.toRight());else break; -// if(this.y%25==0)this.shotEnemy(); -// break; -// -// } -// default: -// break; -// } -// -//// this.direct = ((int)(Math.random()*100))%4; -// -// if (!this.getisLive()){ -// //让坦克退出while即退出线程 -// hero.setPrize(hero.getPrize()+1); -// break; -// } -// } -//// } -// -// } - - //还是没有用,因为坦克太多,会卡住。。。 public boolean TouchOther() { boolean flag = false;//没有碰到 diff --git a/gui/src/main/java/com/github/kuangcp/tank/resource/BombMgr.java b/gui/src/main/java/com/github/kuangcp/tank/mgr/BombMgr.java similarity index 76% rename from gui/src/main/java/com/github/kuangcp/tank/resource/BombMgr.java rename to gui/src/main/java/com/github/kuangcp/tank/mgr/BombMgr.java index 0a316e09..4751ab35 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/resource/BombMgr.java +++ b/gui/src/main/java/com/github/kuangcp/tank/mgr/BombMgr.java @@ -1,12 +1,13 @@ -package com.github.kuangcp.tank.resource; +package com.github.kuangcp.tank.mgr; import com.github.kuangcp.tank.constant.DirectType; +import com.github.kuangcp.tank.domain.Bomb; import com.github.kuangcp.tank.domain.Hero; -import com.github.kuangcp.tank.domain.Tank; import com.github.kuangcp.tank.domain.Shot; +import com.github.kuangcp.tank.domain.Tank; +import com.github.kuangcp.tank.resource.AbstractImgListMgr; import lombok.extern.slf4j.Slf4j; -import javax.imageio.ImageIO; import javax.swing.*; import java.awt.*; import java.util.ArrayList; @@ -17,26 +18,17 @@ * @author https://github.com/kuangcp on 2021-09-11 16:28 */ @Slf4j -public class BombMgr { +public class BombMgr extends AbstractImgListMgr { public static BombMgr instance = new BombMgr(); - public Image[] bombArr = new Image[3]; + public String[] imgPathArr = new String[]{"/images/bomb_1.gif", "/images/bomb_2.gif", "/images/bomb_3.gif"}; + //定义炸弹爆炸集合 public List bombs = Collections.synchronizedList(new ArrayList<>()); - public void initImg() { - try { - // 爆炸三阶段 - for (int i = 1; i <= 3; i++) { - bombArr[i - 1] = ImageIO.read(getClass().getResourceAsStream("/images/bomb_" + i + ".gif")); - } -// bombArr[0] = ImageIO.read(getClass().getResourceAsStream("/images/bomb_1.gif")); -// bombArr[1] = ImageIO.read(getClass().getResourceAsStream("/images/bomb_2.gif")); -// bombArr[2] = ImageIO.read(getClass().getResourceAsStream("/images/bomb_3.gif")); - } catch (Exception e) { - log.error("", e); - } + public String[] getImgPathArr() { + return imgPathArr; } /** @@ -48,16 +40,16 @@ public void drawBomb(Graphics g, JPanel panel) { Bomb b = bombs.get(i); // System.out.println("size = "+bombs.size()); if (b.life > 10) { - g.drawImage(BombMgr.instance.bombArr[0], b.bx, b.by, 30, 30, panel); + g.drawImage(BombMgr.instance.imgArr[0], b.bx, b.by, 30, 30, panel); // if(rr){ // Audio B = new Audio("./src/RE/GameBegin.wav"); // B.start(); // rr = false; // } } else if (b.life > 5) { - g.drawImage(BombMgr.instance.bombArr[1], b.bx, b.by, 30, 30, panel); + g.drawImage(BombMgr.instance.imgArr[1], b.bx, b.by, 30, 30, panel); } else { - g.drawImage(BombMgr.instance.bombArr[2], b.bx, b.by, 30, 30, panel); + g.drawImage(BombMgr.instance.imgArr[2], b.bx, b.by, 30, 30, panel); } //让b的生命减少 b.lifeDown(); diff --git a/gui/src/main/java/com/github/kuangcp/tank/panel/HeroInfoPanel.java b/gui/src/main/java/com/github/kuangcp/tank/panel/HeroInfoPanel.java index 7df2ff77..0b77d158 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/panel/HeroInfoPanel.java +++ b/gui/src/main/java/com/github/kuangcp/tank/panel/HeroInfoPanel.java @@ -88,7 +88,7 @@ public void paint(Graphics g) { public void run() { while (!exit) { TankTool.yieldMsTime(500); - if (!PlayStageMgr.hasStart()) { + if (PlayStageMgr.waitStart()) { continue; } /* diff --git a/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java b/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java index 4a4a7846..c2c12524 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java +++ b/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java @@ -7,8 +7,10 @@ import com.github.kuangcp.tank.domain.Hero; import com.github.kuangcp.tank.domain.Iron; import com.github.kuangcp.tank.domain.Shot; -import com.github.kuangcp.tank.resource.AvatarMgr; -import com.github.kuangcp.tank.resource.BombMgr; +import com.github.kuangcp.tank.resource.AvatarImgMgr; +import com.github.kuangcp.tank.mgr.BombMgr; +import com.github.kuangcp.tank.resource.OverImgMgr; +import com.github.kuangcp.tank.resource.WinImgMgr; import com.github.kuangcp.tank.thread.ExitFlagRunnable; import com.github.kuangcp.tank.util.ExecutePool; import com.github.kuangcp.tank.util.KeyListener; @@ -17,11 +19,9 @@ import com.github.kuangcp.tank.v3.PlayStageMgr; import lombok.extern.slf4j.Slf4j; -import javax.imageio.ImageIO; import javax.swing.*; import java.awt.*; import java.awt.event.KeyEvent; -import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -52,9 +52,6 @@ public class TankGroundPanel extends JPanel implements java.awt.event.KeyListene public static int[][] enemyTankMap = new int[12][2]; public static int[] myself = new int[6]; - Image overImg = null; - Image winImg = null; - private volatile boolean exit = false; public void exit() { @@ -173,16 +170,6 @@ public void startNewStage() { createI(irons, 330, 410, 480, 430); - try { - BombMgr.instance.initImg(); - - overImg = ImageIO.read(getClass().getResourceAsStream("/images/Over4.jpg")); - - AvatarMgr.instance.initImg(); - winImg = ImageIO.read(getClass().getResourceAsStream("/images/Win2.jpg")); - } catch (IOException e) { - log.error("", e); - } PlayStageMgr.instance.markStartLogic(); } @@ -190,7 +177,7 @@ public void startNewStage() { public void paint(Graphics g) { /*画出坦克运动区域 */ super.paint(g); - if (!PlayStageMgr.hasStart() || Objects.isNull(hero)) { + if (PlayStageMgr.waitStart() || Objects.isNull(hero)) { return; } @@ -228,7 +215,7 @@ public void paint(Graphics g) { g.fillRect(560, 120, 40, 40); /*画出头像*/ - g.drawImage(AvatarMgr.instance.curImg, 380, 480, 60, 60, this); + g.drawImage(AvatarImgMgr.instance.curImg, 380, 480, 60, 60, this); /*画出主坦克*/ if (hero.isAlive()) { for (EnemyTank et : enemyList) { @@ -384,13 +371,13 @@ public void paint(Graphics g) { //游戏结束的画面 if (!hero.isAlive()) { - g.drawImage(overImg, 0, 0, 760, 560, this); + g.drawImage(OverImgMgr.instance.curImg, 0, 0, 760, 560, this); g.drawString("您的总成绩为:" + hero.getPrize(), 320, 500); } //判断获胜 if (hero.getPrize() >= 40) { - g.drawImage(winImg, 0, 0, 760, 560, this); + g.drawImage(WinImgMgr.instance.curImg, 0, 0, 760, 560, this); g.drawString("您的总成绩为:" + hero.getPrize(), 320, 500); } } diff --git a/gui/src/main/java/com/github/kuangcp/tank/resource/AbstractImgListMgr.java b/gui/src/main/java/com/github/kuangcp/tank/resource/AbstractImgListMgr.java new file mode 100644 index 00000000..2f2c2201 --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/resource/AbstractImgListMgr.java @@ -0,0 +1,38 @@ +package com.github.kuangcp.tank.resource; + +import lombok.extern.slf4j.Slf4j; + +import javax.imageio.ImageIO; +import java.awt.*; +import java.io.IOException; +import java.util.Objects; + +/** + * @author https://github.com/kuangcp on 2021-09-13 01:17 + */ +@Slf4j +public abstract class AbstractImgListMgr { + + public Image curImg = null; + public Image[] imgArr = null; + + public void loadImg() { + final String[] imgArr = getImgPathArr(); + if (Objects.isNull(imgArr) || imgArr.length < 1) { + log.warn("no image"); + return; + } + try { + this.imgArr = new Image[imgArr.length]; + for (int i = 0; i < imgArr.length; i++) { + this.imgArr[i] = ImageIO.read(getClass().getResourceAsStream(imgArr[i])); + } + } catch (IOException e) { + log.error("", e); + } + curImg = this.imgArr[(int) (Math.random() * imgArr.length)]; + } + + public abstract String[] getImgPathArr(); + +} diff --git a/gui/src/main/java/com/github/kuangcp/tank/resource/AvatarImgMgr.java b/gui/src/main/java/com/github/kuangcp/tank/resource/AvatarImgMgr.java new file mode 100644 index 00000000..b6e13c9c --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/resource/AvatarImgMgr.java @@ -0,0 +1,19 @@ +package com.github.kuangcp.tank.resource; + +import lombok.extern.slf4j.Slf4j; + +/** + * @author https://github.com/kuangcp on 2021-09-11 16:41 + */ +@Slf4j +public class AvatarImgMgr extends AbstractImgListMgr { + + public static AvatarImgMgr instance = new AvatarImgMgr(); + + public String[] imgPathArr = new String[]{"/images/Me.jpg", "/images/Me2.jpg", "/images/Me3.jpg", "/images/Me4.jpg", "/images/Me5.jpg"}; + + @Override + public String[] getImgPathArr() { + return imgPathArr; + } +} diff --git a/gui/src/main/java/com/github/kuangcp/tank/resource/AvatarMgr.java b/gui/src/main/java/com/github/kuangcp/tank/resource/AvatarMgr.java deleted file mode 100644 index 3fb1d90e..00000000 --- a/gui/src/main/java/com/github/kuangcp/tank/resource/AvatarMgr.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.github.kuangcp.tank.resource; - -import lombok.extern.slf4j.Slf4j; - -import javax.imageio.ImageIO; -import java.awt.*; -import java.io.IOException; - -/** - * @author https://github.com/kuangcp on 2021-09-11 16:41 - */ -@Slf4j -public class AvatarMgr { - - public static AvatarMgr instance = new AvatarMgr(); - - Image[] avatarArr = new Image[5]; - - public Image curImg = null; - - public void initImg() { - try { - avatarArr[0] = ImageIO.read(getClass().getResourceAsStream("/images/Me.jpg")); - avatarArr[1] = ImageIO.read(getClass().getResourceAsStream("/images/Me2.jpg")); - avatarArr[2] = ImageIO.read(getClass().getResourceAsStream("/images/Me3.jpg")); - avatarArr[3] = ImageIO.read(getClass().getResourceAsStream("/images/Me4.jpg")); - avatarArr[4] = ImageIO.read(getClass().getResourceAsStream("/images/Me5.jpg")); - } catch (IOException e) { - log.error("", e); - } - curImg = avatarArr[(int) (Math.random() * 5)]; - } -} diff --git a/gui/src/main/java/com/github/kuangcp/tank/resource/OverImgMgr.java b/gui/src/main/java/com/github/kuangcp/tank/resource/OverImgMgr.java new file mode 100644 index 00000000..93d6eab5 --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/resource/OverImgMgr.java @@ -0,0 +1,16 @@ +package com.github.kuangcp.tank.resource; + +/** + * @author https://github.com/kuangcp on 2021-09-13 01:32 + */ +public class OverImgMgr extends AbstractImgListMgr { + + public static OverImgMgr instance = new OverImgMgr(); + + public String[] imgPathArr = new String[]{"/images/Over2.jpg", "/images/Over4.jpg"}; + + @Override + public String[] getImgPathArr() { + return imgPathArr; + } +} diff --git a/gui/src/main/java/com/github/kuangcp/tank/resource/ResourceMgr.java b/gui/src/main/java/com/github/kuangcp/tank/resource/ResourceMgr.java new file mode 100644 index 00000000..55562919 --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/resource/ResourceMgr.java @@ -0,0 +1,20 @@ +package com.github.kuangcp.tank.resource; + +import com.github.kuangcp.tank.mgr.BombMgr; +import lombok.extern.slf4j.Slf4j; + +/** + * @author https://github.com/kuangcp on 2021-09-13 01:35 + */ +@Slf4j +public class ResourceMgr { + + public static void loadResource() { + log.info("start load resource"); + // image + BombMgr.instance.loadImg(); + OverImgMgr.instance.loadImg(); + AvatarImgMgr.instance.loadImg(); + WinImgMgr.instance.loadImg(); + } +} diff --git a/gui/src/main/java/com/github/kuangcp/tank/resource/WinImgMgr.java b/gui/src/main/java/com/github/kuangcp/tank/resource/WinImgMgr.java new file mode 100644 index 00000000..73f7a28d --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/resource/WinImgMgr.java @@ -0,0 +1,19 @@ +package com.github.kuangcp.tank.resource; + +import lombok.extern.slf4j.Slf4j; + +/** + * @author https://github.com/kuangcp on 2021-09-13 01:13 + */ +@Slf4j +public class WinImgMgr extends AbstractImgListMgr { + + public static WinImgMgr instance = new WinImgMgr(); + + public String[] imgPathArr = new String[]{"/images/Win2.jpg"}; + + @Override + public String[] getImgPathArr() { + return imgPathArr; + } +} diff --git a/gui/src/main/java/com/github/kuangcp/tank/v3/MainFrame.java b/gui/src/main/java/com/github/kuangcp/tank/v3/MainFrame.java index afaf26c0..0200ca3d 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v3/MainFrame.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v3/MainFrame.java @@ -1,10 +1,12 @@ package com.github.kuangcp.tank.v3; import com.github.kuangcp.tank.constant.StageCommand; -import com.github.kuangcp.tank.panel.TankGroundPanel; import com.github.kuangcp.tank.panel.HeroInfoPanel; import com.github.kuangcp.tank.panel.StageActionPanel; import com.github.kuangcp.tank.panel.StarterPanel; +import com.github.kuangcp.tank.panel.TankGroundPanel; +import com.github.kuangcp.tank.resource.AvatarImgMgr; +import com.github.kuangcp.tank.resource.ResourceMgr; import lombok.extern.slf4j.Slf4j; import javax.swing.*; @@ -62,8 +64,13 @@ public class MainFrame extends JFrame implements Runnable { //帮助窗口 JMenuItem Help = null; + public MainFrame() { + ResourceMgr.loadResource(); + } + public void run() { final long start = System.currentTimeMillis(); + if (Objects.nonNull(groundPanel)) { groundPanel.exit(); } @@ -186,6 +193,7 @@ public void run() { this.setTitle("Tank"); this.setLocation(150, 60); this.setSize(1000, 625); + this.setIconImage(AvatarImgMgr.instance.curImg); final long beforeVisible = System.currentTimeMillis(); this.setVisible(true); diff --git a/gui/src/main/java/com/github/kuangcp/tank/v3/PlayStageMgr.java b/gui/src/main/java/com/github/kuangcp/tank/v3/PlayStageMgr.java index dd2304bf..d686abc2 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v3/PlayStageMgr.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v3/PlayStageMgr.java @@ -26,8 +26,11 @@ public class PlayStageMgr { public List bricks; // 砖 public List irons; // 铁 - public static boolean hasStart() { - return Objects.nonNull(PlayStageMgr.instance) && PlayStageMgr.instance.startLogic; + /** + * 等待stage启动 + */ + public static boolean waitStart() { + return Objects.isNull(PlayStageMgr.instance) || !PlayStageMgr.instance.startLogic; } public void markStopLogic() { diff --git a/gui/src/main/resources/images/bomb_1.gif b/gui/src/main/resources/images/bomb_1.gif new file mode 100644 index 0000000000000000000000000000000000000000..71090d775666fe8585e54f00bbabeafe0f8d9638 GIT binary patch literal 15486 zcmV-^Jb}YUNk%w1VaNc;0QUd@Ha0f@|NnJkW_DR%OhZOMJVZ)MQ8_$7K}1MFOi)r| zX-am2Ja~vpTwp_6U`S_eJ8pGAXl_Dnb4PD=L||l4dVo%Fbxmk&LUMROU}QvSY(r#d zOKfmdczi}zSwc%pN@HY9dwf7_Y(QveKxAY)O-(>xU_@+eL}+M8baX^yWJqvuOL%xl zY-~heU`%v$NN8wKe0)rBa8P)7O>Asbcz8}|Xh~91T4!fNb8%2}a#($QS#NGQXk|Hb zZ#ZCHJ9l(DY-v1OSwnkxRfB*+Q&LM?T2W(SJ8^70Xkc`TIA~xvWL!IOXgq9WJZNA-f_Ff0Xh3Xa zM09LKaA-()a7c7)TBw^$d~{5Ba8P`7QF3fmc5qi>U}J7H?LpwQ-Vp*eIQ>Ri(u2@ikWMR5fOp#7Wwp&%k zV_v3YWB>pEA^8LW00031EC2ui0LTEy000R800RgdNRVJZfd>aBG`MhKfQ0=bMtmrd zVMT-v3l?-YZk#)H?gqLWsWE^#a{)LK92rue$CND%K2(^oq|KW+OCFqr%t;kj;MxkW` z#OwI9>eP-@?T!Qqa~w={AW3#S_!KTev1UsGzzLLMVy}mLqFtJl=tQFmF>0LmF<#V^ zemmYx30muC(j4v1^BWT`QE-oYF8--G^V-;Mzsf91Gb7T=jfWlYyH;PdAdy$l6U{b zgI|B-;MWmy*o`CLX(<_q)rt?rG>$sv4HZv64mzlyMiSa|Sz5vMmLX!488%#G78;3I zKO;fupmpvHfZTEdF=yof^`x{<09_8y(LQ7fI1YhguBZ}}*Zn9{Jmi2Qr=0zO)1PuV zEd;TMM1SM!YW)#nya`h9>tK_`@D%U{cfD;~X6AtModVz{}6nnptmt?S+S;X2vTT&;g zJOW+X<)vggD-fBRUJ7P)VD5A3K5o)C5IA*`(@&h>>_|>IRx`Sj~Q=`oOUJQiu*g?#qetDv|#CbLC@3cFBDSVa^Pm&_5&aKl|X>@Yq;7af4n z4nG@FrxY_M&#Uh2#Gkt99yD@4dcXru09tPyz{qB_+Rsz%)eGODeNL9%zaE|)6lTM| z)gFBp&s1=;121e(J^noZY!A_X7ajQC)@I9T!TB)jnOw^C_pu)QnKAO$S$93q$REE( z6N63eTV!HvUDP4JlriaRvBR+>k&D&!BhSGIGb^n?_8c7mI<@0-bnX1u?lA6t2jI`b z1FtKtI(9~mwHXI_-1Wy@TP_d)kduRa+3zKKwpzXZ>fw-Tqo?xrmqq>1KB68Bra=Cn zv-|Go#2^3d?$-JQ%*Soc^WyG7q*97t#Vr9S^xl$K(1|VYl|768Bo@k zJ;@ATO{$E&Gz1*-0qZQ^Nl4VV@~dab!yjPshYH{KF!vQC9RWxO3}r~e7z$*5E==J} z0LUMKV9sRlxSBx!qGuC$Ffe2hY0jUPbU_WaMM)Z}m-m_&x;#O#QH=^onGVvWh@kL& zN4uXGZAilz*078)R0uly@i2cdi&+uL9Q3p{xq~!tAVK0X6?xp0hVj3Eu#A;SQ$Lzyw` zM;cxD$^XgZfm$od1HE#T9_C0bEc4A{Lg&45l5=yJ2~7EB5;b{T#*q)Jh(G!M~u zYP-n;6Pl_EJ|qtrOb4IZ^{x>in?OQx5F8~0R3{W^B%>+SbRb!Y9LUx*ix8dm zqo+qHLqBE!1&OqP4E;C(Xg?&Lac1|Za=i^qp!80JgmjHnO@~a6V&3#t6}_4ih;MZ} z5E6T}t6zObKNBiFB6cmf-$9+%n3oS%_M;3^kb;L&@PI@yn7?l`l*{nyp{NZ&XP4pB zYacSxkeWA|NJ%DQ#|z&RF%1L_pvIAw&fK z3MnA6doonWhnh2%BkYGT$;8G0;DeqWMlUP9r^)e}7b(x=t$RiMUfed8!e8|*PPO(9 zR4Vi)urx?1#nq^Bv>+li&;e57?AN*q2gp%m5|M{0kvt1?cPbRGGN+6btpPV8z`b=p za&1_zM#RJ=zH*i$y<2xU_L+7>NIU*@U$cp4zdJ&~U;y9_1#T(n-tXS6)$@RK({ncMbIx>p?Mlp&9 zGLLl_dmZNp$$V9e?4?96*%bMPui|J1tz7~h*0>-v zq+DwRFp0d{q;COkRdt>2UBd_D{g8LO=e=bumwmVI$nrVXk@jz#W7p5jCN}d6lsME& zr$P_|(96NRB$xqV&s{_ZTHTKo%s|J092VLnL$5mntgaa!COTvY^01~m-RSl;u^;_t zUz_~ow>?LRRj&4PEF0$G2>YT#Y)5P=WWPTt0wiDnk1|v)bMnvcE1LJVJCXKwPo+sSmC!0^l%RGunyEvZvk))>>zp$G!Y5pd;wqtl~4(01q-KS zO8Za_xz`U*ASV6LI{okh#L_alf@%O*U2m~nM_~_bgJ_?pdJl+%)rS-f_!}i>Wf&M= zE7w@o#_tR}>03Z(H~)^45gcr+Q)cSQPkJRcLS2;C)vAunsrCd05yF-sgQd zH$?EK2Jip!Zl3h<${3 zeN8wMTo!Ngpmw`eiRbW$mB@u8h=^V#f;VS|@PGvX@C&eT6#BqM_wZl8p@XQ{4{k<&iIqr+N7#q6ScnL*TfcT?l?PZjhGrEBoIqX(sWtwf zHT=*If7TE4ltfNoB_EP!yMu{N^lIsYNPw9v8L6W`oXzn0p z5txh^sAgICXu>#wA;^Ub5osm>3T=P|Zx;$$!wFc>2VD~nH?@%XfG!Zp1taiFa3q9I z=YJJdeAuH9Bk?6sc2pmkl9mXPlxT_P2oW$hl3%zzo5>a`H+KCn2J0{e8Hfur>3sr0 zlWz$CSP%*8&<6dW29;P!?9erlz)Jk^ViCa)YxFl;YPym(F(xRfHWI?LoQM^R*awo>#g1IrTl6S_Vu>rG zvyxL}ed{m<@|a~dP>EhRJ(U0nX8>~{x(=#%4tG!qN7oOLph=PNcG;wc@IZv}vwZXf zkzI6ypVdbIsy(DNGm1A$hxk3)2_GbR5S955m`M?pX{3PEA<{R22{E3-whoPef$S)Z zzd#G;(4vTc|rG;8W^*N2<4v-mg78ij` zIEjFWFWLD}NGcKKiC6HpWlFePcaRH5&<`K~pnBe?25|6&-e&}DDX5Kg1rb335%FiN zw1zZPP#niBIRuA}^jYMHPIE;Y8w3&iu$=A#l=Fp-x0s*-sianVh(iU23-%jb2ocr! zo&BJSoL7mQ(3KKF4ASX+Ob~V>FajhA3!eaYy*Y+5)TS2ZMY9ASBh)YHcP~rC5W?hf zoChB%iG)uWsGfI0ou{jxnJCtISCW{L`>3T^$dw#;ndn)QL2#K@uy%cL6h%3Wk;xuy zYIUy&Fs1^i1)*1TwG>e!Ol$O_efX|IL5P@Xh;CW0x(Yr=Du|YOnIuUByEOy22y;Po znOJxZw(xda(3KSVg$x=Ir=V-4RE;wKrB4L-RO0ojw}F@o!i^i@6vT%*8eylKQjG;m zu}tWhhIo?#39~Q+l6A8;s{L0OtS>G+CM3*#kw|4?(~V zqLxkUP!Gu9d?$DmYZL&XBoJ1@ZGLneF(H*iTP)iuF%R~R6{v*im^LumTb=o_ch!X= z$)LL>U~A_J!FUJf@ClSkiE|(kHU_e3n*;#xRqS95NAY3~L~A&-6PI!ngYkHL)iP1C zHl~vj8n=8g^llhSsPdSO?KrjNRHSnws6|?GSpWtBxu;Qxg?B)nB;c&X5D`ZPvyzGw zLqJL>cz_=^dCcc57{pOg6E)la1}Mh$jZxVWc6txBV_3O{Y=mf%*f*9Bn1sBGkutNS zF&h#42eYat1M+y8cKL;OaAxPQCwn4{AdswK7rGeB4sIY^Gb9n_MNBA^locUNi~^^8 z6^`MvkaU1}T7=~af$7-4m+8A(IXV%_pwY>={V)YhyAE)m3n>eblt=;6$(2|7 zc1d8a@~Bd{c0p1F9|fytNpyoqwi`Y=w5{l{T2wnAb`&6I!cIt!5v7`1X??Y!!&<4N z=a31EyLpXe1dTq!iWQ#u#U^@ec&(vuGGVqS;|Q07?Q4>xh=rKQVr+}SyqP4Z z3rFyF9`I=J*~^draIu!DzXJ@VlG>GZu+uiIw%%9D=h{_3Y<`=x6Lot(9A|ZoVS|E% zPCp2Bw*15dWmV>NYhL(k_&lpV42Dq~onU$n9#9Lahy^C_b}rBW?05n^a1MLkgDX zXr0kHwE^G?-lwIO8wHdI2MwT}D(ZbS;C;&sfnS&c>(B%>?9-LV3YEA5Z%4NO2#`F? zf?n-wD}{h>@u?)e)OXw^;k#Pq<2FqUI?6YB6wz)bcNyg>n!M|%KTV0SPzwp|rNa=` z62Su_%mO(7{dPAnz?Im;&N{#a+z+^bzePFzIQBVtR77LXa4c0sW`A~9i9TAye+m6Zuiz*RW zV2K|tyk~r+o{QPRMp^vye8Hm2Pz=k^%OX)hVcYsaN0Jan>D=whmP=@vE&3=x3`A6t{2{0ps6iJ1C!f-t@3P+)Jjp$gib)0dgc4OYxaWm0vO zdUh=uw{3{c6|y{0;)6C^u@CpaBo_OQ6TzL9nTCy(3ryY*co2-PFxNp|iGm;qZqSys z{btDjd=8CB3sJs~Xx!u^*{{>fjzj8sg&I)nhF+mlJHd@U3b$=h66)cTR)71H!-2c= z{ItD_6uCf&C6|C-m47DpcB~)--Uki9P}^?khJz>nf*%Inq!@>;`dd=7;U11npp z3hn6YZ~@Kj>gzBF`O4)w?C9??owmq%&RrDk#%e`l#Ng$<=&a^z4nhXO68MyP3PF<$ zN~9oZ36w|)rYM2B76px!43;d)-Ura$R}2ZA?j+8SPHw~Ackhngl?cJcn0azL!DV3o zRdX|SLqo_#B65T6@fqLy9@kDA?of>|gw?;Bwz+T)y>JeeAe}1<3F{yXUxfqdAOI<4 z$o(J+x?YLaa1KWg$F?nuQ}A{=_*D&%^4{0U8ji;6=!N)9g8X8kx=2|_wMIk;#Y^)` znDXi7v-dqeH^*%gp7N=4p0?+ZhQiyTn!OIst4UIy4)wrZm*A*a=n1;M!&wjLFaYE* zI|%)F4#Ai8#LgSF3WkS4Vxnt)7(r6>WDoO1gv@aeq@pu>r8D2#8fKvm^PuJjA@t|= zk!l|ioS+1p$&R`vL-=4=`xka(u$?UZg`Dqw<*nD2D6(d|#{F;!8osZVm=5RvU|7;S zH}EEmzB_#(jJm0hLJ#vSNA@Kyg4hhd_X1iHPVp4)@D9{gCb}c{@2aHR4G{eV3S2j^ zUqN>L>ZQY%uHU_W4%uBQ=&K;Ef#)=y^W*1SD2)f#h3t1u;KqU?kC?u`P&u6^>KKHr9>CURat40O(6X;2U1q&AHm$0Zqba(5nI?zg&3awX9Ak+cl`dMYYn(Eb_LA%cG>8>CDS=mb?Qr>EF zU`mtyd?mcaMIgS1Z|mKXC=&QNG8%EX@wm~^IzWmW$B;X6rgtbK0c;!}+;GU8CexdA zd)awVgt84f#2b3F&j6+K8y&E3-2eYv8;UjpdkSkT(d_Ar9lny{Nxh#!@}-|pfCB9r zgZ6<)AkgT0@1zzUJEx198f#>W#-=c+fq_0a=ZlasIVdE1)+*tb7*y`t+sr2b* zALInuFt51!3dt0fV94bhV+NB;Fqn26$PS$l6SJJYXwox6%Y;h*R45@a3vx4ud@2f^ zpmbZRM%{2ya)79qbTUd%87;83efZ(0%b!x}&87(tjR>7Ew$ukANp;k* zZB(4X8fP{o^Gb(7N7D-mFo8N|SRi}Qa_1i8(n@YAe&AgHc$S9G0mhp`1XCh!i8s08BIw7;QPc^bXBu0=P9-7=|8XHQG8w{ zy^R!Q%zjMil0&+?#1d|;(kHE34JyQ;OMY9Y@`9LU3Cym~QG*6CHOegGw2f6(N0LQ6 z=~~g#X1lw$gW62eZ;8S;Z@iJruiSnH5D0K7rz8--0Mwahp0QZlr$*lsYA@wNj&~`kZary`gBnzj%E_X3>-a+u z5{8a@NX;JhXj880GQf3ULMJZ~ksvaIf^|G$BU&iP4b*2ufn_W*>WdqD4idSstqog0 z;mulp#}4}R#BN)$2fk*=x6;VQDV$ObP+%jfc;zerO;ooYeadSZ8yG7Cefsv{%Yqp8BTNmB4~9fdFj z{TAs5-mPN>PcUX?4q^w`{l;~9v?F^)GLpCd`N@(-VVD9Ff}fUsD_k!OzzoZ2nx=57 zAE<3P*7>gLI{bnk7Y;0qEC;)mZL^OL4R)f^K-mjU)8b=E8h6N11n z-jOGp*@PlbQ05+nJ8eaD3EW~Wi6oW45jdPQZMc;j0z#6;M!Hecg1El^LULr zl+;B|ZYHO6%nVNEmo|V7WDtD8VrRma4u9ToR}&o@DfqGkCpcjpnE-$o`Vj-~d0=5% zRg^OwDY^)m$1p;1WOsICt)cvKH^XBqI{bkzBN<5^tLoIxu%fPa&c-VHs7DvA_}2n% zQx0!yf=Tkw4=@;l4nP9w4Lz9AyI(_6$FUQfgony1JkANdH1I@gIx){M%gH8QCBUT4u6ovx9wV~ZDzx-D+)3L~#W z$Lj<_f-?B&SrGNC+q@?dOl}XXOB2&p*rT%Sp)aR(%i>9ggpP8gw;$!;O?u1GkJ&13 zAo)t3Pgp&xo+aRA)=UihX)Z~(Z(kxL5rKjPp0}Ort$$?Y_M%h9Iuy-W;>c~W!)<(s}r$jxVT}Ifz1`cV( zEB48I%V7qA$aamdWR3H>VwJuUrMEuHOh|U3QJCap%TL_a4cF4=>dFNk2T4brGh+xH zX!g2Wb&eC^(><@kha{w}tWPocwkyJ8q;qTSkdmTeO0^iqW?*mf?3!CQ<3t_fC`~=y zN?rvA7CuN>;!w^Zud<$ylUCHJqnv3b1ryb|>%iCqvgs$$`8t_Z!12cgFQSEKABOZz%bmV|cP@`bk8^pwa65GM226xN*js$+z)eP@0-7~sFKUjk4lt)rym;1 zS+SW`jzV;WoW;*IX>HobMUFu`&2#`XExd#YWYGP9$JV?4@sF<<#^snT^z^z{9CwJ< z`Hb@Iz!vY4Xf(;1d8`@80a9w)+_e3m0XrIThfw@>vy5(!vS+koDld(S0<0FqeQIr@ zt-a!!HmcpM9`>$ted`11xQktE_i@KVw^X^_x_hfVwi!ySh3P1yd-6NO)DZ|SzT={wSvo!6$?>t*|j0vBC`XXgcR$i)w>B zs(U>T&;@NWD<828Ju(!6U??#;x;&z^Su3C?swCsnFP%cbuERE~)4Ho0iUi~e1%!te z8x?vP0P8WmIf6RLaXHz6jGAb=X*mc5+KR{8skN{J1tSA)V30VWCek{fxSNb-lQP~= zBt9EIoQT2yHWa=q>b1>azy!1|1vH2v^f(qXgX^;?zKSE+qa)nOvb~!*nd39b*)nZe z87mU0^!Wm9_ySLQKW*5C6|$udv!%oT{1HtZiJAT14x9gW{g1TMHDau&J_1lb( z*(kA^D9L%Z9n&nO%Oe|9x|yq`H`KFp&@`%p#pJ^ar1OcegE5?VFI~)s0f>kH2n(KR z7f#U-zcMPGYD4cZiXYp=mS{u6fI6etIh%_^fhYm$>IN~KH9K0ENsFD|N;ZbDbd-(vaxwl`vzVI4mx8ky$_t|4xv29laEuJOn+%u>lEEmN zz1tS^!!CBfn1k?!e%OYwQM%G;maL&ONb{btQ8*BDLNK8Xa?Cu3u(-^l6f;}Lfe^R& znxK=MN8F-{zVfTri@di1KbeEMZX`uJ11^QzH67$6#5ja*=mz^!!v>KIi-Nfo6ux2m z5KVdzX;F`uBf_4zL+3k8ym>K!0JkbSrFKC{c>Kz%UhqbRDgn?xY8%Cc|l3IqJJL0h0d8i<&Y4_#cRd3ntL zR6|L7DW@7?NfJ^b!Q{aHIh*uY8TrvJK-)3Q=mvGTM8Y!Z&lge)515O| z?y8oxS(zjnhj6e&Z6MIG2}K+VzlglX-l-!Gv?YX7Hu8kfe5u2u=!bDJ5~FYmbI}r1 zs)`Pk&%H9v0Fb56JfX`Jk+V4+J!?ZoIuAWtyoLkMb)betOCo7|kho*Y9F@5Lp5iaT zL(6&8IJX&>11cq>P?xIkP?H1886v2BYPukkNw8^7xU;o*`@oc`!h-Myp-c&Fpt46h ztKgb6MP0#4v(M5pAs-Ra0x81bF%tY}HPdj4yK+)JeKYIHK47%A$BVeToSmAqGZXdF z=XA}*%biB$hJKibgDau9t4K3ExEZO&|2iRKy~!wh!;F-v!b~7@lo|OLIm0Qr_|(%V zEhjFS&1RFFSS8QLFq)bB7;CvG#ak+Ul9r6=uAOM4Yq=J&>AAflR<{tXQryf=T?=?H zDXl0N;B1YWI@Jk$mp<|q67#Tj-BgxcRKiFdfJWz(JiqWrFGj6tGn z%|`>pSKZ8IoefU<7EpBxpJ0-7DOG^w8PYq8AsHGg5cl9c8#)Jy5jF&moM(T*;f$7&o6l94CF!mE{Upx}L7kw*uO)VFg*7 zMN~tbmM1*Cw?Tl?vP`C$Bx!WRzT7V>ZBxQ98lsxR%(J@WTN&&*3*dYyQ@vUONmZ?t zGm$#jgTrSo5Pip zK&hkOka ztQKJ(o|@n*GFExbNx{I(Mta7aQmSvd zO>pT6aXX7Twqp+UTcBBrylEi%u)RXO%cB9;JQLaj@~jdlxZL3;-LYI9hRYG#rtlm; z+i6Z6;l65{Us%MCd?gKeKn=KV%_{e5>c)jM@-thBwF$<#=Xk{VO`LOBvuFAJ0ga>KMODgd&ZuA4$a&M zFdX6|&P}|9K25X5aicF(Y9o?9VdrVJp1>5P5Sk_wyx=-Akr+5BI|!N zbHeIa8@#x# zwOhpVV433awn&v7ayoK2^zEq-7v_X5r z;;9?www~X68%SW&Xs)e8j;3F&z~uYhLtU)yk$#u|P;I9$C+rE0p#cCLtLVT&Ru>M? ze&~WAlNQO{i(&-lV8bqvfW0BxXr(H0xbY;fW0dC>ZOu#S{3dY|N9`%0C#lE>Q59vp zQ8{*a7Wgy1r7>R4(t;;ghZNv~E=b@13Py-fjLznUUn^9>%jmUj={kMeyrW=?Q>5Q^ zPHN`^ZFjzFymj)A3xIg&U;iyRRHE{q`Ayjv<$Hp^1{W2G@-^HIq9jUI9o;c~vnVJE zsfA(29T9ST0x69fvH*kb5BEB*(*qHA@;AoYk7KVW7fwVk7j?Vp4(YQ$lUrnSq_Txb zXrkq}p7h5nK6~Rbih3eB)JX@F(;_t!R51$X<2Y7dKtOl(uUoUzMl}k=iQh!0vOos_ zkRxA~jn-_tDRYn~sDmBRRXP)Phpe|ar!)K*y4dXLoilQ-^CZ1E3UNB(3QY=4lXZ{7 zYZ323S+_5G{@*E=jS37S8k*Ao1%dH5H(d2iILu9n>B26r0>d=y&J3Pe;j8Z~Y-b|X z^P;f0A{FM`R56ZoF?rxaa+oVthxI`ZZJdyY{svg-{a4`hn|c5Mpr@g6FN!VaCbLLI z^mdPx*{TK4+;&x)up!!*-E_pGP62a1wiq-sfxa|6E)EHe^y;>9c*mf?MOxptucsn-j|E=erQIb}xr>aqyf_2}blU!8^k+&!U&@8-abItAct~5`zSw2#Ho8F^_y7Ax-EA}sIO9_t1h)UHX%Dh zG?nSN-Iv?DzK>QKF@Sv{IG(u`oAs4@O~DgNc9o7)U)xcmU~l0(9V_3>yo5k>8z9Eaf7e= z4;;m*l6b(ydIpF90>=da7l5F^gbEijWY|w&KX(D#Q5-j5z_@(@9`4)skY751?AUGW z*Kb__lnAS&Mw8+`VcT(sY_R;T=^7f>8K`oj^s+W zD%r2G8`Ca%wx7MK{RZ!b==We=zQTup_WOCGK#2(nYwU=R@z;?61OiZL{qM`6t7EdX z<8piDsUwAouE|p+(x>M_r~j$jvDU0*iJA=%5?$K*_yeG;S5M<>!wEQ`LcfUx(Q*SJ z#F0k!*cTm2ByF@#bpcd(T~pa@mswR4#^m0FKXI1YOGt4i;d)L1_0(!DJrtI1TW$2! zgSz3DAAKA7SC4N968Pg@#`%a`a&tXIkwyV129kj!K}MoVm2s!tNl>*&UsXSGw_=an zO(tRhrLmJ@Nh5WVhfJeIW*%}=ss`VS@?kY2S#IKo7JoS67=S(gbV!jqpxybZ zo&_eyYDEL|(;RNLMM@!QFS#_~QQx)ngh_;4soRutWa*-0*(JnUN=!lZ>#`-uN0mTc zp@j~AfOcz-gQM=JV?R9dInj`Sz3R|k>;_0{KNtz5(SNxyHeR8>Zux9+F|C*4IRH@V zqFC&7B9x$EO4ck(k1Z8uQ(!tp7fGIR)m4BdrRop3xq*w@L+E(?AD_mlyRKfxA(x|X zCMoy-TdN(JG+l1b0`)7R3R6kuzb2(Pu}qv$HJ%WVs-#|NsYQD;W#6GR?Pg&Gb=!RT zag1um^mUw@okMzU;JU6RWFVFk^@*g+```mEStC)K)Y4-P6~H6wU8Y{oG5ydF7A&z7 z#dtqyxe&Ffwdkw9DEc&2j-QR{-$QK$;9qk}CgzbwtPr?MABD=jxw#@^@CVX`GPB0 zDKHn78bB4L^lti%m2angtK=Ow2=Y)jVJor_vXqN|Lj zf7z;`$iVdw4xOdRmQ*|6(>Mr>)LU8h(M3K|!(I7JlsUpTrDCa|aY1*!Bd(u#GMMh&@`M z4=3UgCE6#3nZlGn@TSO^NP=ewcnTLs0EVZiMk?4MiB%Ly#<7VBC~>P_uaqXZt8}HC zZbB80j(A7W5h+%++@mb9$1*kfh*cfANB2VFHijvMOiI|oQxl)f1rzUFD%Q6o|;ec{aibsB8^H zr>q*+BccG{j&!*PKAvMAEoEpOd1z!yiV&46^dkX0J;xW$Aqou!^fagc`P4>aIJkd3 zZ9|9yWl!jX9)gCYs9oX8e*A&Mec(}ZoO@t7p<1AIu1rYN`KZ~Hgd2N+Bq%IYiddPM zge0uz3EZhdS*sMuFtLNDp6W{egrcH-B4s=c1CQz&HkG1uvnv2ZmGTH{*dVnFY#N2t z9}jg-h4`>l33QNI++!Xk=<_5bAVCt=F#;}Rp&wq1<*hs94rw z4b$B+_yy-@Q<ay|vu8wKFK1e`Dbae6yd{I<-NNAfS9 zI`Xlt2Bi|0J;6E%u>?9q#R$~s2NBL;g@7rgOytcUSCXs{xWec(ZS|?(z<1v4^lE6_ z{FJB!LOCVvmR+(8uF8HpATg7zv3G1xKkyM(p$NexOIXJVTBHUm2>}NEsDqbENZzQF zcD-;lkkrcekkabZni2wEhkg}2zp@vp=#Ymz{v$|oM(-~s8{!_tJe`DW`guKc-aQrr zDVZ<O5Y^u_Zxyy3(fh%C1RQRIk-- ze0AG!al_e~)e8?j=%E||cne1E*wjA0r<`K$oXHe29d!xvR?TCK{C?9Eh^#{kUSI>Q zW9XUFEg7OsWf8t&sPk z;RzaeLXL`khU|fZkyg%`v4?1o?Bd~i#muObMSr`Sj`sLL(1oYs90*J7H1_B6d!+5* zL%=cgQfwDX6(Teuc!7E*SCG!fm8D2Rf!iNMI((l0FCt9DBMt!G!&Y*2kz9`qygIDk zRXK&kFKz)cgh{5*k17PE?8SL@P|~DidzYm0R$g>mGq371zWt$H(SsR4AAo%1W8P2y z^5^>4_aCBM*ivU+MMXjv%9y+=R3O5$7hZc>ueX#JmY$lV4=VV4NIR{SAw)&25P8Jo zAkUxtd&*{d7N=b8)RQdhVT+-OMD4A?gOP5NtkPyU_7z-h-) z)SaujS^&huyx|Fk6H>@k=jNuf!A z#qijjR;ZKMh#(oJMZxUc;E)FVZ6H>3965L(LW~?@5s?@&A*_U%IzdFtc-`>8&!81W zRUiTt;M^B9hJ9&^{9#P~-JsV^Uz}70V+@+m!G!Hd2$R_zJd^`D48;8{p;VEeEtO!3 z8JBU5-kM3FeHcqWKtaiI%V)p|jC4~No}UPMT~*ivuchBe4O$5?AU9cJLIhe78pPg3 zUH93d9num63Rj(&gh*it)euQmc|jC(OFtN1sYn#J=-Qv{TS;7nNu)_Vpab6ZLpcz| z6doPE=vuY}$K*=Ze2l1tOjWE`I8I&lgyB{UB!59- zmH>|lwTAqaQdsE0#{t)hO`RxSp9%V%2)g1T62$~*OhRmpKX8k?ZHtJ-hlN4lEE=R3 zewZ_lEDMg5g}HXLGld%kbp)JB~~&Dh!YOmaLHproC=^6(p3e364?X5-D9eN zOow1hZ3)S1>|j-#W4TDqdzfK#{NAT%U@+PpPU@uR@MJU6*F{>|Q#}vKr3C_}S(-Is zLXrznfQzc2#nM^WMxfp3g`+JZ)%COmBtnM*{$c~N+e|7Q@{z+E-U+d_*fIho%7Egq z)gmKG-}0nQR^~=M$b+iz8h>>EOG;v*amdG^lw?)dib=?WT5zQT`VUz6nZ_lZ9x$e3 z3Rgmk4S@t@Q1TXT=3gl)QTqK!DO!|obcA4<3#5_b$GO`3GFgz?FI_*~W1kPHv)O4$)(xQ*Sm9RpMhG;ll(Sig6(a0=2L0Ln`q| zIR<3k`G$O$p>C+AkqiLyMcTFyk8Ie-5W11naNH)ECq*t0^>xmABHLnhr1KEl1G$Vz zsAfm#c2!y+7+{JKQ05m2%{$yJ6 z9XqW`{t=dL9MF+`h7T402g!&ic1D`=1%O2A0kJ7!N}8i?7zs~4#INCos?;bA-6)PK zT{}vtL`vvJrRA6%Tj{xs*Zs*Sz8IfKMDtL@MevA>4gjQi96BT)+DR#(QmJl~1H6@l zk<43a8t6r2Wj%oDeUO+yn8AtKUFfN)rJmsbF-YbRq;EwKmraE8UBm#?gF6sJKHz9S z@F>S&={ar$R=#9MmTE-Q->Hs6K8R?eev@F~=vzptJDzF%U8<$(3BIxAeA-Ech7P*8 zL&#MGt1urqcw9c1K|fHd$Gt^%Rt0Mc*Ie9ZVL(JH>EuQ1L!;uB$ml1e;i#{clDoyB zuNv#1t(NFj=0iW6Y6Qx~K_pPCILe&br#tOP zqxy$uvPB09#3p`9yS^)s$ZLxI9}fOqvWCu5iCn2RpVz?~U5F^8(aCPa;VY@bV9dij zD91RFtHkoi^1($B#zU}1l*aM}$HJ>`wkb2}AEpu4$c__XoGQRFpIa~=^TkDB2qsay zOhmw}JIq65i5&8D=JC`jLIkU${j9KdsD=`efQ;-rxRSmq$G=)cmcoU>>SU|T1yfPP z%wAO98RzK8o48U0%v!7&fE(EYE!C}P|J@s=iY(iXlipPXLkQBcnuN^CXAkLx(mHIP zEQGDf1IHO#Y~gAw^#Ri2<|}a}$6iPTLR>D=GK9>c80KE?*+f=DxNGPJsWEC~=fLi8 z7}lhv?W@piC*H1wPNquMm$0H3rQKiU60h@KLOx7YR0=946;DVVVOb6?DkNxN9aK#cGA z4n8&xIx-SMIubrMBywdIMm`ooIvPek8$322Ksq5TDlIrR0w^E`E+!6AMGZze3Nk7Q zBOVSmE)qI27Ctr_LOLEsJ_SNF1}Y*18x{&aG72sx4l*hdHZB%AG6FX$1UoJaLNyLP zG72gp6HGo1E+!U6IvYzr5;7_lHZB`9E&@t61T!WEH!2c7G895K6e}eHFCq#zDhxa> z8#^)=GAa}z9tJNW3`aE%H!2f4E)FOl7Ctc>LN*H<778vR3^OJYHYyz^BLX@mAxAkP zI4~P5CI>?-7D_e~GA0^IHWxQ45uFCqdq9tt}p3k?hwIVBuADI7B<7(yTvG8zR7 z1`HDn1O@~Z8x$=xDiakFDk~`q4F((?7bz+pK|nfLPBAq!GWne=by6ymUo0RW9@%^) zwr?&O5eg#_1{MnihiF+gEGSqxE*=#U4Fv;PEF*0v9dR5Lcp@50E-Qo`7F!q-fF&Q0 zEhumjakz`}8@9ddTv_p$APXiEq9y<6hxg|T^Gq)ia* z@Pzdu+8*q;_&{*voI6*}k?#Q8zYj+Lhi-7+zSGAAf-OfdId_NY4gQu#u%3N^_6F`N zIDY)<0I}&okQwi3^jJQ^)hCcY0py3zLH6X=V08A?v)_2~Y=@S6?kz-M0Ih*mpl82f z7{Gne(`yGVNi~_M}kdO*V7(i+AxJRIX12G8? zjypz_Sa>!1I1rKP1dwHxT6T%fmp=+b-$6`51Z4nvBpBss3^r&bi(C?v&YT0y$>o=B zBIG2R1D&~0Io4ek-$O)pnI$^|A<8A4Um7Wp9%Wi&Mr?udR%JwS9?A})0wKyNqe7ws zsX%^`%FvSrjZnyb74z!N6ed3z#x(zKBAbtF8f5D-k@A=5<?Wi!T#t2J=e-0;yJ!HO8Z@%V6PHV~#USOz8@e&0Tl2X4MhvmP zITO7zqEI%yGDBzux~f{9(i*VPxAr?R#U^i^^Eu|S+w{fJt2R65ntZh2NJ|{|!vuj{G}9Z&Q`%^Md9`9fKzE(c)XtyBN0n~T&uja0+uH<3GVUb}eu-O~xbDZk zCo!#b+(L&r{K2I2S!I0%8C~*5II-j*O>xG9UHBwoyk{Zm9CuS$z_?|OG-&Hcf!Uw@ zOt?U!=}&(6%OC9jfY(4D&ToebVcbD_@QQTQkUb2d&!wOuBoOkiec#g=5JPyxqWzA3 z#p5Eh&ZIg$Y2_ZwHEv#PKWqg2(&(jLjJjKUk;Sx4}VBTfdPO4@G!?K;E@J% zBuqk8njTZmWWDgs@pq1#;0o8ay%hTJjR#5Ijg*%>f~>(DXuMkw%z+nUu}DZ}gNl)0 zcElda&s-FYW8zR~y+`hFb8htEER6>MzlaYWbd;YoHf4{ynUPV2X3r^8a!Qx zK6-FLV{QbYGXcy!)aN_RNz8H#)L9&xxx+eQGKF9Sra)f-fE~gE06pO8C`GD|1zIg& zy{c8By=!nic2BVS>-x9Tsk&_vDKpt2g2Kr=2fqx z>}X!ivDo+pZfGC7ld+8DsHQA5ZC+fU0#&$8(8{)R5QQzd2r1BWfEB3Y=8;l$ZuHFxJQV-uVxGrOPtu6 z&+fielCh1fgF$;!;Oa296y9eKa*JKS(ovf3bC$F`h0-hu5V;9O>Q1zlz5BtDX4aeN z2q!w*V2V*7qonYD^?FmFu6W5+?cLH|Hr{THhk#bm*=i40J;|=H#aFIsCAE7F-ClS9 z!soC@DWi+Ts`6`Av||iuA8Mxe~@<%KD(R2h8g)Ppa@X`jt z0V`xOLLu}XOPnYo>#}&TH8gP7(SvnxH?R1+E>G1AY1NjvZGhvRk0J6HA;-j4H-0Zf zpQ^TKUb)a}ex)=F9mF7pbsY>?v1+k&s&{Ti+zI!y_@#4y|k@x7nIE z6=)DWm)}?L)Z17NHpHAAOMl_nUJ9v)-cV!hoyd1#blR7ZZ;a-uX7?4=1+GVXAm#f3 z6WFbO@PQQwt;z||(m{^iPq@eAI)gS-;Cka!!v~LHYZtJ(y`RS!Fm5Afnbq$9RPwm( zjJ;^>Sv;nZ%uN&-#_ruJS<^CbG+E8lS7i651NZ2uNp0wqa}?6ub}+;71+;t%P$1(O zhs)}1l6S-?o{*#&o$|BP0Puq!=upXCT@e_GI`+H?NUM|DNr;#Xueh_}qL8`EvMi*{~ql#X`)zKhEr#1zCc zo}UQu8Pf2-_$Bc7Xb{-xWNK4;)b0N8Yq&-2j#A_C?U6=7&Gc+plwLt&Cw3Af<<}5C zLlUjAemfUD1wk)?*B~omC!6AP&*Bf`7JE!2YQ+XTa?@f{6-TmUa_-|wWX5#N@_Q<^ zfW}u6)?sxM2v2#0Ir_mnFw!aa#CAPUR{ivCu-9VTRdP=CZ2LAx6XZ~zmNU<_Da18i zF|v8+Lm4L#EY!n5S%+Q%(J}n6B^j76+@%j~Ms(-E4s(YL>%ew%hevWFQ~0EX#M4%c zKG>5+ze_vO4n?h>OG5{O@whq9AhR`I2iba4& zC`6efM64r%nIkTO4eG_h7VMAJ6U0stcThATKYEf+R%sDjW~F`|`#S7MCg zl}Z939K_LwM$v+y6)tcSH(-^EQssty>(GDK*op4QiNDB>x`c`86g1xdB83G}Fa(hx)iDzkSBUk~ za8s_KBO~4(7lPT{T4U zh>w|bJXu&~c2tz4A}`0%Kk6bNX5cQzKm577ELz8+0m}e3v zV^cc=!5%k+65}w666k7_be6K#DK$h?RU?+Fw{!^RPAf<*tl63+lVfn%EaT)qyh0G{ zft@r#KO)3w&z6#&k_Lq&AbA#RVmWCm=we;unSN=Hakpups4mud5WO-G1Y(ycp)Bf@ zV$jun^n+)5ppsUzj_DD2eBP1ab6>6p%;35?shJlvZHrsgGAJ&vNdW- z(~B_4p4|CLP{K3dsFM_06ec-&0jN3LrJ;WRbB*Q6eL&LMrNH4r!CL0j1o=X6(eC3i@d zg8Sr&-N=-Z21m^1m9DCd-lLukK_rEGP04r@1{6En$(@~4YKGJ`+~r!hv~KSuJfj+o z>ZL`xmtJ$9kxqIbZoB{Xblxp(YU2?#WhFBj_*@K4N*G% zF#w8`s~Q1hpSf&VmONHgPiA0W>Pe|MdW0z#OX?(|IrWCJ3ZV;8Bc=Efi?I(6l8zF@ zs<1U%QU(BE#Z740eSNB!KlHMeh^+hn8KEHLAAF%2L*b^gG+PA6XfUL3&3Q?3=v^q9 zOaEGwrs#-r6QAa~nd)insnLW^DiHj+-Dryi`1Qd(0gWz?v zpmtphum=4KrwYe9ZU|Mk*QcErRd18BPs@b^K_M}S6M!*b0$Otcz_LMmn9@{dBGanR zbz&3=xo;`1Eyqk2<+lx?Dx}C0GAAc13PyS`Nx+aw8*@O@DX+P?GT`IP5vrGD+%CQ`)dc5XX2a1-iyRlYc6pJw^h9?m4*jb*Htwam9 z&WSIrbfWG?EBJVe4vCbMX0;6e!LvQlxeNhTVYLR(0$BPvvvJ!st@BdsiMd~tWU0Ag z@7645I~3^waR3T2@L-mk)?CgBre4c?uGyO5Dlru|Nr(fP`5S!Wdww9}Fw!egvTDG! zbU<3z!20N@s%dm96gblbb4d}n1JMJdyM=-#lM{KTd8;$1>O!94Zu%C0HkrNh>ojEg z6b9xHmwP;l`MdQqD=vI4_XA!G#*7Nlq6AT&vzrr}qY%31sIS9|XQ@K{Ld4N>R-*I4 zHu?}dyc3V1u(0}61Nf@;sH*u{w8t5k_e*5znLzCsmI40%e(KMJNxM%+3DI55MX{`S z>V#1&Rg}fXmSf3@!iBR*mw{?3@OhhLLMnSQ%Yw@gG)Ii#oUze($i92QR2tEwqC{!a z&irVm0$~Q&>7-@w`F` zV!QVOGj$CW`vIjZIghDnG}T-}{R}@jrb7mK%nR`<-I6<9;V6oom`+VgMmxg;YRV7B zSd8NbFYi7xj@vEEy@ zbBrGkXU|dpQE!~{r`XqTe#bS-bd%AfiXNSpB+Mg-4Ha1}*3o5Cx}0Ok&56bBzT`qH zeob0jVh{bXcZ$>_zikvQJiVGW#JlHKT&gr!x}xGpkYy2rd6d$Um8g^Gx5yd4Y8j=k zTqNeV7RFbHWt*ZxB*!BBI$e2J^$6k;e0~M~(Srg;H8oT7dr4?$hZ?M}5GJU+fl$l} zyRS7#ax96AYs-XVJe9-EWkDE9HPK&meCL9U$UpuV57N}Edvv86J{M#v z6%Ahs4nEo45Sd*S>9N!-9=RDFTbu$z)e=L<9bKdBm&OyuY%$~OS)wHwKmU?KID+E5 z8o6cv&fZX0g@z&FgIeLhZxy~(p&CMM` z4@|vF)9!@<82cbdSqD6QBP+8~Ex>G4)cWj&pAtpgEj4BQP7Vn3A$7w4Fm~ZLOWBgv{9KZ1qk?FPn z9vVFYD_UrGhB92qf>I_W@*KbC4v{mnGxB-y;69QL=#Yrtf)3j*@)3HS6mPb>LiB|( zGwARj4}vGXU0mYi@07FNTt+$DF7d7re;yMiZsI5rVo*#!&|sXr3^AQlpBGp%UT&^P zj=~-+LYps7_5skY2;V=}+4ru2^<8*+%6IqzM(`M8CJGYwYpoFCei@s75a#i>MBVta zTq>1cjGb)JtaAC9cd+7nA{0^&V?v<}@1hDpB|5nC6;b+cUzb<05VZ>Zioa4*YZ=&{Ir$Qi z4+`rpBQmuQfqn*28sbkMg<5Ux-NdC)sMx;{wpOSz3L^6F`0?Kt2w|7j0TAv244gO6 z;6a255u#Jr(BVUf5hYHfSkdCeg98w1JScFY$A;=KjwD&qlXxvK5rQ}wUPN&@3740p`ZRb)t zCDBI7r3)3&btmPTODQ#n;*?5gpW3zCMf*7R_viaO-}k=H@A+Qum1 zSy)(blRBtGFhrvR6p+Q{>QDe9kYNOIcv2^p%8x<>vBfaW8fG}U=()N9s4LY)VJve4 zVT9wUF~!|XP>i~Q0AK_}F&cuhF6RiY8)8Wj$V&}i7=WSxg6Nr;05A+<7>J?_ zr4mFC29C1{g0ZN;Fr-v6P?V!qLpaVM2onqw5QHg;GI=~JM@O1M&QYuodaFe;S2Aj^ zqmYqdiJrSs4|Aeei)dbixxa?W=drD=xKcYkIYvPdU2{HwV*rMNIL089EH%MU;#9E} zL@2o$wJ>yzxx13L+({3CDXeQKp%c|7>K}D@lpI>e+G&`7;5fKu%xgNlVR_sAY%xi#29L&9@7|v#Sn@pG(M4W zo5>ml8HLG+SWk{pFoc4_R6lnBM*$20l^BSl3<75;F{`A_kXi{5I7hAIuE5MyxTPH| zb;ZaM3ko6zgS=>J96(`1kpRRN87?MV%!UgV?FeTIEY#&ffN~MI7(`(PhCnz9+`945@LhuNwn}Aq?Rm;VQzzikIA~2S%HQ8PWxWLpE2v?4p zpfF1)px6tk-pfH3n295x!~#+}atX|6(GC$(ox}_b=8HvSTx{tr6S_KZ?Zo;Xc8~^R zqGFB)Gg-XsW@~IOBx3?VZf!)PLNaR}Zo_jDn-Uf*l%r=vp)#qu6kRr#C3S|GYzAy= zV??9y_$D^6z+Huh1voBuM!!|qrUt&B3=A^%Wt|%NTeksyT6<8cPB^& z3nU!_o=;O{dzJwc-q77t)saINY07;rRp0;F)G1-C`_k#IA`6dv-pb2odP*c~8aDJ? zuIW4O5Zhb6^2*u%ldj3rV?9@D2Y*pz0enq!-NVy9C2&Muf zp7*w#d)gS)kBwa!m)Aa9*49}p^2NS=y`=h~#OX?l-_8vgyUB`erwsQxZ_TRlA7)9zTV`rXxxHM; zj;nVbU5fPe<6A`T$efC2Hb0jMW1s8i^W0 zct9Fdk-tD2W4jDDji>9>=$OlwG<8&|ct4g_s+KBqGXZPRb?8SR&&OEwv+jq=LyfQX zd6|Y6))eQO4;U2D%T?|}S$nO5qoW@m%mK44glA%NRzbnl%GYMjvF>Egvw{-j$GTT| zAcOTrwKT?kz&#`YTEFXy>9a{yY^E%Z6#Yd)H;1%s$K}7x>hN#?GJUB+Al9;q+}k>X z(6I?8s{*p4S;oH1Z`z?silW}8++SdPN48IL^z5d!FH7r7bt*(}jz>?|_0)!ZUMLx= zqX(3gDn9Lz6rVl$+w^3)+A}M&!Qdefh##Ri6rS3%*0B0wlu)y!@y5U&y7a9icJtAt zA3J9w{C>}`Yl*B59|C5qs$R+P0oV8{C$T(qm#g{)*!((Qj?_BcvwYh;Ti0uODF~h% zZB1U?YDMr~j7E3dJ~sajZ&3SYl}9nsiz~V)7LAeLy~6G99LSU;HZxm+e7#}KicM9t z)vY1j`igH=nX5XlA?MzDx_^|-UbdyTAIvvcn(Zuag;Lns4N12oH^w@-7gW&+#C4)< zEb+ zg$@skJ_-!70OjXSpIJS5TO@btI(sQ_Dub zzTDXRtCLy>l_wSo>Q#fy!n>@sWn?97&Bx=!<#80ecl zcM%eQoeRjY^LaEWEE{PTZL^r&Ncx;u#KvLQ(c;K^FG?zD`we3bpd9e+qw;imj`vs= z^TF5bc9LmFi0(XN>TSHa)}m%ox2*@%0eR z{qc}xt4iAy=qqf1q-$R1A9Z4B-lArq5|sO|avurv9gy!$GC{e1Lo_1I_4&RpXbG`jDh^2g#@V zeF}23UEdc&4;+4_Z>3g^%))tPoTu&8VyE2)$KOY-8*KfZWy!UsCf?m#?EHq&6h6$} zP~#cOxbBE*Ew_CJUml2QaP~sbDI$H%(_bfc)V(TW8eGk*uUYu5`CQ`m1cENs&c3f> z{KR?^n;Pym>X3rYQZwkKJ<@B(ay_oIXSe7jjrBx1kycH0AgO)9*NEv*)4S9imP|LM z!|Q->`)XwT&NQ3!#1(xVdy@k-?9CBPl6D_!KarT2WqG7xuj{tUEkM%3Q8nG| zNk>7?%fP%4XS&6*6SxM;-TvLV(EoqIE9O%#{bI`Dj`(PQ63zP*`9^<_%RaED$KbHG zV&=h%tjV~+bg;Qn-+zkW&JE=TG*_GSPpRc|4~q+%&n)wQr&&MusC1zDtV{p9fV8>c zO0cEQ+kZOf - %d{HH:mm:ss.SSS} %highlight(%-5level) [%thread] %logger{30} %highlight(%M):%yellow(%-3L) %msg%n - + %d{HH:mm:ss.SSS} %highlight(%-5level)[%-16thread] %-32logger{32}%highlight(%15M):%yellow(%-3L) %msg%n DEBUG From 9abc1d055340edf2d2a3dd293ee7007054ffc039 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Mon, 13 Sep 2021 03:16:03 +0800 Subject: [PATCH 221/476] x) prize and blank panel --- .../github/kuangcp/tank/domain/EnemyTank.java | 28 +++--- .../com/github/kuangcp/tank/domain/Hero.java | 2 +- .../com/github/kuangcp/tank/mgr/BombMgr.java | 8 +- .../kuangcp/tank/panel/HeroInfoPanel.java | 3 +- .../kuangcp/tank/panel/StageActionPanel.java | 16 ++-- .../kuangcp/tank/panel/TankGroundPanel.java | 83 +++--------------- .../tank/resource/AbstractImgListMgr.java | 21 +++-- .../kuangcp/tank/resource/AvatarImgMgr.java | 9 +- .../kuangcp/tank/resource/OverImgMgr.java | 8 +- .../kuangcp/tank/resource/WinImgMgr.java | 8 +- .../github/kuangcp/tank/util/ExecutePool.java | 1 + .../com/github/kuangcp/tank/v3/MainFrame.java | 87 ++++++++++--------- .../github/kuangcp/tank/v3/PlayStageMgr.java | 59 ++++++++++++- 13 files changed, 161 insertions(+), 172 deletions(-) diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java b/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java index 2f7f9411..318317fe 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java @@ -441,7 +441,10 @@ public void actionModeRun() { //判断坦克是否死亡 if (!this.isAlive()) { //让坦克退出while即退出线程 - hero.addPrize(1); + + if (!abort) { + hero.addPrize(1); + } break; } } catch (Exception e) { @@ -481,11 +484,7 @@ public void run2() { // else this.direct = (int)(Math.random()*4); if (min % 27 == 0) this.shotEnemy(); - try { - Thread.sleep(50); - } catch (Exception e) { - log.error("", e); - } + TankTool.yieldMsTime(50); } } @@ -524,11 +523,7 @@ public void run2() { else break; if (min % 27 == 0) this.shotEnemy(); - try { - Thread.sleep(50); - } catch (Exception e) { - log.error("", e); - } + TankTool.yieldMsTime(50); } } @@ -549,11 +544,8 @@ public void run2() { // else continue; if (min % 27 == 0) this.shotEnemy(); - try { - Thread.sleep(50); - } catch (Exception e) { - log.error("", e); - } + + TankTool.yieldMsTime(50); } @@ -566,7 +558,9 @@ public void run2() { //判断坦克是否死亡 if (!this.isAlive()) { //让坦克退出while即退出线程 - hero.addPrize(1); + if (!abort) { + hero.addPrize(1); + } break; } diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/Hero.java b/gui/src/main/java/com/github/kuangcp/tank/domain/Hero.java index 82642009..c8713f9f 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/domain/Hero.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/Hero.java @@ -18,7 +18,7 @@ public class Hero extends Tank { public Shot shot = null;//子弹 public Graphics g; //画笔不可少 private int prize = 0;//击敌个数 - public int maxLiveShot = 8;//主坦克子弹线程存活的最大数 + public int maxLiveShot = 80;//主坦克子弹线程存活的最大数 int speed = 3; public int getLife() { diff --git a/gui/src/main/java/com/github/kuangcp/tank/mgr/BombMgr.java b/gui/src/main/java/com/github/kuangcp/tank/mgr/BombMgr.java index 4751ab35..a6631bd5 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/mgr/BombMgr.java +++ b/gui/src/main/java/com/github/kuangcp/tank/mgr/BombMgr.java @@ -22,15 +22,13 @@ public class BombMgr extends AbstractImgListMgr { public static BombMgr instance = new BombMgr(); - public String[] imgPathArr = new String[]{"/images/bomb_1.gif", "/images/bomb_2.gif", "/images/bomb_3.gif"}; + public BombMgr() { + super.imgPathArr = new String[]{"/images/bomb_1.gif", "/images/bomb_2.gif", "/images/bomb_3.gif"}; + } //定义炸弹爆炸集合 public List bombs = Collections.synchronizedList(new ArrayList<>()); - public String[] getImgPathArr() { - return imgPathArr; - } - /** * 画出炸弹 */ diff --git a/gui/src/main/java/com/github/kuangcp/tank/panel/HeroInfoPanel.java b/gui/src/main/java/com/github/kuangcp/tank/panel/HeroInfoPanel.java index 0b77d158..f89beeca 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/panel/HeroInfoPanel.java +++ b/gui/src/main/java/com/github/kuangcp/tank/panel/HeroInfoPanel.java @@ -10,7 +10,6 @@ import javax.swing.*; import java.awt.*; import java.util.List; -import java.util.Objects; /** * 就是一个用来显示属性的画板 @@ -43,7 +42,7 @@ public HeroInfoPanel(JLabel jl1, List ets, JLabel prizeNo) { public void paint(Graphics g) { super.paint(g); - if (Objects.isNull(PlayStageMgr.instance) || !PlayStageMgr.instance.startLogic) { + if (PlayStageMgr.waitStart()) { return; } diff --git a/gui/src/main/java/com/github/kuangcp/tank/panel/StageActionPanel.java b/gui/src/main/java/com/github/kuangcp/tank/panel/StageActionPanel.java index b31df0cb..12c68884 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/panel/StageActionPanel.java +++ b/gui/src/main/java/com/github/kuangcp/tank/panel/StageActionPanel.java @@ -96,7 +96,7 @@ public void actionPerformed(ActionEvent ae) { frame.firstStart = false; TankGroundPanel.newStage = false; Shot.setSpeed(8); - frame.remove(frame.jsp1); + frame.remove(frame.centerPanel); // Saved s = new Saved(ets, hero, bricks, irons,ETS,myself); // s.readAll(); //实现一样的功能还省内存 @@ -120,7 +120,7 @@ private void startNewStage() { // 重新设置线程池大小 final ThreadPoolExecutor pool = (ThreadPoolExecutor) ExecutePool.shotPool; - final int poolSize = (int) Math.max(TankGroundPanel.getEnSize() * EnemyTank.maxLiveShot * 0.4, 10); + final int poolSize = (int) Math.max(TankGroundPanel.getEnSize() * EnemyTank.maxLiveShot * 0.6, 10); pool.setCorePoolSize(poolSize); pool.setMaximumPoolSize(poolSize); @@ -130,21 +130,15 @@ private void startNewStage() { // ExecutePool.shotScheduler = new FiberForkJoinScheduler("enemyShot", poolSize, null, false); if (Objects.nonNull(actionThread) && !actionThread.isInterrupted()) { - PlayStageMgr.instance.markStopLogic(); log.info("clean last stage"); actionThread.interrupt(); - PlayStageMgr.instance.hero.setAlive(false); - // TODO clean - for (EnemyTank et : ets) { - et.setAlive(false); - et.setAbort(true); - } + PlayStageMgr.instance.abortStage(); } frame.firstStart = false; TankGroundPanel.newStage = true; Shot.setSpeed(8); - frame.remove(frame.jsp1); + frame.remove(frame.centerPanel); log.info("start new stage frame thread, shot:{}", totalMaxShots); actionThread = new Thread(() -> { @@ -154,7 +148,7 @@ private void startNewStage() { } beginAudio = new Audio("./src/RE/GameBegin.wav"); // beginAudio.start(); - frame.groundPanel.startNewStage(); + frame.groundPanel.startNewRound(); }); actionThread.setName("actionThread"); actionThread.start();//将画板线程开启 diff --git a/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java b/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java index c2c12524..6d4e158e 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java +++ b/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java @@ -7,10 +7,8 @@ import com.github.kuangcp.tank.domain.Hero; import com.github.kuangcp.tank.domain.Iron; import com.github.kuangcp.tank.domain.Shot; -import com.github.kuangcp.tank.resource.AvatarImgMgr; import com.github.kuangcp.tank.mgr.BombMgr; -import com.github.kuangcp.tank.resource.OverImgMgr; -import com.github.kuangcp.tank.resource.WinImgMgr; +import com.github.kuangcp.tank.resource.AvatarImgMgr; import com.github.kuangcp.tank.thread.ExitFlagRunnable; import com.github.kuangcp.tank.util.ExecutePool; import com.github.kuangcp.tank.util.KeyListener; @@ -54,6 +52,10 @@ public class TankGroundPanel extends JPanel implements java.awt.event.KeyListene private volatile boolean exit = false; + public TankGroundPanel() { + + } + public void exit() { this.exit = true; } @@ -62,8 +64,7 @@ public void exit() { * 画板的构造函数 用来初始化对象 * 多个敌方坦克应该用集合类,而且要考虑线程安全所以不能用ArrayList只能用Vector */ - public void startNewStage() { -// log.info("start new Stage"); + public void startNewRound() { //创建英雄坦克 if (newStage) {//正常启动并创建坦克线程 hero = new Hero(480, 500, 3);//坐标和步长和敌人坦克集合 @@ -175,12 +176,13 @@ public void startNewStage() { @Override public void paint(Graphics g) { - /*画出坦克运动区域 */ super.paint(g); if (PlayStageMgr.waitStart() || Objects.isNull(hero)) { + PlayStageMgr.drawStopIgnore(g, this); return; } + /*画出坦克运动区域 */ g.setColor(new Color(0, 0, 0)); g.fillRect(0, 0, 760, 560);//填满一个黑色矩形,说明了是一整个JPanel在JFrame上的 g.setColor(Color.green); @@ -213,9 +215,10 @@ public void paint(Graphics g) { g.setColor(Color.YELLOW); g.fillRect(200, 120, 40, 40); g.fillRect(560, 120, 40, 40); - /*画出头像*/ - g.drawImage(AvatarImgMgr.instance.curImg, 380, 480, 60, 60, this); + /*画出头像*/ + g.drawImage(AvatarImgMgr.instance.curImg, 380, 480, + AvatarImgMgr.instance.width, AvatarImgMgr.instance.height, this); /*画出主坦克*/ if (hero.isAlive()) { for (EnemyTank et : enemyList) { @@ -304,50 +307,6 @@ public void paint(Graphics g) { EnemyTank demon = enemyList.get(i); //存活再画出来 if (demon.isAlive()) { - //坦克间的碰撞 -// for(int k=0;k= 40) { - g.drawImage(WinImgMgr.instance.curImg, 0, 0, 760, 560, this); - g.drawString("您的总成绩为:" + hero.getPrize(), 320, 500); + if (PlayStageMgr.instance.hasWinCurrentRound() || !hero.isAlive()) { + PlayStageMgr.instance.stopStage(); } } @@ -539,16 +490,10 @@ public void run() { int maxFPS = 160; long fpsTime = (long) ((1000.0 / maxFPS) * 1000000); - long now = System.nanoTime(); + long now; //每隔100ms重绘 while (!exit) { now = System.nanoTime(); -// try { -// Thread.sleep(10); -// //里面的数字就是刷新时间的间隔 而且每当按下J键就会拉起MyPanel线程 相当于加快了刷新 -// } catch (Exception e) { -// log.error("", e); -// } // 进行重绘 this.repaint(); diff --git a/gui/src/main/java/com/github/kuangcp/tank/resource/AbstractImgListMgr.java b/gui/src/main/java/com/github/kuangcp/tank/resource/AbstractImgListMgr.java index 2f2c2201..607ae43d 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/resource/AbstractImgListMgr.java +++ b/gui/src/main/java/com/github/kuangcp/tank/resource/AbstractImgListMgr.java @@ -16,23 +16,26 @@ public abstract class AbstractImgListMgr { public Image curImg = null; public Image[] imgArr = null; + public String[] imgPathArr; + + // 实际渲染大小 + public int width; + public int height; + public void loadImg() { - final String[] imgArr = getImgPathArr(); - if (Objects.isNull(imgArr) || imgArr.length < 1) { - log.warn("no image"); + if (Objects.isNull(imgPathArr) || imgPathArr.length < 1) { + log.warn("no image {}", this.getClass().getSimpleName()); return; } try { - this.imgArr = new Image[imgArr.length]; - for (int i = 0; i < imgArr.length; i++) { - this.imgArr[i] = ImageIO.read(getClass().getResourceAsStream(imgArr[i])); + this.imgArr = new Image[imgPathArr.length]; + for (int i = 0; i < imgPathArr.length; i++) { + this.imgArr[i] = ImageIO.read(getClass().getResourceAsStream(imgPathArr[i])); } } catch (IOException e) { log.error("", e); } - curImg = this.imgArr[(int) (Math.random() * imgArr.length)]; + curImg = this.imgArr[(int) (Math.random() * imgPathArr.length)]; } - public abstract String[] getImgPathArr(); - } diff --git a/gui/src/main/java/com/github/kuangcp/tank/resource/AvatarImgMgr.java b/gui/src/main/java/com/github/kuangcp/tank/resource/AvatarImgMgr.java index b6e13c9c..d912d5d4 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/resource/AvatarImgMgr.java +++ b/gui/src/main/java/com/github/kuangcp/tank/resource/AvatarImgMgr.java @@ -10,10 +10,11 @@ public class AvatarImgMgr extends AbstractImgListMgr { public static AvatarImgMgr instance = new AvatarImgMgr(); - public String[] imgPathArr = new String[]{"/images/Me.jpg", "/images/Me2.jpg", "/images/Me3.jpg", "/images/Me4.jpg", "/images/Me5.jpg"}; + public AvatarImgMgr() { + super.width = 60; + super.height = 60; - @Override - public String[] getImgPathArr() { - return imgPathArr; + super.imgPathArr = new String[]{"/images/Me.jpg", "/images/Me2.jpg", "/images/Me3.jpg", "/images/Me4.jpg", + "/images/Me5.jpg"}; } } diff --git a/gui/src/main/java/com/github/kuangcp/tank/resource/OverImgMgr.java b/gui/src/main/java/com/github/kuangcp/tank/resource/OverImgMgr.java index 93d6eab5..c729112a 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/resource/OverImgMgr.java +++ b/gui/src/main/java/com/github/kuangcp/tank/resource/OverImgMgr.java @@ -7,10 +7,10 @@ public class OverImgMgr extends AbstractImgListMgr { public static OverImgMgr instance = new OverImgMgr(); - public String[] imgPathArr = new String[]{"/images/Over2.jpg", "/images/Over4.jpg"}; + public OverImgMgr() { + super.imgPathArr = new String[]{"/images/Over2.jpg", "/images/Over4.jpg"}; - @Override - public String[] getImgPathArr() { - return imgPathArr; + super.width = 760; + super.height = 650; } } diff --git a/gui/src/main/java/com/github/kuangcp/tank/resource/WinImgMgr.java b/gui/src/main/java/com/github/kuangcp/tank/resource/WinImgMgr.java index 73f7a28d..4d3fdece 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/resource/WinImgMgr.java +++ b/gui/src/main/java/com/github/kuangcp/tank/resource/WinImgMgr.java @@ -10,10 +10,10 @@ public class WinImgMgr extends AbstractImgListMgr { public static WinImgMgr instance = new WinImgMgr(); - public String[] imgPathArr = new String[]{"/images/Win2.jpg"}; + public WinImgMgr() { + super.width = 760; + super.height = 650; - @Override - public String[] getImgPathArr() { - return imgPathArr; + super.imgPathArr = new String[]{"/images/Win2.jpg"}; } } diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/ExecutePool.java b/gui/src/main/java/com/github/kuangcp/tank/util/ExecutePool.java index 8447623d..6705140c 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/util/ExecutePool.java +++ b/gui/src/main/java/com/github/kuangcp/tank/util/ExecutePool.java @@ -17,6 +17,7 @@ public class ExecutePool { * 敌人全部的子弹线程 */ public static final ExecutorService shotPool = ExecutePool.buildFixedPool("enemyShot", 5); +// public static final ExecutorService eventPool = ExecutePool.buildFixedPool("event", 2); // ForkJoin // public static final ForkJoinPool forkJoinPool = new ForkJoinPool(65); diff --git a/gui/src/main/java/com/github/kuangcp/tank/v3/MainFrame.java b/gui/src/main/java/com/github/kuangcp/tank/v3/MainFrame.java index 0200ca3d..8c8f8875 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v3/MainFrame.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v3/MainFrame.java @@ -41,14 +41,13 @@ public class MainFrame extends JFrame implements Runnable { public volatile TankGroundPanel groundPanel = null;//坦克的主画板 - public StageActionPanel mp2 = null;//放按钮的画板 - public StageActionPanel mpe1 = null;//对按钮事件监听处理的 - public HeroInfoPanel mp3 = null; //显示属性的画板 - public StarterPanel Fir; + public StageActionPanel actionPanel = null;//放按钮的画板 + public HeroInfoPanel heroInfoPanel = null; //显示属性的画板 + public StarterPanel starterPanel; public JButton startBtn = null, jb2 = null, jb3, jb4; //按钮 - public JSplitPane jsp1, jsp2;//拆分窗格 + public JSplitPane centerPanel, rightAreaPanel;//拆分窗格 public JLabel jl1 = null, jl2 = null, jl3 = null, me = null, prizeNo = null; - public boolean firstStart = true; //判断是否首次运行 + public boolean firstStart = true; //判断是否首次运行, 通过开始按钮触发 //作出我需要的菜单 JMenuBar jmb = null; @@ -74,28 +73,27 @@ public void run() { if (Objects.nonNull(groundPanel)) { groundPanel.exit(); } - if (Objects.nonNull(mp3)) { - mp3.exit(); + if (Objects.nonNull(heroInfoPanel)) { + heroInfoPanel.exit(); } groundPanel = new TankGroundPanel(); - mpe1 = new StageActionPanel(this, groundPanel.hero, groundPanel.enemyList, groundPanel.bricks, groundPanel.irons, TankGroundPanel.enemyTankMap, TankGroundPanel.myself);//监听按钮事件的画布对象 - mp2 = new StageActionPanel(this, groundPanel.hero, groundPanel.enemyList, groundPanel.bricks, groundPanel.irons, TankGroundPanel.enemyTankMap, TankGroundPanel.myself);//放按钮的画布 + actionPanel = new StageActionPanel(this, groundPanel.hero, groundPanel.enemyList, groundPanel.bricks, groundPanel.irons, TankGroundPanel.enemyTankMap, TankGroundPanel.myself);//放按钮的画布 //提示信息 jl1 = new JLabel(" : : " + groundPanel.enemyList.size()); prizeNo = new JLabel("已击杀 :");//战绩的标签 me = new JLabel("Myth"); - mp3 = new HeroInfoPanel(jl1, groundPanel.enemyList, prizeNo);//显示一些属性 + heroInfoPanel = new HeroInfoPanel(jl1, groundPanel.enemyList, prizeNo);//显示一些属性 if (!firstStart) { //已经成为一个线程 要启动它 Thread t = new Thread(groundPanel); t.setName("windowPanel"); t.start(); - Thread t2 = new Thread(mp3); - t2.setName("mp3"); + Thread t2 = new Thread(heroInfoPanel); + t2.setName("heroInfoPanel"); t2.start(); } @@ -114,21 +112,21 @@ public void run() { Help = new JMenuItem("Setting"); // - Help.addActionListener(mpe1); - Help.setActionCommand("Help");///////// + Help.addActionListener(actionPanel); + Help.setActionCommand("Help"); //注册监听 - jmi4.addActionListener(mpe1); - jmi4.setActionCommand("Continue");//////////// + jmi4.addActionListener(actionPanel); + jmi4.setActionCommand("Continue"); //注册监听 - jmi3.addActionListener(mpe1); - jmi3.setActionCommand("saveExit");//////// + jmi3.addActionListener(actionPanel); + jmi3.setActionCommand("saveExit"); // - jmi2.addActionListener(mpe1); - jmi2.setActionCommand("暂停");///////// + jmi2.addActionListener(actionPanel); + jmi2.setActionCommand("暂停"); jmi2.setMnemonic('E'); //对jmil相应 - jmil.addActionListener(mpe1); - jmil.setActionCommand(StageCommand.START);////// + jmil.addActionListener(actionPanel); + jmil.setActionCommand(StageCommand.START); jm1.add(jmil); // jm1.add(jmi2); @@ -141,44 +139,47 @@ public void run() { jmb.add(jm2); jb3 = new JButton("暂停游戏"); - jb3.addActionListener(mpe1); //注册监听 + jb3.addActionListener(actionPanel); //注册监听 jb3.setActionCommand("暂停"); jb4 = new JButton("继续游戏"); - jb4.addActionListener(mpe1); //注册监听 + jb4.addActionListener(actionPanel); //注册监听 jb4.setActionCommand("继续"); jb2 = new JButton("退出游戏"); - jb2.addActionListener(mpe1); //注册监听 + jb2.addActionListener(actionPanel); //注册监听 jb2.setActionCommand("结束"); startBtn = new JButton(firstStart ? "游戏开始" : "重新开始"); - startBtn.addActionListener(mpe1); + startBtn.addActionListener(actionPanel); startBtn.setActionCommand(StageCommand.START); - mp2.add(startBtn); - mp2.add(jb2); - mp2.add(jb3); - mp2.add(jb4); + actionPanel.add(startBtn); + actionPanel.add(jb2); + actionPanel.add(jb3); + actionPanel.add(jb4); //显示属性的窗体: - mp3.setLayout(new GridLayout(6, 1, 0, 0)); + heroInfoPanel.setLayout(new GridLayout(6, 1, 0, 0)); - mp3.add(jl1);//网格布局 - mp3.add(prizeNo); + heroInfoPanel.add(jl1);//网格布局 + heroInfoPanel.add(prizeNo); // mp3.add(jl2); // mp3.add(jl3); - mp3.add(me); + heroInfoPanel.add(me); // jl1 =new JLabel("898908098");//不会对上面造成任何影响 - jsp2 = new JSplitPane(JSplitPane.VERTICAL_SPLIT, mp2, mp3);//水平 - jsp2.setDividerLocation(150); - Fir = new StarterPanel(); - if (!firstStart) jsp1 = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, groundPanel, jsp2);//垂直 - else jsp1 = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, Fir, jsp2); - jsp1.setDividerLocation(760); + rightAreaPanel = new JSplitPane(JSplitPane.VERTICAL_SPLIT, actionPanel, heroInfoPanel);//水平 + rightAreaPanel.setDividerLocation(150); + + starterPanel = new StarterPanel(); + if (!firstStart) { + centerPanel = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, groundPanel, rightAreaPanel); + } else { + centerPanel = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, starterPanel, rightAreaPanel); + } + centerPanel.setDividerLocation(760); + this.add(centerPanel, BorderLayout.CENTER); - //把画板加入JFrame - this.add(jsp1, BorderLayout.CENTER); // this.add(mp2,BorderLayout.EAST); // this.add(mp,BorderLayout.CENTER); diff --git a/gui/src/main/java/com/github/kuangcp/tank/v3/PlayStageMgr.java b/gui/src/main/java/com/github/kuangcp/tank/v3/PlayStageMgr.java index d686abc2..5bb2387b 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v3/PlayStageMgr.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v3/PlayStageMgr.java @@ -4,9 +4,13 @@ import com.github.kuangcp.tank.domain.EnemyTank; import com.github.kuangcp.tank.domain.Hero; import com.github.kuangcp.tank.domain.Iron; +import com.github.kuangcp.tank.resource.OverImgMgr; +import com.github.kuangcp.tank.resource.WinImgMgr; import com.github.kuangcp.tank.util.TankTool; import lombok.extern.slf4j.Slf4j; +import java.awt.*; +import java.awt.image.ImageObserver; import java.util.List; import java.util.Objects; @@ -20,6 +24,8 @@ public class PlayStageMgr { public Hero hero; public boolean startLogic = false; + public boolean winCurRound = false; + public int roundPrize = 0; static int round = 0; public List enemyTanks; @@ -40,11 +46,11 @@ public void markStopLogic() { public void markStartLogic() { this.startLogic = true; round++; - log.info("start round {}", round); + log.info("start round:{}", round); } - public static void init(Hero hero, List ets, List bricks, List irons) { - instance = new PlayStageMgr(hero, ets, bricks, irons); + public static void init(Hero hero, List enemyTanks, List bricks, List irons) { + instance = new PlayStageMgr(hero, enemyTanks, bricks, irons); } private PlayStageMgr(Hero hero, List enemyTanks, List bricks, List irons) { @@ -54,6 +60,53 @@ private PlayStageMgr(Hero hero, List enemyTanks, List bricks, this.irons = irons; } + public boolean hasWinCurrentRound() { + final Hero hero = instance.hero; + if (Objects.isNull(hero)) { + winCurRound = false; + } else { + winCurRound = hero.getPrize() >= 30; + } + return winCurRound; + } + + public void abortStage() { + this.winCurRound = false; + this.stopStage(); + } + + public void stopStage() { + roundPrize = hero.getPrize(); + instance.markStopLogic(); + log.info("clean round:{}", round); + instance.hero.setAlive(false); + // TODO clean + for (EnemyTank et : enemyTanks) { + et.setAbort(true); + et.setAlive(false); + } + } + + public static void drawStopIgnore(Graphics g, ImageObserver observer) { + if (Objects.isNull(instance)) { + return; + } + instance.drawStop(g, observer); + } + + public void drawStop(Graphics g, ImageObserver observer) { + g.setColor(Color.YELLOW); + if (winCurRound) { + g.drawImage(WinImgMgr.instance.curImg, 0, 0, + WinImgMgr.instance.width, WinImgMgr.instance.height, observer); + } else { + g.drawImage(OverImgMgr.instance.curImg, 0, 0, + OverImgMgr.instance.width, OverImgMgr.instance.height, observer); + } + g.drawString("您的总成绩为:" + roundPrize, 320, 500); + } + + // FIXME // public boolean ableToMove(Hero hero) { // return ets.stream().allMatch(v -> TankTool.ablePass(hero, v)) // && bricks.stream().allMatch(v -> TankTool.ablePass(hero, v)) From 80d143011c9a0bfbe7f012d8e2b71bf31c408d88 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Mon, 13 Sep 2021 23:21:13 +0800 Subject: [PATCH 222/476] *) rename --- .../tank/domain/{Shot.java => Bullet.java} | 5 +- .../github/kuangcp/tank/domain/EnemyTank.java | 55 +++++++------- .../com/github/kuangcp/tank/domain/Hero.java | 38 ++++++---- .../com/github/kuangcp/tank/mgr/BombMgr.java | 20 ++--- .../kuangcp/tank/panel/StageActionPanel.java | 10 +-- .../kuangcp/tank/panel/TankGroundPanel.java | 76 ++++++++++--------- .../{OverImgMgr.java => DefeatImgMgr.java} | 6 +- .../kuangcp/tank/resource/ResourceMgr.java | 4 +- .../{WinImgMgr.java => VictoryImgMgr.java} | 6 +- .../github/kuangcp/tank/util/TankTool.java | 4 +- .../com/github/kuangcp/tank/v1/MyPanel.java | 4 +- .../com/github/kuangcp/tank/v2/MyPanel.java | 10 +-- .../github/kuangcp/tank/v3/PlayStageMgr.java | 12 +-- .../github/kuangcp/tank/v3/SettingFrame.java | 6 +- 14 files changed, 137 insertions(+), 119 deletions(-) rename gui/src/main/java/com/github/kuangcp/tank/domain/{Shot.java => Bullet.java} (92%) rename gui/src/main/java/com/github/kuangcp/tank/resource/{OverImgMgr.java => DefeatImgMgr.java} (64%) rename gui/src/main/java/com/github/kuangcp/tank/resource/{WinImgMgr.java => VictoryImgMgr.java} (65%) diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/Shot.java b/gui/src/main/java/com/github/kuangcp/tank/domain/Bullet.java similarity index 92% rename from gui/src/main/java/com/github/kuangcp/tank/domain/Shot.java rename to gui/src/main/java/com/github/kuangcp/tank/domain/Bullet.java index 1c86781c..9aeda2dd 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/domain/Shot.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/Bullet.java @@ -7,7 +7,7 @@ * 子弹类 对象做成了线程 */ @Slf4j -public class Shot implements Runnable { +public class Bullet implements Runnable { public int sx; public int sy; @@ -23,7 +23,7 @@ public static void setSpeed(int s) { speed = s; } - public Shot(int sx, int sy, int direct) { + public Bullet(int sx, int sy, int direct) { this.sx = sx; this.sy = sy; this.direct = direct; @@ -59,5 +59,6 @@ public void run() { this.isLive = false; } } while (this.isLive); + log.info("shot die"); } } diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java b/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java index 318317fe..fbf22692 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java @@ -5,6 +5,7 @@ import com.github.kuangcp.tank.constant.DirectType; import com.github.kuangcp.tank.util.ExecutePool; import com.github.kuangcp.tank.util.TankTool; +import com.github.kuangcp.tank.v3.PlayStageMgr; import lombok.extern.slf4j.Slf4j; import java.util.ArrayList; @@ -24,18 +25,17 @@ public class EnemyTank extends Tank implements Runnable { // 敌人 id public long id; - public List shotList = Collections.synchronizedList(new ArrayList<>());//子弹集合 + + public Bullet s = null; + public List bulletList = Collections.synchronizedList(new ArrayList<>());//子弹集合 private long lastShotMs = 0; private long shotCDMs = 168; + public static int maxLiveShot = 3; //子弹线程存活的最大数 + public boolean delayRemove = false; // 延迟回收内存,避免子弹线程执行中断 public List ets; public List bricks; public List irons; - public Shot s = null; - public static int maxLiveShot = 3; //子弹线程存活的最大数 - public boolean delayRemove = false; - - Hero hero; boolean overlap = true; //同类之间允许重叠 boolean bri = true; @@ -54,7 +54,6 @@ public EnemyTank(int x, int y, int speed, int direct) { } public void SetInfo(Hero hero, List ets, List bricks, List irons) { - this.hero = hero; this.ets = ets; this.bricks = bricks; this.irons = irons; @@ -92,29 +91,29 @@ public void shotEnemy() { if (lastShotMs != 0 && nowMs - lastShotMs < shotCDMs) { return; } - if (this.shotList.size() >= this.maxLiveShot || !this.isAlive()) { + if (this.bulletList.size() >= this.maxLiveShot || !this.isAlive()) { return; } switch (this.getDirect()) { case 0: {//0123 代表 上下左右 - s = new Shot(this.getX() - 1, this.getY() - 15, 0); - shotList.add(s); + s = new Bullet(this.getX() - 1, this.getY() - 15, 0); + bulletList.add(s); break; } case 1: { - s = new Shot(this.getX() - 2, this.getY() + 15, 1); - shotList.add(s); + s = new Bullet(this.getX() - 2, this.getY() + 15, 1); + bulletList.add(s); break; } case 2: { - s = new Shot(this.getX() - 15 - 2, this.getY(), 2); - shotList.add(s); + s = new Bullet(this.getX() - 15 - 2, this.getY(), 2); + bulletList.add(s); break; } case 3: { - s = new Shot(this.getX() + 15 - 2, this.getY() - 1, 3); - shotList.add(s); + s = new Bullet(this.getX() + 15 - 2, this.getY() - 1, 3); + bulletList.add(s); break; } } @@ -284,7 +283,7 @@ public boolean toUp() { if (TankTool.ablePass(this, iron)) ableMove = false; } - if (ableMove && TankTool.ablePass(this, hero)) { + if (ableMove && TankTool.ablePass(this, PlayStageMgr.instance.hero)) { y -= speed; try { Thread.sleep(100); @@ -316,7 +315,7 @@ public boolean toDown() { if (TankTool.ablePass(this, iron)) ableMove = false; } - if (ableMove && TankTool.ablePass(this, hero)) { + if (ableMove && TankTool.ablePass(this, PlayStageMgr.instance.hero)) { y += speed; try { Thread.sleep(100); @@ -349,7 +348,7 @@ public boolean toLeft() { if (TankTool.ablePass(this, iron)) ableMove = false; } - if (ableMove && TankTool.ablePass(this, hero)) { + if (ableMove && TankTool.ablePass(this, PlayStageMgr.instance.hero)) { x -= speed; try { Thread.sleep(100); @@ -381,7 +380,7 @@ public boolean toRight() { if (TankTool.ablePass(this, iron)) ableMove = false; } - if (ableMove && TankTool.ablePass(this, hero)) { + if (ableMove && TankTool.ablePass(this, PlayStageMgr.instance.hero)) { x += speed; try { Thread.sleep(100); @@ -405,8 +404,10 @@ public void run() { // actionModeRun(); run2(); + log.info("enemy die"); + if (this.isAbort()) { - for (Shot d : this.shotList) { + for (Bullet d : this.bulletList) { d.isLive = false; } } @@ -443,7 +444,7 @@ public void actionModeRun() { //让坦克退出while即退出线程 if (!abort) { - hero.addPrize(1); + PlayStageMgr.instance.hero.addPrize(1); } break; } @@ -476,7 +477,7 @@ public void run2() { min++; // if(!bri)this.direct = (int)(Math.random()*4); if (y > 30) { - if (TankTool.ablePass(this, hero) && overlap) y -= speed; + if (TankTool.ablePass(this, PlayStageMgr.instance.hero) && overlap) y -= speed; // if(bri)y-=speed; // else {y+=speed;this.direct = 1;} // else continue; @@ -496,7 +497,7 @@ public void run2() { // if(!bri)this.direct = (int)(Math.random()*4); if (y < 530) { - if (TankTool.ablePass(this, hero) && overlap && bri) y += speed; + if (TankTool.ablePass(this, PlayStageMgr.instance.hero) && overlap && bri) y += speed; // if(bri) y+=speed; // else {y-=speed;this.direct = 0;} // else continue; @@ -516,7 +517,7 @@ public void run2() { // if(!bri)this.direct = (int)(Math.random()*4); if (x > 30) { - if (TankTool.ablePass(this, hero) && overlap && bri) x -= speed; + if (TankTool.ablePass(this, PlayStageMgr.instance.hero) && overlap && bri) x -= speed; // if(bri)x-=speed; // else{x+=speed;this.direct = 3;} // else continue; @@ -535,7 +536,7 @@ public void run2() { // if(!bri)this.direct = (int)(Math.random()*4); if (x < 710) { - if (TankTool.ablePass(this, hero) && overlap && bri) { + if (TankTool.ablePass(this, PlayStageMgr.instance.hero) && overlap && bri) { x += speed; } // if(bri)x+=speed; @@ -559,7 +560,7 @@ public void run2() { if (!this.isAlive()) { //让坦克退出while即退出线程 if (!abort) { - hero.addPrize(1); + PlayStageMgr.instance.hero.addPrize(1); } break; diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/Hero.java b/gui/src/main/java/com/github/kuangcp/tank/domain/Hero.java index c8713f9f..9907912d 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/domain/Hero.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/Hero.java @@ -1,6 +1,7 @@ package com.github.kuangcp.tank.domain; +import com.github.kuangcp.tank.constant.DirectType; import com.github.kuangcp.tank.util.ExecutePool; import java.awt.*; @@ -10,15 +11,17 @@ public class Hero extends Tank { //子弹集合 - public Vector shotList = new Vector<>(); + public Vector bulletList = new Vector<>(); private final ExecutorService shotExecutePool; private long lastShotMs = 0; private long shotCDMs = 268; - public Shot shot = null;//子弹 + private int originX, originY; + + public Bullet bullet = null;//子弹 public Graphics g; //画笔不可少 private int prize = 0;//击敌个数 - public int maxLiveShot = 80;//主坦克子弹线程存活的最大数 + public int maxLiveShot = 7;//主坦克子弹线程存活的最大数 int speed = 3; public int getLife() { @@ -53,42 +56,49 @@ public void addPrize(int delta) { this.prize += delta; } - public Hero(int x, int y, int speed) { super(x, y, speed); + this.originX = x; + this.originY = y; this.shotExecutePool = ExecutePool.buildFixedPool("heroShot", maxLiveShot); } + public void resurrect() { + this.setX(this.originX); + this.setY(this.originY); + this.setDirect(DirectType.UP); + } + public void shotEnemy() { final long nowMs = System.currentTimeMillis(); if (lastShotMs != 0 && nowMs - lastShotMs < shotCDMs) { return; } - if (this.shotList.size() >= this.maxLiveShot || !this.isAlive()) { + if (this.bulletList.size() >= this.maxLiveShot || !this.isAlive()) { return; } //判断坦克方向来 初始化子弹的起始发射位置 switch (this.getDirect()) { case 0://0123 代表 上下左右 - shot = new Shot(this.getX() - 1, this.getY() - 15, 0); - shotList.add(shot); + bullet = new Bullet(this.getX() - 1, this.getY() - 15, 0); + bulletList.add(bullet); break; case 1: - shot = new Shot(this.getX() - 2, this.getY() + 15, 1); - shotList.add(shot); + bullet = new Bullet(this.getX() - 2, this.getY() + 15, 1); + bulletList.add(bullet); break; case 2: - shot = new Shot(this.getX() - 15 - 2, this.getY(), 2); - shotList.add(shot); + bullet = new Bullet(this.getX() - 15 - 2, this.getY(), 2); + bulletList.add(bullet); break; case 3: - shot = new Shot(this.getX() + 15 - 2, this.getY() - 1, 3); - shotList.add(shot); + bullet = new Bullet(this.getX() + 15 - 2, this.getY() - 1, 3); + bulletList.add(bullet); break; } //启动子弹线程 - shotExecutePool.execute(shot); + shotExecutePool.execute(bullet); lastShotMs = nowMs; } diff --git a/gui/src/main/java/com/github/kuangcp/tank/mgr/BombMgr.java b/gui/src/main/java/com/github/kuangcp/tank/mgr/BombMgr.java index a6631bd5..71522d90 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/mgr/BombMgr.java +++ b/gui/src/main/java/com/github/kuangcp/tank/mgr/BombMgr.java @@ -3,7 +3,7 @@ import com.github.kuangcp.tank.constant.DirectType; import com.github.kuangcp.tank.domain.Bomb; import com.github.kuangcp.tank.domain.Hero; -import com.github.kuangcp.tank.domain.Shot; +import com.github.kuangcp.tank.domain.Bullet; import com.github.kuangcp.tank.domain.Tank; import com.github.kuangcp.tank.resource.AbstractImgListMgr; import lombok.extern.slf4j.Slf4j; @@ -63,11 +63,11 @@ public void drawBomb(Graphics g, JPanel panel) { /** * 工具类-检测爆炸的函数 */ - public void checkBong(Tank tank, List shots) { - shots.forEach(v -> this.checkBong(tank, v)); + public void checkBong(Tank tank, List bullets) { + bullets.forEach(v -> this.checkBong(tank, v)); } - private void checkBong(Tank t, Shot s) { + private void checkBong(Tank t, Bullet s) { if (!s.isLive) { return; } @@ -90,10 +90,10 @@ private void checkBong(Tank t, Shot s) { //创建一个炸弹,放入集合 Bomb b = new Bomb(t.getX() - 10, t.getY() - 15);//敌方的坐标 bombs.add(b); + + // 复活 if (t instanceof Hero) { - t.setX(480); - t.setY(500); - t.setDirect(0); + ((Hero) t).resurrect(); } } break; @@ -111,10 +111,10 @@ private void checkBong(Tank t, Shot s) { //创建一个炸弹,放入集合 Bomb b = new Bomb(t.getX() - 15, t.getY() - 10);//敌方的坐标 bombs.add(b); + + // 复活 if (t instanceof Hero) { - t.setX(480); - t.setY(500); - t.setDirect(0); + ((Hero) t).resurrect(); } } break; diff --git a/gui/src/main/java/com/github/kuangcp/tank/panel/StageActionPanel.java b/gui/src/main/java/com/github/kuangcp/tank/panel/StageActionPanel.java index 12c68884..4e452b3a 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/panel/StageActionPanel.java +++ b/gui/src/main/java/com/github/kuangcp/tank/panel/StageActionPanel.java @@ -7,7 +7,7 @@ import com.github.kuangcp.tank.domain.EnemyTank; import com.github.kuangcp.tank.domain.Hero; import com.github.kuangcp.tank.domain.Iron; -import com.github.kuangcp.tank.domain.Shot; +import com.github.kuangcp.tank.domain.Bullet; import com.github.kuangcp.tank.util.Audio; import com.github.kuangcp.tank.util.ExecutePool; import com.github.kuangcp.tank.util.Saved; @@ -56,7 +56,7 @@ public void actionPerformed(ActionEvent ae) { if (ae.getActionCommand().equals("暂停")) { System.out.println("暂停"); frame.groundPanel.hero.setSpeed(0); - Shot.setSpeed(0); + Bullet.setSpeed(0); for (EnemyTank et : ets) { et.setSpeed(0); } @@ -65,7 +65,7 @@ public void actionPerformed(ActionEvent ae) { if (ae.getActionCommand().equals("继续")) { System.out.println("继续"); frame.groundPanel.hero.setSpeed(3); - Shot.setSpeed(8); + Bullet.setSpeed(8); for (EnemyTank et : ets) { et.setSpeed(2); } @@ -95,7 +95,7 @@ public void actionPerformed(ActionEvent ae) { System.out.println(StageCommand.START); frame.firstStart = false; TankGroundPanel.newStage = false; - Shot.setSpeed(8); + Bullet.setSpeed(8); frame.remove(frame.centerPanel); // Saved s = new Saved(ets, hero, bricks, irons,ETS,myself); // s.readAll(); @@ -137,7 +137,7 @@ private void startNewStage() { frame.firstStart = false; TankGroundPanel.newStage = true; - Shot.setSpeed(8); + Bullet.setSpeed(8); frame.remove(frame.centerPanel); log.info("start new stage frame thread, shot:{}", totalMaxShots); diff --git a/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java b/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java index 6d4e158e..6b2eb29a 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java +++ b/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java @@ -6,7 +6,7 @@ import com.github.kuangcp.tank.domain.EnemyTank; import com.github.kuangcp.tank.domain.Hero; import com.github.kuangcp.tank.domain.Iron; -import com.github.kuangcp.tank.domain.Shot; +import com.github.kuangcp.tank.domain.Bullet; import com.github.kuangcp.tank.mgr.BombMgr; import com.github.kuangcp.tank.resource.AvatarImgMgr; import com.github.kuangcp.tank.thread.ExitFlagRunnable; @@ -38,7 +38,7 @@ public class TankGroundPanel extends JPanel implements java.awt.event.KeyListene public KeyListener keyListener; public static boolean newStage = true; // 敌人的数量 - public static int enSize = 9; + public static int enSize = 1; //定义一个 泛型的集合ets 表示敌人坦克集合 public List enemyList = Collections.synchronizedList(new ArrayList<>()); @@ -125,6 +125,7 @@ public void startNewRound() { ett = new EnemyTank(enemyTankMap[i][0], enemyTankMap[i][1], 2, i % 4); ett.SetInfo(hero, enemyList, bricks, irons); Thread t = new Thread(ett); + // TODO 改造成一个线程或者少数线程 是否使用时间轮 t.setName("enemyThreadF-" + ett.id); t.start(); //坦克加入集合 @@ -187,7 +188,7 @@ public void paint(Graphics g) { g.fillRect(0, 0, 760, 560);//填满一个黑色矩形,说明了是一整个JPanel在JFrame上的 g.setColor(Color.green); g.drawRect(20, 20, 720, 520); - Shot myShot; + Bullet myBullet; /*画出障碍物__砖__ 铁__*/ @@ -222,7 +223,7 @@ public void paint(Graphics g) { /*画出主坦克*/ if (hero.isAlive()) { for (EnemyTank et : enemyList) { - BombMgr.instance.checkBong(hero, et.shotList); + BombMgr.instance.checkBong(hero, et.bulletList); } this.drawTank(hero.getX(), hero.getY(), g, hero.getDirect(), hero.getType()); @@ -230,50 +231,50 @@ public void paint(Graphics g) { // 画出自己的子弹画子弹是可以封装成一个方法的 // 从ss 这个子弹集合中 取出每颗子弹,并画出来 - for (int i = 0; i < hero.shotList.size(); i++) { - myShot = hero.shotList.get(i); + for (int i = 0; i < hero.bulletList.size(); i++) { + myBullet = hero.bulletList.get(i); for (Brick brick : bricks) { - TankTool.judgeHint(myShot, brick); + TankTool.judgeHint(myBullet, brick); } for (Iron iron : irons) { - TankTool.judgeHint(myShot, iron); + TankTool.judgeHint(myBullet, iron); } - if (myShot.sx < 440 && myShot.sx > 380 && myShot.sy < 540 && myShot.sy > 480) { - myShot.isLive = false; + if (myBullet.sx < 440 && myBullet.sx > 380 && myBullet.sy < 540 && myBullet.sy > 480) { + myBullet.isLive = false; hero.setAlive(false); } - if (hero.shotList.get(i) != null && hero.shotList.get(i).isLive) { + if (hero.bulletList.get(i) != null && hero.bulletList.get(i).isLive) { g.setColor(Color.YELLOW); - g.draw3DRect(myShot.sx, myShot.sy, 3, 3, false); + g.draw3DRect(myBullet.sx, myBullet.sy, 3, 3, false); } //子弹线程死了 就要把它从集合中删除 - if (!myShot.isLive) { - hero.shotList.remove(myShot); + if (!myBullet.isLive) { + hero.bulletList.remove(myBullet); } } /*敌人子弹*/ for (EnemyTank et : enemyList) { - for (int i = 0; i < et.shotList.size(); i++) { - myShot = et.shotList.get(i); + for (int i = 0; i < et.bulletList.size(); i++) { + myBullet = et.bulletList.get(i); for (Brick brick : bricks) { - TankTool.judgeHint(myShot, brick); + TankTool.judgeHint(myBullet, brick); } for (Iron iron : irons) { - TankTool.judgeHint(myShot, iron); + TankTool.judgeHint(myBullet, iron); } - if (myShot.sx < 440 && myShot.sx > 380 && myShot.sy < 540 && myShot.sy > 480) { - myShot.isLive = false; + if (myBullet.sx < 440 && myBullet.sx > 380 && myBullet.sy < 540 && myBullet.sy > 480) { + myBullet.isLive = false; hero.setAlive(false); } - if (et.shotList.get(i) != null && et.shotList.get(i).isLive) { + if (et.bulletList.get(i) != null && et.bulletList.get(i).isLive) { g.setColor(Color.cyan); - g.draw3DRect(myShot.sx, myShot.sy, 1, 1, false); + g.draw3DRect(myBullet.sx, myBullet.sy, 1, 1, false); } - if (!myShot.isLive) { - et.shotList.remove(myShot); + if (!myBullet.isLive) { + et.bulletList.remove(myBullet); } } } @@ -282,16 +283,18 @@ public void paint(Graphics g) { /*画出敌人坦克*/ //坦克少于5个就自动添加4个 - if (enemyList.size() < 5) { - for (int i = 0; i < 4; i++) { - EnemyTank d = new EnemyTank(20 + (int) (Math.random() * 400), 20 + (int) (Math.random() * 300), 2, i % 4); - d.SetInfo(hero, enemyList, bricks, irons); - Thread fillThread = new Thread(d); - fillThread.setName("fillEnemy" + d.id); - fillThread.start(); - enemyList.add(d); - } - } +// if (enemyList.size() < 5) { +// for (int i = 0; i < 4; i++) { +// EnemyTank d = new EnemyTank(20 + (int) (Math.random() * 400), 20 + (int) (Math.random() * 300), 2, i % 4); +// d.SetInfo(hero, enemyList, bricks, irons); +// Thread fillThread = new Thread(d); +// fillThread.setName("fillEnemy" + d.id); +// fillThread.start(); +// enemyList.add(d); +// } +// } + + // for(int i=0;i= h.getHx() - 1 && s.sx <= h.getHx() + 20 && s.sy >= h.getHy() - 1 && s.sy <= h.getHy() + 10) { s.isLive = false; diff --git a/gui/src/main/java/com/github/kuangcp/tank/v1/MyPanel.java b/gui/src/main/java/com/github/kuangcp/tank/v1/MyPanel.java index a354d680..e9a10484 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v1/MyPanel.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v1/MyPanel.java @@ -36,8 +36,8 @@ public void paint(Graphics g) { //调用函数绘画出主坦克 this.drawTank(hero.getX(), hero.getY(), g, hero.getDirect(), hero.getType()); //画出子弹 - if (hero.shot != null) { - g.draw3DRect(hero.shot.sx, hero.shot.sy, 1, 1, false); + if (hero.bullet != null) { + g.draw3DRect(hero.bullet.sx, hero.bullet.sy, 1, 1, false); } //画出敌人坦克 diff --git a/gui/src/main/java/com/github/kuangcp/tank/v2/MyPanel.java b/gui/src/main/java/com/github/kuangcp/tank/v2/MyPanel.java index 77c951de..3c812581 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v2/MyPanel.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v2/MyPanel.java @@ -3,7 +3,7 @@ import com.github.kuangcp.tank.domain.EnemyTank; import com.github.kuangcp.tank.domain.Hero; -import com.github.kuangcp.tank.domain.Shot; +import com.github.kuangcp.tank.domain.Bullet; import javax.swing.*; import java.awt.*; @@ -36,11 +36,11 @@ public void paint(Graphics g) { this.drawTank(hero.getX(), hero.getY(), g, hero.getDirect(), hero.getType()); //从ss 这个子弹集合中 取出每颗子弹,并画出来 - for (int i = 0; i < this.hero.shotList.size(); i++) { - Shot myShot = hero.shotList.get(i); + for (int i = 0; i < this.hero.bulletList.size(); i++) { + Bullet myBullet = hero.bulletList.get(i); // 以下代码只能画出一颗子弹 不完善 - if (myShot != null) { - g.draw3DRect(hero.shot.sx, hero.shot.sy, 1, 1, false); + if (myBullet != null) { + g.draw3DRect(hero.bullet.sx, hero.bullet.sy, 1, 1, false); } } diff --git a/gui/src/main/java/com/github/kuangcp/tank/v3/PlayStageMgr.java b/gui/src/main/java/com/github/kuangcp/tank/v3/PlayStageMgr.java index 5bb2387b..3bcfcb80 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v3/PlayStageMgr.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v3/PlayStageMgr.java @@ -4,8 +4,8 @@ import com.github.kuangcp.tank.domain.EnemyTank; import com.github.kuangcp.tank.domain.Hero; import com.github.kuangcp.tank.domain.Iron; -import com.github.kuangcp.tank.resource.OverImgMgr; -import com.github.kuangcp.tank.resource.WinImgMgr; +import com.github.kuangcp.tank.resource.DefeatImgMgr; +import com.github.kuangcp.tank.resource.VictoryImgMgr; import com.github.kuangcp.tank.util.TankTool; import lombok.extern.slf4j.Slf4j; @@ -97,11 +97,11 @@ public static void drawStopIgnore(Graphics g, ImageObserver observer) { public void drawStop(Graphics g, ImageObserver observer) { g.setColor(Color.YELLOW); if (winCurRound) { - g.drawImage(WinImgMgr.instance.curImg, 0, 0, - WinImgMgr.instance.width, WinImgMgr.instance.height, observer); + g.drawImage(VictoryImgMgr.instance.curImg, 0, 0, + VictoryImgMgr.instance.width, VictoryImgMgr.instance.height, observer); } else { - g.drawImage(OverImgMgr.instance.curImg, 0, 0, - OverImgMgr.instance.width, OverImgMgr.instance.height, observer); + g.drawImage(DefeatImgMgr.instance.curImg, 0, 0, + DefeatImgMgr.instance.width, DefeatImgMgr.instance.height, observer); } g.drawString("您的总成绩为:" + roundPrize, 320, 500); } diff --git a/gui/src/main/java/com/github/kuangcp/tank/v3/SettingFrame.java b/gui/src/main/java/com/github/kuangcp/tank/v3/SettingFrame.java index 6269b44c..156eece8 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v3/SettingFrame.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v3/SettingFrame.java @@ -2,7 +2,7 @@ import com.github.kuangcp.tank.constant.SettingCommand; import com.github.kuangcp.tank.domain.Hero; -import com.github.kuangcp.tank.domain.Shot; +import com.github.kuangcp.tank.domain.Bullet; import com.github.kuangcp.tank.panel.TankGroundPanel; import lombok.extern.slf4j.Slf4j; @@ -94,10 +94,10 @@ public SettingFrame(Hero hero) { @Override public void actionPerformed(ActionEvent event) { if (event.getActionCommand().equals(SettingCommand.SHOT_SPEED_INCREMENT)) { - Shot.setSpeed(Shot.getSpeed() + 1); + Bullet.setSpeed(Bullet.getSpeed() + 1); } if (event.getActionCommand().equals(SettingCommand.SHOT_SPEED_DECREMENT)) { - Shot.setSpeed(Shot.getSpeed() - 1); + Bullet.setSpeed(Bullet.getSpeed() - 1); } if (event.getActionCommand().equals(SettingCommand.TANK_SPEED_INCREMENT)) { From e11fbb46fd3bc2960b355e5a827185ee72eadc23 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Tue, 14 Sep 2021 01:12:35 +0800 Subject: [PATCH 223/476] *) draw self --- .../java/com/github/kuangcp/tank/Readme.md | 7 +- .../github/kuangcp/tank/domain/EnemyTank.java | 5 + .../com/github/kuangcp/tank/domain/Hero.java | 115 ++++++++++++- .../com/github/kuangcp/tank/domain/Tank.java | 7 +- .../kuangcp/tank/panel/TankGroundPanel.java | 2 - .../github/kuangcp/tank/v1/MainPanelV1.java | 96 +++++++++++ .../com/github/kuangcp/tank/v1/MyPanel.java | 157 ------------------ .../github/kuangcp/tank/v1/TankGameV1.java | 13 +- .../com/github/kuangcp/tank/v2/MyPanel.java | 8 +- 9 files changed, 227 insertions(+), 183 deletions(-) create mode 100644 gui/src/main/java/com/github/kuangcp/tank/v1/MainPanelV1.java delete mode 100644 gui/src/main/java/com/github/kuangcp/tank/v1/MyPanel.java diff --git a/gui/src/main/java/com/github/kuangcp/tank/Readme.md b/gui/src/main/java/com/github/kuangcp/tank/Readme.md index a86c1eb5..f34f0cc0 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/Readme.md +++ b/gui/src/main/java/com/github/kuangcp/tank/Readme.md @@ -1,3 +1,8 @@ -gradle tank +# Tank +> like `Tank 1990` + +1. build: `gradle clean tank` +2. extract: `unzip out/build/distributions/gui.zip` +3. run: `./gui/bin/gui` -javaagent:/path/to/quasar-core-0.7.4-jdk8.jar diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java b/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java index fbf22692..88aefa04 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java @@ -8,6 +8,7 @@ import com.github.kuangcp.tank.v3.PlayStageMgr; import lombok.extern.slf4j.Slf4j; +import java.awt.*; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -568,4 +569,8 @@ public void run2() { } } + @Override + public void drawSelf(Graphics g) { + + } } diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/Hero.java b/gui/src/main/java/com/github/kuangcp/tank/domain/Hero.java index 9907912d..d7b62aad 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/domain/Hero.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/Hero.java @@ -19,10 +19,8 @@ public class Hero extends Tank { private int originX, originY; public Bullet bullet = null;//子弹 - public Graphics g; //画笔不可少 private int prize = 0;//击敌个数 public int maxLiveShot = 7;//主坦克子弹线程存活的最大数 - int speed = 3; public int getLife() { return life; @@ -103,18 +101,123 @@ public void shotEnemy() { } public void moveUp() { - setY(getY() - getSpeed()); + this.y -= this.speed; } public void moveDown() { - setY(getY() + getSpeed()); + this.y += this.speed; } public void moveLeft() { - setX(getX() - getSpeed()); + this.x -= this.speed; } public void moveRight() { - setX(getX() + getSpeed()); + this.x += this.speed; + } + + /** + * 画出坦克的函数 XY是坦克中心的坐标,不是画图参照点 + */ + @Override + public void drawSelf(Graphics g) { + //系统画图函数的参照点 (全是取的左上角) + int topX, topY; + g.setColor(Color.yellow); + + this.halfHeight = 30; + this.halfWidth = 20; + switch (direct) { + case DirectType.UP: { + topX = this.x - this.halfWidth; + topY = this.y - this.halfHeight; + + this.drawVerBorder(g, topX, topY); + //5.画出炮管 + g.fill3DRect(topX + this.halfWidth - 1, topY - 1, 2, this.halfWidth + 1, false); + break; + } + case DirectType.DOWN: { + topX = this.x - this.halfWidth; + topY = this.y - this.halfHeight; + + this.drawVerBorder(g, topX, topY); + //5.画出炮管 + g.fill3DRect(topX + this.halfWidth - 1, topY + this.halfWidth * 2 + 1, 2, this.halfWidth + 1, false); + break; + } + case DirectType.LEFT: { + topX = this.x - this.halfHeight; + topY = this.y - this.halfWidth; + + this.drawHorBorder(g, topX, topY); + + //5.画出炮管 + g.fill3DRect(topX - 1, topY + this.halfWidth - 1, this.halfWidth + 1, 2, false); + break; + } + case DirectType.RIGHT: { + topX = this.x - this.halfHeight; + topY = this.y - this.halfWidth; + + this.drawHorBorder(g, topX, topY); + + //5.画出炮管 + g.fill3DRect(topX + this.halfHeight + 2, topY + this.halfWidth - 1, this.halfWidth + 1, 2, false); + break; + } + } + } + + /** + * 水平方向 + */ + private void drawHorBorder(Graphics g, int topX, int topY) { + final int quarterWidth = this.halfWidth / 2; + final int tap = this.halfHeight * 2 / 7; + + for (int i = 0; i < 7; i++) { + g.fill3DRect(topX + tap * i + 1, topY, tap - 1, quarterWidth, false); + } + + g.fill3DRect(topX + quarterWidth * 2, topY + quarterWidth, this.halfHeight * 2 - 4 * quarterWidth, this.halfWidth, false); + + for (int i = 0; i < 7; i++) { + g.fill3DRect(topX + tap * i + 1, topY + this.halfHeight, tap - 1, quarterWidth, false); + } + } + + /** + * 竖直方向 + */ + private void drawVerBorder(Graphics g, int topX, int topY) { + final int quarterWidth = this.halfWidth / 2; + //1.左边的矩形 + final int tap = this.halfHeight * 2 / 7; + for (int i = 0; i < 7; i++) { + g.fill3DRect(topX, topY + tap * i + 1, quarterWidth, tap - 1, false); + } + + //2.画出右边矩形 + for (int i = 0; i < 7; i++) { + g.fill3DRect(topX + quarterWidth * 3, topY + tap * i + 1, quarterWidth, tap - 1, false); + } + + //3.画出中间矩形 + g.fill3DRect(topX + quarterWidth, topY + quarterWidth * 2, this.halfWidth, this.halfHeight * 2 - 4 * quarterWidth, false); + } + + @Override + public String toString() { + return "Hero{" + + "bulletList=" + bulletList + + ", lastShotMs=" + lastShotMs + + ", shotCDMs=" + shotCDMs + + ", x=" + x + + ", y=" + y + + ", prize=" + prize + + ", maxLiveShot=" + maxLiveShot + + ", speed=" + speed + + '}'; } } diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/Tank.java b/gui/src/main/java/com/github/kuangcp/tank/domain/Tank.java index a2c0c1ea..ccb90352 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/domain/Tank.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/Tank.java @@ -1,10 +1,12 @@ package com.github.kuangcp.tank.domain; +import java.awt.*; + /** * 最起初的坦克类 * 往后有继承 */ -public class Tank { +public abstract class Tank { int x; // 坦克中心的横坐标 int y; // 坦克中心的纵坐标 int direct = 0; // 初始方向 @@ -97,10 +99,11 @@ public void setY(int y) { this.y = y; } - // 构造器 public Tank(int x, int y, int speed) { this.x = x; this.y = y; this.speed = speed; } + + public abstract void drawSelf(Graphics g); } diff --git a/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java b/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java index 6b2eb29a..46c371ad 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java +++ b/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java @@ -352,8 +352,6 @@ public void drawTank(int X, int Y, Graphics g, int direct, int type) { break; case 0: g.setColor(Color.yellow); - hero.g = g; - break; } diff --git a/gui/src/main/java/com/github/kuangcp/tank/v1/MainPanelV1.java b/gui/src/main/java/com/github/kuangcp/tank/v1/MainPanelV1.java new file mode 100644 index 00000000..a57e3ce8 --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/v1/MainPanelV1.java @@ -0,0 +1,96 @@ +package com.github.kuangcp.tank.v1; + +import com.github.kuangcp.tank.domain.EnemyTank; +import com.github.kuangcp.tank.domain.Hero; +import lombok.extern.slf4j.Slf4j; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; +import java.util.Objects; +import java.util.Vector; + +/** + * @author https://github.com/kuangcp on 2021-09-11 23:15 + */ +@Slf4j +@SuppressWarnings("serial") +class MainPanelV1 extends JPanel implements KeyListener { + + //定义我的坦克 + Hero hero = null; + //定义泛型的集合类 + Vector ets = new Vector<>(); + + //画板的构造函数 放图形的对象 + public MainPanelV1() { + } + + //重写paint + public void paint(Graphics g) { + super.paint(g); + + if (Objects.isNull(hero)) { + hero = new Hero(20, 20, 4); + } + g.fillRect(0, 0, 500, 400); + + //调用函数绘画出主坦克 + hero.drawSelf(g); + //画出子弹 + if (hero.bullet != null) { + g.draw3DRect(hero.bullet.sx, hero.bullet.sy, 1, 1, false); + } + + //画出敌人坦克 + for (EnemyTank s : ets) { + s.drawSelf(g); + } + + // 画出砖块 + g.setColor(Color.yellow); + g.drawRect(0, 0, 400, 300); + for (int j = 0; j < 20; j++) { + g.draw3DRect(60 + 8 * j, 50, 5, 10, false); + } + + } + + //当按下键盘上的键时监听者的处理函数 + public void keyPressed(KeyEvent e) { + //加了if条件后 实现了墙的限制(如果是游戏中的道具,该怎么办) + if (e.getKeyCode() == KeyEvent.VK_A) { + hero.setDirect(2); + if ((hero.getX() - 10) > 0) + hero.moveLeft(); + + } + if (e.getKeyCode() == KeyEvent.VK_D) { + hero.setDirect(3); + if ((hero.getX() + 15) < 405) + hero.moveRight(); + } + + if (e.getKeyCode() == KeyEvent.VK_W) { + hero.setDirect(0); + if ((hero.getY() - 13) > 0) + hero.moveUp(); + + } + if (e.getKeyCode() == KeyEvent.VK_S) { + hero.setDirect(1); + if ((hero.getY() - 15) < 275) + hero.moveDown(); + + } + //必须重新绘制窗口,不然上面的方法不能视觉上动起来 + this.repaint(); + } + + public void keyReleased(KeyEvent arg0) { + } + + public void keyTyped(KeyEvent arg0) { + } +} diff --git a/gui/src/main/java/com/github/kuangcp/tank/v1/MyPanel.java b/gui/src/main/java/com/github/kuangcp/tank/v1/MyPanel.java deleted file mode 100644 index e9a10484..00000000 --- a/gui/src/main/java/com/github/kuangcp/tank/v1/MyPanel.java +++ /dev/null @@ -1,157 +0,0 @@ -package com.github.kuangcp.tank.v1; - -import com.github.kuangcp.tank.domain.Brick; -import com.github.kuangcp.tank.domain.EnemyTank; -import com.github.kuangcp.tank.domain.Hero; - -import javax.swing.*; -import java.awt.*; -import java.awt.event.KeyEvent; -import java.awt.event.KeyListener; -import java.util.Vector; - -/** - * @author https://github.com/kuangcp on 2021-09-11 23:15 - */ -@SuppressWarnings("serial") -class MyPanel extends JPanel implements KeyListener { - - //定义我的坦克 - Hero hero = null; - int enSize = 3; //敌人的数量 - //定义泛型的集合类 - Vector ets = new Vector<>(); - Vector bricks = new Vector<>(); - - //画板的构造函数 放图形的对象 - public MyPanel() { - } - - //重写paint - public void paint(Graphics g) { - super.paint(g); - hero = new Hero(20,20,4); - g.fillRect(0, 0, 500, 400); - - //调用函数绘画出主坦克 - this.drawTank(hero.getX(), hero.getY(), g, hero.getDirect(), hero.getType()); - //画出子弹 - if (hero.bullet != null) { - g.draw3DRect(hero.bullet.sx, hero.bullet.sy, 1, 1, false); - } - - //画出敌人坦克 - for (EnemyTank s : ets) { - this.drawTank(s.getX(), s.getY(), g, s.getDirect(), s.getType()); - } - - // 画出砖块 - g.setColor(Color.yellow); - g.drawRect(0, 0, 400, 300); - for (int j = 0; j < 20; j++) { - g.draw3DRect(60 + 8 * j, 50, 5, 10, false); - } - - } - - //画出坦克的函数 XY是坦克中心的坐标,不是画图参照点 - public void drawTank(int X, int Y, Graphics g, int direct, int type) { - //判断坦克类型 - int x, y;//系统画图函数的参照点 - switch (type) { - case 1: - g.setColor(Color.cyan); - break; - case 0: - g.setColor(Color.yellow); - break; - } - - //判断方向 - switch (direct) { - - case 0: {//向上VK_UP - x = X - 10; - y = Y - 15; - //1.左边的矩形 - g.fill3DRect(x, y, 5, 30, false); - //2.画出右边矩形 - g.fill3DRect(x + 15, y, 5, 30, false); - //3.画出中间矩形 - g.fill3DRect(x + 5, y + 5, 10, 20, false); - //4.画出圆形 - g.fillOval(x + 4, y + 10, 10, 10); - //5.画出线 - g.drawLine(x + 9, y + 15, x + 9, y); - break; - } - case 1: {//向下 VK_DOWN - x = X - 10; - y = Y - 15; - g.fill3DRect(x, y, 5, 30, false); - g.fill3DRect(x + 15, y, 5, 30, false); - g.fill3DRect(x + 5, y + 5, 10, 20, false); - g.fillOval(x + 4, y + 10, 10, 10); - g.drawLine(x + 9, y + 15, x + 9, y + 30); - break; - } - case 2: {//向左 VK_LEFT - x = X - 15; - y = Y - 10; - g.fill3DRect(x, y, 30, 5, false); - g.fill3DRect(x, y + 15, 30, 5, false); - g.fill3DRect(x + 5, y + 5, 20, 10, false); - g.fillOval(x + 10, y + 4, 10, 10); - g.drawLine(x + 15, y + 9, x, y + 9); - break; - } - case 3: {//向右VK_RIGHT - x = X - 15; - y = Y - 10; - g.fill3DRect(x, y, 30, 5, false); - g.fill3DRect(x, y + 15, 30, 5, false); - g.fill3DRect(x + 5, y + 5, 20, 10, false); - g.fillOval(x + 10, y + 4, 10, 10); - g.drawLine(x + 15, y + 9, x + 30, y + 9); - break; - } - } - } - - //当按下键盘上的键时监听者的处理函数 - public void keyPressed(KeyEvent e) { - //加了if条件后 实现了墙的限制(如果是游戏中的道具,该怎么办) - if (e.getKeyCode() == KeyEvent.VK_A) { - hero.setDirect(2); - if ((hero.getX() - 10) > 0) - hero.moveLeft(); - - } - if (e.getKeyCode() == KeyEvent.VK_D) { - hero.setDirect(3); - if ((hero.getX() + 15) < 405) - hero.moveRight(); - } - - if (e.getKeyCode() == KeyEvent.VK_W) { - hero.setDirect(0); - if ((hero.getY() - 13) > 0) - hero.moveUp(); - - } - if (e.getKeyCode() == KeyEvent.VK_S) { - hero.setDirect(1); - if ((hero.getY() - 15) < 275) - hero.moveDown(); - - } - //必须重新绘制窗口,不然上面的方法不能视觉上动起来 - this.repaint(); - } - - public void keyReleased(KeyEvent arg0) { - } - - public void keyTyped(KeyEvent arg0) { - } -} diff --git a/gui/src/main/java/com/github/kuangcp/tank/v1/TankGameV1.java b/gui/src/main/java/com/github/kuangcp/tank/v1/TankGameV1.java index 356983fe..6fe496e9 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v1/TankGameV1.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v1/TankGameV1.java @@ -10,8 +10,6 @@ @SuppressWarnings("serial") public class TankGameV1 extends JFrame { - MyPanel mp; - @SuppressWarnings("unused") public static void main(String[] args) { TankGameV1 Tank = new TankGameV1(); @@ -19,15 +17,12 @@ public static void main(String[] args) { //最外层JFrame的构造函数 public TankGameV1() { - mp = new MyPanel(); - - this.add(mp); - //注册键盘监听 - //下面的语句翻译为 :当前类的监;听者是mp - this.addKeyListener(mp); + MainPanelV1 panel = new MainPanelV1(); + this.add(panel); + this.addKeyListener(panel); this.setLocation(900, 200); - this.setSize(500, 400); + this.setSize(405, 333); this.setVisible(true); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } diff --git a/gui/src/main/java/com/github/kuangcp/tank/v2/MyPanel.java b/gui/src/main/java/com/github/kuangcp/tank/v2/MyPanel.java index 3c812581..d63723fd 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v2/MyPanel.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v2/MyPanel.java @@ -4,6 +4,7 @@ import com.github.kuangcp.tank.domain.EnemyTank; import com.github.kuangcp.tank.domain.Hero; import com.github.kuangcp.tank.domain.Bullet; +import com.github.kuangcp.tank.util.TankTool; import javax.swing.*; import java.awt.*; @@ -67,7 +68,6 @@ public void drawTank(int X, int Y, Graphics g, int direct, int type) { break; case 0: g.setColor(Color.yellow); - hero.g = g; break; } @@ -173,11 +173,7 @@ public void keyTyped(KeyEvent arg0) { public void run() { //每隔100ms重绘 while (true) { - try { - Thread.sleep(100); - } catch (Exception e) { - } - + TankTool.yieldMsTime(7); //进行重绘 this.repaint(); } From df99b494cfedb628face6d00da00e1e0a1bcf4ab Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Tue, 14 Sep 2021 01:30:40 +0800 Subject: [PATCH 224/476] *) same draw --- .../github/kuangcp/tank/domain/EnemyTank.java | 3 +- .../com/github/kuangcp/tank/domain/Hero.java | 83 +------------- .../com/github/kuangcp/tank/domain/Tank.java | 88 ++++++++++++++- .../kuangcp/tank/panel/HeroInfoPanel.java | 58 +++------- .../kuangcp/tank/panel/TankGroundPanel.java | 105 +++--------------- 5 files changed, 121 insertions(+), 216 deletions(-) diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java b/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java index 88aefa04..2b0ff8dd 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java @@ -571,6 +571,7 @@ public void run2() { @Override public void drawSelf(Graphics g) { - + g.setColor(Color.cyan); + super.drawSelf(g); } } diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/Hero.java b/gui/src/main/java/com/github/kuangcp/tank/domain/Hero.java index d7b62aad..4db1bb23 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/domain/Hero.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/Hero.java @@ -121,91 +121,10 @@ public void moveRight() { */ @Override public void drawSelf(Graphics g) { - //系统画图函数的参照点 (全是取的左上角) - int topX, topY; g.setColor(Color.yellow); - - this.halfHeight = 30; - this.halfWidth = 20; - switch (direct) { - case DirectType.UP: { - topX = this.x - this.halfWidth; - topY = this.y - this.halfHeight; - - this.drawVerBorder(g, topX, topY); - //5.画出炮管 - g.fill3DRect(topX + this.halfWidth - 1, topY - 1, 2, this.halfWidth + 1, false); - break; - } - case DirectType.DOWN: { - topX = this.x - this.halfWidth; - topY = this.y - this.halfHeight; - - this.drawVerBorder(g, topX, topY); - //5.画出炮管 - g.fill3DRect(topX + this.halfWidth - 1, topY + this.halfWidth * 2 + 1, 2, this.halfWidth + 1, false); - break; - } - case DirectType.LEFT: { - topX = this.x - this.halfHeight; - topY = this.y - this.halfWidth; - - this.drawHorBorder(g, topX, topY); - - //5.画出炮管 - g.fill3DRect(topX - 1, topY + this.halfWidth - 1, this.halfWidth + 1, 2, false); - break; - } - case DirectType.RIGHT: { - topX = this.x - this.halfHeight; - topY = this.y - this.halfWidth; - - this.drawHorBorder(g, topX, topY); - - //5.画出炮管 - g.fill3DRect(topX + this.halfHeight + 2, topY + this.halfWidth - 1, this.halfWidth + 1, 2, false); - break; - } - } + super.drawSelf(g); } - /** - * 水平方向 - */ - private void drawHorBorder(Graphics g, int topX, int topY) { - final int quarterWidth = this.halfWidth / 2; - final int tap = this.halfHeight * 2 / 7; - - for (int i = 0; i < 7; i++) { - g.fill3DRect(topX + tap * i + 1, topY, tap - 1, quarterWidth, false); - } - - g.fill3DRect(topX + quarterWidth * 2, topY + quarterWidth, this.halfHeight * 2 - 4 * quarterWidth, this.halfWidth, false); - - for (int i = 0; i < 7; i++) { - g.fill3DRect(topX + tap * i + 1, topY + this.halfHeight, tap - 1, quarterWidth, false); - } - } - - /** - * 竖直方向 - */ - private void drawVerBorder(Graphics g, int topX, int topY) { - final int quarterWidth = this.halfWidth / 2; - //1.左边的矩形 - final int tap = this.halfHeight * 2 / 7; - for (int i = 0; i < 7; i++) { - g.fill3DRect(topX, topY + tap * i + 1, quarterWidth, tap - 1, false); - } - - //2.画出右边矩形 - for (int i = 0; i < 7; i++) { - g.fill3DRect(topX + quarterWidth * 3, topY + tap * i + 1, quarterWidth, tap - 1, false); - } - - //3.画出中间矩形 - g.fill3DRect(topX + quarterWidth, topY + quarterWidth * 2, this.halfWidth, this.halfHeight * 2 - 4 * quarterWidth, false); - } @Override public String toString() { diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/Tank.java b/gui/src/main/java/com/github/kuangcp/tank/domain/Tank.java index ccb90352..8f5b8ed8 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/domain/Tank.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/Tank.java @@ -1,5 +1,7 @@ package com.github.kuangcp.tank.domain; +import com.github.kuangcp.tank.constant.DirectType; + import java.awt.*; /** @@ -105,5 +107,89 @@ public Tank(int x, int y, int speed) { this.speed = speed; } - public abstract void drawSelf(Graphics g); + /** + * 画出坦克的函数 XY是坦克中心的坐标,不是画图参照点 + */ + public void drawSelf(Graphics g){ + //系统画图函数的参照点 (全是取的左上角) + int topX, topY; + + switch (direct) { + case DirectType.UP: { + topX = this.x - this.halfWidth; + topY = this.y - this.halfHeight; + + this.drawVerBorder(g, topX, topY); + //5.画出炮管 + g.fill3DRect(topX + this.halfWidth - 1, topY - 1, 2, this.halfWidth + 1, false); + break; + } + case DirectType.DOWN: { + topX = this.x - this.halfWidth; + topY = this.y - this.halfHeight; + + this.drawVerBorder(g, topX, topY); + //5.画出炮管 + g.fill3DRect(topX + this.halfWidth - 1, topY + this.halfWidth * 2 + 1, 2, this.halfWidth + 1, false); + break; + } + case DirectType.LEFT: { + topX = this.x - this.halfHeight; + topY = this.y - this.halfWidth; + + this.drawHorBorder(g, topX, topY); + + //5.画出炮管 + g.fill3DRect(topX - 1, topY + this.halfWidth - 1, this.halfWidth + 1, 2, false); + break; + } + case DirectType.RIGHT: { + topX = this.x - this.halfHeight; + topY = this.y - this.halfWidth; + + this.drawHorBorder(g, topX, topY); + + //5.画出炮管 + g.fill3DRect(topX + this.halfHeight + 2, topY + this.halfWidth - 1, this.halfWidth + 1, 2, false); + break; + } + } + } + /** + * 水平方向 + */ + private void drawHorBorder(Graphics g, int topX, int topY) { + final int quarterWidth = this.halfWidth / 2; + final int tap = this.halfHeight * 2 / 7; + + for (int i = 0; i < 7; i++) { + g.fill3DRect(topX + tap * i + 1, topY, tap - 1, quarterWidth, false); + } + + g.fill3DRect(topX + quarterWidth * 2, topY + quarterWidth, this.halfHeight * 2 - 4 * quarterWidth, this.halfWidth, false); + + for (int i = 0; i < 7; i++) { + g.fill3DRect(topX + tap * i + 1, topY + this.halfHeight, tap - 1, quarterWidth, false); + } + } + + /** + * 竖直方向 + */ + private void drawVerBorder(Graphics g, int topX, int topY) { + final int quarterWidth = this.halfWidth / 2; + //1.左边的矩形 + final int tap = this.halfHeight * 2 / 7; + for (int i = 0; i < 7; i++) { + g.fill3DRect(topX, topY + tap * i + 1, quarterWidth, tap - 1, false); + } + + //2.画出右边矩形 + for (int i = 0; i < 7; i++) { + g.fill3DRect(topX + quarterWidth * 3, topY + tap * i + 1, quarterWidth, tap - 1, false); + } + + //3.画出中间矩形 + g.fill3DRect(topX + quarterWidth, topY + quarterWidth * 2, this.halfWidth, this.halfHeight * 2 - 4 * quarterWidth, false); + } } diff --git a/gui/src/main/java/com/github/kuangcp/tank/panel/HeroInfoPanel.java b/gui/src/main/java/com/github/kuangcp/tank/panel/HeroInfoPanel.java index f89beeca..0fb9d184 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/panel/HeroInfoPanel.java +++ b/gui/src/main/java/com/github/kuangcp/tank/panel/HeroInfoPanel.java @@ -2,6 +2,7 @@ import com.github.kuangcp.tank.domain.EnemyTank; +import com.github.kuangcp.tank.domain.Tank; import com.github.kuangcp.tank.thread.ExitFlagRunnable; import com.github.kuangcp.tank.util.TankTool; import com.github.kuangcp.tank.v3.PlayStageMgr; @@ -24,6 +25,21 @@ public class HeroInfoPanel extends JPanel implements ExitFlagRunnable { JLabel prizeNo; List ets; + final Tank heroIcon = new Tank(20, 20, 0) { + @Override + public void drawSelf(Graphics g) { + g.setColor(Color.yellow); + super.drawSelf(g); + } + }; + final Tank enemyIcon = new Tank(100, 20, 0) { + @Override + public void drawSelf(Graphics g) { + g.setColor(Color.cyan); + super.drawSelf(g); + } + }; + private volatile boolean exit = false; public void exit() { @@ -42,46 +58,8 @@ public HeroInfoPanel(JLabel jl1, List ets, JLabel prizeNo) { public void paint(Graphics g) { super.paint(g); - if (PlayStageMgr.waitStart()) { - return; - } - - int X = 20, Y = 20; - int x, y; - /*主坦克*/ - g.setColor(Color.yellow); - //向上 - x = X - 10; - y = Y - 15;//把坦克中心坐标换算成系统用来画坦克的坐标 - //(并且全是取的左上角) - - g.fill3DRect(x, y, 5, 30, false); - g.fill3DRect(x + 15, y, 5, 30, false); - g.fill3DRect(x + 5, y + 5, 10, 20, false); - g.fillOval(x + 4, y + 10, 10, 10); - g.drawLine(x + 9, y + 15, x + 9, y); - - /*敌人坦克*/ - g.setColor(Color.cyan); - X = 100; - Y = 20; - x = X - 10; - y = Y - 15;//把坦克中心坐标换算成系统用来画坦克的坐标 - //(并且全是取的左上角) - - g.fill3DRect(x, y, 5, 30, false); - g.fill3DRect(x + 15, y, 5, 30, false); - g.fill3DRect(x + 5, y + 5, 10, 20, false); - g.fillOval(x + 4, y + 10, 10, 10); - g.drawLine(x + 9, y + 15, x + 9, y); - -// MyPanel3 tank = new MyPanel3();//因为原本的MyPanel是做成了线程 莫名其妙就被调用到了 -// //主坦克: -// g.setColor(Color.yellow); -// tank.drawTank(0, 0, g, 0, 0); -// //敌人坦克: -// g.setColor(Color.cyan); -// tank.drawTank(0, 40, g, 0, 1); + heroIcon.drawSelf(g); + enemyIcon.drawSelf(g); } public void run() { diff --git a/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java b/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java index 46c371ad..4bbe5aa4 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java +++ b/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java @@ -184,7 +184,7 @@ public void paint(Graphics g) { } /*画出坦克运动区域 */ - g.setColor(new Color(0, 0, 0)); + g.setColor(new Color(8, 8, 8, 237)); g.fillRect(0, 0, 760, 560);//填满一个黑色矩形,说明了是一整个JPanel在JFrame上的 g.setColor(Color.green); g.drawRect(20, 20, 720, 520); @@ -226,7 +226,7 @@ public void paint(Graphics g) { BombMgr.instance.checkBong(hero, et.bulletList); } - this.drawTank(hero.getX(), hero.getY(), g, hero.getDirect(), hero.getType()); + hero.drawSelf(g); } // 画出自己的子弹画子弹是可以封装成一个方法的 @@ -283,16 +283,16 @@ public void paint(Graphics g) { /*画出敌人坦克*/ //坦克少于5个就自动添加4个 -// if (enemyList.size() < 5) { -// for (int i = 0; i < 4; i++) { -// EnemyTank d = new EnemyTank(20 + (int) (Math.random() * 400), 20 + (int) (Math.random() * 300), 2, i % 4); -// d.SetInfo(hero, enemyList, bricks, irons); -// Thread fillThread = new Thread(d); -// fillThread.setName("fillEnemy" + d.id); -// fillThread.start(); -// enemyList.add(d); -// } -// } + if (enemyList.size() < 5) { + for (int i = 0; i < 4; i++) { + EnemyTank d = new EnemyTank(20 + (int) (Math.random() * 400), 20 + (int) (Math.random() * 300), 2, i % 4); + d.SetInfo(hero, enemyList, bricks, irons); + Thread fillThread = new Thread(d); + fillThread.setName("fillEnemy" + d.id); + fillThread.start(); + enemyList.add(d); + } + } // for(int i=0;i From 152ee601403bb9b3eb44a945c02ebda4916b9025 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Tue, 14 Sep 2021 02:07:10 +0800 Subject: [PATCH 225/476] *) color mark life --- .../github/kuangcp/tank/domain/EnemyTank.java | 30 +++++++++++---- .../com/github/kuangcp/tank/domain/Hero.java | 2 +- .../kuangcp/tank/panel/StageActionPanel.java | 4 +- .../kuangcp/tank/panel/TankGroundPanel.java | 13 +------ .../github/kuangcp/tank/v1/MainPanelV1.java | 38 +++++++++++++++++++ .../github/kuangcp/tank/v3/PlayStageMgr.java | 16 ++++++++ .../github/kuangcp/tank/v3/SettingFrame.java | 9 ++--- 7 files changed, 86 insertions(+), 26 deletions(-) diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java b/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java index 2b0ff8dd..b100b4da 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java @@ -11,7 +11,9 @@ import java.awt.*; import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.atomic.AtomicLong; @@ -42,6 +44,20 @@ public class EnemyTank extends Tank implements Runnable { boolean bri = true; boolean ableMove = true; + /** + * 随机生成 的 最大生命值 + */ + private static final int maxLife = 5; + private static final Map colorMap = new HashMap<>(maxLife); + + static { + colorMap.put(1, Color.WHITE); + colorMap.put(2, new Color(93, 217, 41)); + colorMap.put(3, new Color(34, 155, 234)); + colorMap.put(4, new Color(155, 62, 202)); + colorMap.put(5, new Color(240, 57, 23)); + } + //继承了属性,即使直接使用父类的构造器,构造器也一定要显式声明 public EnemyTank(int x, int y, int speed, int direct) { super(x, y, speed); @@ -49,11 +65,17 @@ public EnemyTank(int x, int y, int speed, int direct) { this.direct = direct; this.speed = speed; this.alive = true; - this.life = ThreadLocalRandom.current().nextInt(3) + 1; + this.life = ThreadLocalRandom.current().nextInt(maxLife) + 1; this.id = counter.addAndGet(1); // log.info("create new Demons. {}", id); } + @Override + public void drawSelf(Graphics g) { + g.setColor(colorMap.getOrDefault(this.life, Color.cyan)); + super.drawSelf(g); + } + public void SetInfo(Hero hero, List ets, List bricks, List irons) { this.ets = ets; this.bricks = bricks; @@ -568,10 +590,4 @@ public void run2() { } } } - - @Override - public void drawSelf(Graphics g) { - g.setColor(Color.cyan); - super.drawSelf(g); - } } diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/Hero.java b/gui/src/main/java/com/github/kuangcp/tank/domain/Hero.java index 4db1bb23..14d5b585 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/domain/Hero.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/Hero.java @@ -16,7 +16,7 @@ public class Hero extends Tank { private long lastShotMs = 0; private long shotCDMs = 268; - private int originX, originY; + private final int originX, originY; public Bullet bullet = null;//子弹 private int prize = 0;//击敌个数 diff --git a/gui/src/main/java/com/github/kuangcp/tank/panel/StageActionPanel.java b/gui/src/main/java/com/github/kuangcp/tank/panel/StageActionPanel.java index 4e452b3a..efb0685b 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/panel/StageActionPanel.java +++ b/gui/src/main/java/com/github/kuangcp/tank/panel/StageActionPanel.java @@ -116,11 +116,11 @@ public void actionPerformed(ActionEvent ae) { private void startNewStage() { - final int totalMaxShots = TankGroundPanel.getEnSize() * EnemyTank.maxLiveShot; + final int totalMaxShots = PlayStageMgr.getEnemySize() * EnemyTank.maxLiveShot; // 重新设置线程池大小 final ThreadPoolExecutor pool = (ThreadPoolExecutor) ExecutePool.shotPool; - final int poolSize = (int) Math.max(TankGroundPanel.getEnSize() * EnemyTank.maxLiveShot * 0.6, 10); + final int poolSize = (int) Math.max(PlayStageMgr.getEnemySize() * EnemyTank.maxLiveShot * 0.6, 10); pool.setCorePoolSize(poolSize); pool.setMaximumPoolSize(poolSize); diff --git a/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java b/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java index 4bbe5aa4..a4c9bd4e 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java +++ b/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java @@ -37,8 +37,7 @@ public class TankGroundPanel extends JPanel implements java.awt.event.KeyListene public volatile Hero hero; public KeyListener keyListener; public static boolean newStage = true; - // 敌人的数量 - public static int enSize = 1; + //定义一个 泛型的集合ets 表示敌人坦克集合 public List enemyList = Collections.synchronizedList(new ArrayList<>()); @@ -87,7 +86,7 @@ public void startNewRound() { // 创建 敌人的坦克 EnemyTank ett = null; if (newStage) {//正常启动并创建坦克线程 - for (int i = 0; i < enSize; i++) { + for (int i = 0; i < PlayStageMgr.getEnemySize(); i++) { //在四个随机区域产生坦克 switch ((int) (Math.random() * 4)) { case 0: @@ -436,12 +435,4 @@ public void run() { // clean listener keyListener.exit(); } - - public static int getEnSize() { - return enSize; - } - - public static void setEnSize(int enSize) { - TankGroundPanel.enSize = enSize; - } } \ No newline at end of file diff --git a/gui/src/main/java/com/github/kuangcp/tank/v1/MainPanelV1.java b/gui/src/main/java/com/github/kuangcp/tank/v1/MainPanelV1.java index a57e3ce8..98f77f85 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v1/MainPanelV1.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v1/MainPanelV1.java @@ -2,6 +2,7 @@ import com.github.kuangcp.tank.domain.EnemyTank; import com.github.kuangcp.tank.domain.Hero; +import com.github.kuangcp.tank.domain.Tank; import lombok.extern.slf4j.Slf4j; import javax.swing.*; @@ -38,6 +39,43 @@ public void paint(Graphics g) { //调用函数绘画出主坦克 hero.drawSelf(g); + + new Tank(50, 20, 0) { + @Override + public void drawSelf(Graphics g) { + g.setColor(Color.WHITE); + super.drawSelf(g); + } + }.drawSelf(g); + new Tank(80, 20, 0) { + @Override + public void drawSelf(Graphics g) { + g.setColor(new Color(93, 217, 41)); + super.drawSelf(g); + } + }.drawSelf(g); + new Tank(110, 20, 0) { + @Override + public void drawSelf(Graphics g) { + g.setColor(new Color(34, 155, 234)); + super.drawSelf(g); + } + }.drawSelf(g); + new Tank(140, 20, 0) { + @Override + public void drawSelf(Graphics g) { + g.setColor(new Color(155, 62, 202)); + super.drawSelf(g); + } + }.drawSelf(g); + new Tank(170, 20, 0) { + @Override + public void drawSelf(Graphics g) { + g.setColor(new Color(240, 57, 23)); + super.drawSelf(g); + } + }.drawSelf(g); + //画出子弹 if (hero.bullet != null) { g.draw3DRect(hero.bullet.sx, hero.bullet.sy, 1, 1, false); diff --git a/gui/src/main/java/com/github/kuangcp/tank/v3/PlayStageMgr.java b/gui/src/main/java/com/github/kuangcp/tank/v3/PlayStageMgr.java index 3bcfcb80..a28b4ea4 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v3/PlayStageMgr.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v3/PlayStageMgr.java @@ -28,6 +28,10 @@ public class PlayStageMgr { public int roundPrize = 0; static int round = 0; + /** + * 敌人的数量 + */ + static int enemySize = 14; public List enemyTanks; public List bricks; // 砖 public List irons; // 铁 @@ -116,4 +120,16 @@ public void drawStop(Graphics g, ImageObserver observer) { public boolean ableToMove(Hero hero) { return enemyTanks.stream().allMatch(v -> TankTool.ablePass(hero, v)); } + + public static int getEnemySize() { + return enemySize; + } + + public static void setEnemySize(int enemySize) { + PlayStageMgr.enemySize = enemySize; + } + + public static void addEnemySize(int delta) { + PlayStageMgr.enemySize += delta; + } } diff --git a/gui/src/main/java/com/github/kuangcp/tank/v3/SettingFrame.java b/gui/src/main/java/com/github/kuangcp/tank/v3/SettingFrame.java index 156eece8..0f2e9908 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v3/SettingFrame.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v3/SettingFrame.java @@ -1,9 +1,8 @@ package com.github.kuangcp.tank.v3; import com.github.kuangcp.tank.constant.SettingCommand; -import com.github.kuangcp.tank.domain.Hero; import com.github.kuangcp.tank.domain.Bullet; -import com.github.kuangcp.tank.panel.TankGroundPanel; +import com.github.kuangcp.tank.domain.Hero; import lombok.extern.slf4j.Slf4j; import javax.imageio.ImageIO; @@ -115,11 +114,11 @@ public void actionPerformed(ActionEvent event) { } if (event.getActionCommand().equals(SettingCommand.DEMONS_COUNT_INCREMENT)) { - TankGroundPanel.setEnSize(TankGroundPanel.getEnSize() + 1); - log.info("{}", TankGroundPanel.getEnSize()); + PlayStageMgr.addEnemySize(1); + log.info("{}", PlayStageMgr.getEnemySize()); } if (event.getActionCommand().equals(SettingCommand.DEMONS_COUNT_DECREMENT)) { - TankGroundPanel.setEnSize(TankGroundPanel.getEnSize() - 1); + PlayStageMgr.addEnemySize(-1); } } From 26e27d0d488e2481c65711a650a0d7e60d2a4390 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Tue, 14 Sep 2021 02:49:46 +0800 Subject: [PATCH 226/476] *) add invincible state --- .../github/kuangcp/tank/domain/Bullet.java | 10 +-- .../github/kuangcp/tank/domain/EnemyTank.java | 4 +- .../com/github/kuangcp/tank/domain/Hero.java | 43 ++++++--- .../com/github/kuangcp/tank/domain/Tank.java | 7 ++ .../com/github/kuangcp/tank/mgr/BombMgr.java | 88 ++++++++++--------- .../kuangcp/tank/panel/TankGroundPanel.java | 12 +-- .../github/kuangcp/tank/util/TankTool.java | 2 +- .../github/kuangcp/tank/v3/PlayStageMgr.java | 14 ++- .../github/kuangcp/tank/v3/SettingFrame.java | 6 +- 9 files changed, 115 insertions(+), 71 deletions(-) diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/Bullet.java b/gui/src/main/java/com/github/kuangcp/tank/domain/Bullet.java index 9aeda2dd..bbbc8c62 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/domain/Bullet.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/Bullet.java @@ -13,7 +13,7 @@ public class Bullet implements Runnable { public int sy; public int direct; public static int speed = 3;//如果改动要记得按钮事件里也要改 - public boolean isLive = true;//是否还活着 + public boolean alive = true;//是否还活着 public static int getSpeed() { return speed; @@ -52,13 +52,13 @@ public void run() { //判断子弹是否碰到边缘 if (sx < 20 || sx > 740 || sy < 20 || sy > 540) { - this.isLive = false; + this.alive = false; } if (sx < 440 && sx > 380 && sy < 540 && sy > 480) { - this.isLive = false; + this.alive = false; } - } while (this.isLive); - log.info("shot die"); + } while (this.alive); +// log.info("bullet die"); } } diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java b/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java index b100b4da..dbc3657b 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java @@ -427,11 +427,11 @@ public void run() { // actionModeRun(); run2(); - log.info("enemy die"); +// log.info("enemy die"); if (this.isAbort()) { for (Bullet d : this.bulletList) { - d.isLive = false; + d.alive = false; } } } diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/Hero.java b/gui/src/main/java/com/github/kuangcp/tank/domain/Hero.java index 14d5b585..4855f813 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/domain/Hero.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/Hero.java @@ -3,11 +3,15 @@ import com.github.kuangcp.tank.constant.DirectType; import com.github.kuangcp.tank.util.ExecutePool; +import com.github.kuangcp.tank.v3.PlayStageMgr; +import lombok.extern.slf4j.Slf4j; import java.awt.*; import java.util.Vector; import java.util.concurrent.ExecutorService; +import java.util.concurrent.ThreadLocalRandom; +@Slf4j public class Hero extends Tank { //子弹集合 @@ -21,6 +25,7 @@ public class Hero extends Tank { public Bullet bullet = null;//子弹 private int prize = 0;//击敌个数 public int maxLiveShot = 7;//主坦克子弹线程存活的最大数 + private long lastDieMs = 0; public int getLife() { return life; @@ -61,7 +66,35 @@ public Hero(int x, int y, int speed) { this.shotExecutePool = ExecutePool.buildFixedPool("heroShot", maxLiveShot); } + /** + * 画出坦克的函数 XY是坦克中心的坐标,不是画图参照点 + */ + @Override + public void drawSelf(Graphics g) { + g.setColor(Color.yellow); + if (this.isInvincible()) { + g.setColor(Color.cyan); + } + super.drawSelf(g); + } + + public boolean isInvincible() { + // 复活 无敌 + if (System.currentTimeMillis() - this.lastDieMs < PlayStageMgr.getInvincibleMs()) { + return true; + } + // TODO 道具 无敌 + return false; + } + public void resurrect() { + this.lastDieMs = System.currentTimeMillis(); + + // 1/10 概率原地复活 + if (ThreadLocalRandom.current().nextInt(10) == 0) { + return; + } + this.setX(this.originX); this.setY(this.originY); this.setDirect(DirectType.UP); @@ -116,16 +149,6 @@ public void moveRight() { this.x += this.speed; } - /** - * 画出坦克的函数 XY是坦克中心的坐标,不是画图参照点 - */ - @Override - public void drawSelf(Graphics g) { - g.setColor(Color.yellow); - super.drawSelf(g); - } - - @Override public String toString() { return "Hero{" + diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/Tank.java b/gui/src/main/java/com/github/kuangcp/tank/domain/Tank.java index 8f5b8ed8..5d095fc1 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/domain/Tank.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/Tank.java @@ -1,6 +1,7 @@ package com.github.kuangcp.tank.domain; import com.github.kuangcp.tank.constant.DirectType; +import lombok.extern.slf4j.Slf4j; import java.awt.*; @@ -8,6 +9,7 @@ * 最起初的坦克类 * 往后有继承 */ +@Slf4j public abstract class Tank { int x; // 坦克中心的横坐标 int y; // 坦克中心的纵坐标 @@ -29,6 +31,11 @@ public void setLife(int life) { this.life = life; } + public void addLife(int delta){ + log.info("delta={}", delta); + this.life += delta; + } + public boolean isAbort() { return abort; } diff --git a/gui/src/main/java/com/github/kuangcp/tank/mgr/BombMgr.java b/gui/src/main/java/com/github/kuangcp/tank/mgr/BombMgr.java index 71522d90..d8adb92f 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/mgr/BombMgr.java +++ b/gui/src/main/java/com/github/kuangcp/tank/mgr/BombMgr.java @@ -2,8 +2,8 @@ import com.github.kuangcp.tank.constant.DirectType; import com.github.kuangcp.tank.domain.Bomb; -import com.github.kuangcp.tank.domain.Hero; import com.github.kuangcp.tank.domain.Bullet; +import com.github.kuangcp.tank.domain.Hero; import com.github.kuangcp.tank.domain.Tank; import com.github.kuangcp.tank.resource.AbstractImgListMgr; import lombok.extern.slf4j.Slf4j; @@ -13,6 +13,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Objects; /** * @author https://github.com/kuangcp on 2021-09-11 16:28 @@ -36,7 +37,6 @@ public void drawBomb(Graphics g, JPanel panel) { for (int i = 0; i < bombs.size(); i++) { //取出炸弹 Bomb b = bombs.get(i); -// System.out.println("size = "+bombs.size()); if (b.life > 10) { g.drawImage(BombMgr.instance.imgArr[0], b.bx, b.by, 30, 30, panel); // if(rr){ @@ -56,7 +56,6 @@ public void drawBomb(Graphics g, JPanel panel) { if (b.life == 0) { bombs.remove(b); } -// System.out.println("size = "+bombs.size()); } } @@ -67,58 +66,61 @@ public void checkBong(Tank tank, List bullets) { bullets.forEach(v -> this.checkBong(tank, v)); } - private void checkBong(Tank t, Bullet s) { - if (!s.isLive) { + private void checkBong(Tank tank, Bullet bullet) { + if (!bullet.alive) { return; } - switch (t.getDirect()) { + switch (tank.getDirect()) { case DirectType.UP: case DirectType.DOWN: - if (t.getX() - 10 <= s.sx && - t.getX() + 10 >= s.sx && - t.getY() - 15 <= s.sy && - t.getY() + 15 >= s.sy) { - s.isLive = false; - - t.setLife(t.getLife() - 1); - - if (t.getLife() <= 0) { - t.setAlive(false); - } - - //创建一个炸弹,放入集合 - Bomb b = new Bomb(t.getX() - 10, t.getY() - 15);//敌方的坐标 - bombs.add(b); - - // 复活 - if (t instanceof Hero) { - ((Hero) t).resurrect(); - } + if (tank.getX() - 10 <= bullet.sx && + tank.getX() + 10 >= bullet.sx && + tank.getY() - 15 <= bullet.sy && + tank.getY() + 15 >= bullet.sy) { + bullet.alive = false; + final int bx = tank.getX() - tank.getHalfWidth(); + final int by = tank.getY() - tank.getHalfHeight(); + + this.handleBombAndTank(tank, bx, by); } break; case DirectType.LEFT: case DirectType.RIGHT: - if (t.getX() - 15 <= s.sx && - t.getX() + 15 >= s.sx && - t.getY() - 10 <= s.sy && - t.getY() + 10 >= s.sy) { - s.isLive = false; - t.setLife(t.getLife() - 1); - - if (t.getLife() == 0) t.setAlive(false); - - //创建一个炸弹,放入集合 - Bomb b = new Bomb(t.getX() - 15, t.getY() - 10);//敌方的坐标 - bombs.add(b); - - // 复活 - if (t instanceof Hero) { - ((Hero) t).resurrect(); - } + if (tank.getX() - 15 <= bullet.sx && + tank.getX() + 15 >= bullet.sx && + tank.getY() - 10 <= bullet.sy && + tank.getY() + 10 >= bullet.sy) { + bullet.alive = false; + final int bx = tank.getX() - tank.getHalfHeight(); + final int by = tank.getY() - tank.getHalfWidth(); + + this.handleBombAndTank(tank, bx, by); + break; } break; } } + private void handleBombAndTank(Tank tank, int bx, int by) { + // 复活 或 无敌 + Hero hero = null; + if (tank instanceof Hero) { + hero = (Hero) tank; + if (hero.isInvincible()) { + return; + } + } + + tank.addLife(-1); + if (tank.getLife() <= 0) { + tank.setAlive(false); + } + + bombs.add(new Bomb(bx, by)); + + if (Objects.nonNull(hero)) { + hero.resurrect(); + } + } } diff --git a/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java b/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java index a4c9bd4e..a1d2e9db 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java +++ b/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java @@ -239,16 +239,16 @@ public void paint(Graphics g) { TankTool.judgeHint(myBullet, iron); } if (myBullet.sx < 440 && myBullet.sx > 380 && myBullet.sy < 540 && myBullet.sy > 480) { - myBullet.isLive = false; + myBullet.alive = false; hero.setAlive(false); } - if (hero.bulletList.get(i) != null && hero.bulletList.get(i).isLive) { + if (hero.bulletList.get(i) != null && hero.bulletList.get(i).alive) { g.setColor(Color.YELLOW); g.draw3DRect(myBullet.sx, myBullet.sy, 3, 3, false); } //子弹线程死了 就要把它从集合中删除 - if (!myBullet.isLive) { + if (!myBullet.alive) { hero.bulletList.remove(myBullet); } } @@ -264,15 +264,15 @@ public void paint(Graphics g) { TankTool.judgeHint(myBullet, iron); } if (myBullet.sx < 440 && myBullet.sx > 380 && myBullet.sy < 540 && myBullet.sy > 480) { - myBullet.isLive = false; + myBullet.alive = false; hero.setAlive(false); } - if (et.bulletList.get(i) != null && et.bulletList.get(i).isLive) { + if (et.bulletList.get(i) != null && et.bulletList.get(i).alive) { g.setColor(Color.cyan); g.draw3DRect(myBullet.sx, myBullet.sy, 1, 1, false); } - if (!myBullet.isLive) { + if (!myBullet.alive) { et.bulletList.remove(myBullet); } } diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/TankTool.java b/gui/src/main/java/com/github/kuangcp/tank/util/TankTool.java index 004cf5d2..8467c0d5 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/util/TankTool.java +++ b/gui/src/main/java/com/github/kuangcp/tank/util/TankTool.java @@ -215,7 +215,7 @@ public static boolean ablePass(Tank t, Hinder h) { public static void judgeHint(Bullet s, Hinder h) { if (s.sx >= h.getHx() - 1 && s.sx <= h.getHx() + 20 && s.sy >= h.getHy() - 1 && s.sy <= h.getHy() + 10) { - s.isLive = false; + s.alive = false; if (h instanceof Brick) { h.setAlive(false); diff --git a/gui/src/main/java/com/github/kuangcp/tank/v3/PlayStageMgr.java b/gui/src/main/java/com/github/kuangcp/tank/v3/PlayStageMgr.java index a28b4ea4..2b2dacb2 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v3/PlayStageMgr.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v3/PlayStageMgr.java @@ -28,10 +28,18 @@ public class PlayStageMgr { public int roundPrize = 0; static int round = 0; + + // 游戏参数 配置 /** * 敌人的数量 */ - static int enemySize = 14; + static int enemySize = 36; + /** + * 无敌状态 时间 + */ + static long invincibleMs = 3000L; + + // 场景 上下文 public List enemyTanks; public List bricks; // 砖 public List irons; // 铁 @@ -132,4 +140,8 @@ public static void setEnemySize(int enemySize) { public static void addEnemySize(int delta) { PlayStageMgr.enemySize += delta; } + + public static long getInvincibleMs() { + return invincibleMs; + } } diff --git a/gui/src/main/java/com/github/kuangcp/tank/v3/SettingFrame.java b/gui/src/main/java/com/github/kuangcp/tank/v3/SettingFrame.java index 0f2e9908..2beafaef 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v3/SettingFrame.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v3/SettingFrame.java @@ -107,15 +107,15 @@ public void actionPerformed(ActionEvent event) { } if (event.getActionCommand().equals(SettingCommand.TANK_HP_INCREMENT)) { - this.hero.setLife(this.hero.getLife() + 1); + this.hero.addLife(1); } if (event.getActionCommand().equals(SettingCommand.TANK_HP_DECREMENT)) { - this.hero.setLife(this.hero.getLife() - 1); + this.hero.addLife(-1); } if (event.getActionCommand().equals(SettingCommand.DEMONS_COUNT_INCREMENT)) { PlayStageMgr.addEnemySize(1); - log.info("{}", PlayStageMgr.getEnemySize()); + log.info("{}", PlayStageMgr.getEnemySize()); } if (event.getActionCommand().equals(SettingCommand.DEMONS_COUNT_DECREMENT)) { PlayStageMgr.addEnemySize(-1); From 26a1ad7fcb988399933cf4fad22b1f364596ac8b Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Thu, 16 Sep 2021 02:57:23 +0800 Subject: [PATCH 227/476] *) optimize bullet thread --- .../github/kuangcp/tank/domain/Bullet.java | 53 ++++++- .../github/kuangcp/tank/domain/EnemyTank.java | 10 +- .../com/github/kuangcp/tank/domain/Hero.java | 76 ++++----- .../com/github/kuangcp/tank/domain/Tank.java | 10 +- .../kuangcp/tank/panel/TankGroundPanel.java | 8 +- .../kuangcp/tank/util/AbstractLoopEvent.java | 98 ++++++++++++ .../github/kuangcp/tank/util/ExecutePool.java | 8 +- .../github/kuangcp/tank/util/LoopEvent.java | 22 +++ .../tank/util/LoopEventExecutePool.java | 44 ++++++ .../github/kuangcp/tank/v3/MainTankGame.java | 3 + .../tank/util/LoopEventExecutePoolTest.java | 149 ++++++++++++++++++ 11 files changed, 429 insertions(+), 52 deletions(-) create mode 100644 gui/src/main/java/com/github/kuangcp/tank/util/AbstractLoopEvent.java create mode 100644 gui/src/main/java/com/github/kuangcp/tank/util/LoopEvent.java create mode 100644 gui/src/main/java/com/github/kuangcp/tank/util/LoopEventExecutePool.java create mode 100644 gui/src/test/java/com/github/kuangcp/tank/util/LoopEventExecutePoolTest.java diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/Bullet.java b/gui/src/main/java/com/github/kuangcp/tank/domain/Bullet.java index bbbc8c62..6678e3b0 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/domain/Bullet.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/Bullet.java @@ -1,13 +1,17 @@ package com.github.kuangcp.tank.domain; +import com.github.kuangcp.tank.util.AbstractLoopEvent; import com.github.kuangcp.tank.util.TankTool; import lombok.extern.slf4j.Slf4j; +import java.util.UUID; +import java.util.concurrent.TimeUnit; + /** * 子弹类 对象做成了线程 */ @Slf4j -public class Bullet implements Runnable { +public class Bullet extends AbstractLoopEvent { public int sx; public int sy; @@ -15,6 +19,9 @@ public class Bullet implements Runnable { public static int speed = 3;//如果改动要记得按钮事件里也要改 public boolean alive = true;//是否还活着 + public static long fixedDelayTime = 50; + public static long delayStartTime = 50; + public static int getSpeed() { return speed; } @@ -24,12 +31,56 @@ public static void setSpeed(int s) { } public Bullet(int sx, int sy, int direct) { + super(UUID.randomUUID().toString(), fixedDelayTime, delayStartTime, TimeUnit.MILLISECONDS); + + this.sx = sx; + this.sy = sy; + this.direct = direct; + } + + public Bullet(String id, int sx, int sy, int direct) { + super(id, fixedDelayTime, delayStartTime, TimeUnit.MILLISECONDS); this.sx = sx; this.sy = sy; this.direct = direct; } + @Override public void run() { + newRun(); + } + + private void newRun() { + switch (direct) { + //上下左右 + case 0: + sy -= speed; + break; + case 1: + sy += speed; + break; + case 2: + sx -= speed; + break; + case 3: + sx += speed; + break; + } + + //判断子弹是否碰到边缘 + if (sx < 20 || sx > 740 || sy < 20 || sy > 540) { + this.alive = false; + this.stop(); + } + + if (sx < 440 && sx > 380 && sy < 540 && sy > 480) { + this.alive = false; + this.stop(); + } + // log.info("bullet die"); + } + + private void originRun() { do { // 每个子弹发射的延迟运动的时间 TankTool.yieldMsTime(55); diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java b/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java index dbc3657b..5ad500f8 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java @@ -3,7 +3,7 @@ import com.github.kuangcp.tank.constant.DirectType; -import com.github.kuangcp.tank.util.ExecutePool; +import com.github.kuangcp.tank.util.LoopEventExecutePool; import com.github.kuangcp.tank.util.TankTool; import com.github.kuangcp.tank.v3.PlayStageMgr; import lombok.extern.slf4j.Slf4j; @@ -140,9 +140,13 @@ public void shotEnemy() { break; } } - //启动子弹线程 - ExecutePool.shotPool.execute(s); + LoopEventExecutePool.addLoopEvent(s); + + // 常规线程池 +// ExecutePool.shotPool.execute(s); + // ForkJoin // ExecutePool.forkJoinPool.execute(s); + // 协程池 // ExecutePool.shotScheduler.getExecutor().execute(s); lastShotMs = nowMs; diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/Hero.java b/gui/src/main/java/com/github/kuangcp/tank/domain/Hero.java index 4855f813..639aa290 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/domain/Hero.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/Hero.java @@ -2,13 +2,12 @@ import com.github.kuangcp.tank.constant.DirectType; -import com.github.kuangcp.tank.util.ExecutePool; +import com.github.kuangcp.tank.util.LoopEventExecutePool; import com.github.kuangcp.tank.v3.PlayStageMgr; import lombok.extern.slf4j.Slf4j; import java.awt.*; import java.util.Vector; -import java.util.concurrent.ExecutorService; import java.util.concurrent.ThreadLocalRandom; @Slf4j @@ -16,7 +15,6 @@ public class Hero extends Tank { //子弹集合 public Vector bulletList = new Vector<>(); - private final ExecutorService shotExecutePool; private long lastShotMs = 0; private long shotCDMs = 268; @@ -27,43 +25,10 @@ public class Hero extends Tank { public int maxLiveShot = 7;//主坦克子弹线程存活的最大数 private long lastDieMs = 0; - public int getLife() { - return life; - } - - public void setLife(int life) { - this.life = life; - } - - public int getSpeed() { - return speed; - } - - public void setSpeed(int speed) { - this.speed = speed; - } - - public void addSpeed(int delta) { - this.speed += delta; - } - - public int getPrize() { - return prize; - } - - public void setPrize(int prize) { - this.prize = prize; - } - - public void addPrize(int delta) { - this.prize += delta; - } - public Hero(int x, int y, int speed) { super(x, y, speed); this.originX = x; this.originY = y; - this.shotExecutePool = ExecutePool.buildFixedPool("heroShot", maxLiveShot); } /** @@ -91,7 +56,7 @@ public void resurrect() { this.lastDieMs = System.currentTimeMillis(); // 1/10 概率原地复活 - if (ThreadLocalRandom.current().nextInt(10) == 0) { + if (ThreadLocalRandom.current().nextInt(1) == 0) { return; } @@ -129,7 +94,8 @@ public void shotEnemy() { break; } //启动子弹线程 - shotExecutePool.execute(bullet); +// shotExecutePool.execute(bullet); + LoopEventExecutePool.addLoopEvent(bullet); lastShotMs = nowMs; } @@ -149,6 +115,40 @@ public void moveRight() { this.x += this.speed; } + + public int getLife() { + return life; + } + + public void setLife(int life) { + this.life = life; + } + + public int getSpeed() { + return speed; + } + + public void setSpeed(int speed) { + this.speed = speed; + } + + public void addSpeed(int delta) { + this.speed += delta; + } + + public int getPrize() { + return prize; + } + + public void setPrize(int prize) { + this.prize = prize; + } + + public void addPrize(int delta) { + this.prize += delta; + } + + @Override public String toString() { return "Hero{" + diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/Tank.java b/gui/src/main/java/com/github/kuangcp/tank/domain/Tank.java index 5d095fc1..706278e6 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/domain/Tank.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/Tank.java @@ -15,10 +15,10 @@ public abstract class Tank { int y; // 坦克中心的纵坐标 int direct = 0; // 初始方向 int type = 0; // 坦克的种类 - int speed = 5; // 前进的步长 + int speed; // 前进的步长 boolean alive = true;// 是否存活 boolean abort = false; // 重开一局等外部终止因素 - int life = 1;//生命值 + int life = 1; //生命值 int halfWidth = 10; int halfHeight = 15; @@ -31,8 +31,7 @@ public void setLife(int life) { this.life = life; } - public void addLife(int delta){ - log.info("delta={}", delta); + public void addLife(int delta) { this.life += delta; } @@ -117,7 +116,7 @@ public Tank(int x, int y, int speed) { /** * 画出坦克的函数 XY是坦克中心的坐标,不是画图参照点 */ - public void drawSelf(Graphics g){ + public void drawSelf(Graphics g) { //系统画图函数的参照点 (全是取的左上角) int topX, topY; @@ -162,6 +161,7 @@ public void drawSelf(Graphics g){ } } } + /** * 水平方向 */ diff --git a/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java b/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java index a1d2e9db..1af65495 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java +++ b/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java @@ -187,7 +187,7 @@ public void paint(Graphics g) { g.fillRect(0, 0, 760, 560);//填满一个黑色矩形,说明了是一整个JPanel在JFrame上的 g.setColor(Color.green); g.drawRect(20, 20, 720, 520); - Bullet myBullet; + /*画出障碍物__砖__ 铁__*/ @@ -231,7 +231,7 @@ public void paint(Graphics g) { // 画出自己的子弹画子弹是可以封装成一个方法的 // 从ss 这个子弹集合中 取出每颗子弹,并画出来 for (int i = 0; i < hero.bulletList.size(); i++) { - myBullet = hero.bulletList.get(i); + Bullet myBullet = hero.bulletList.get(i); for (Brick brick : bricks) { TankTool.judgeHint(myBullet, brick); } @@ -256,7 +256,7 @@ public void paint(Graphics g) { /*敌人子弹*/ for (EnemyTank et : enemyList) { for (int i = 0; i < et.bulletList.size(); i++) { - myBullet = et.bulletList.get(i); + Bullet myBullet = et.bulletList.get(i); for (Brick brick : bricks) { TankTool.judgeHint(myBullet, brick); } @@ -314,7 +314,7 @@ public void paint(Graphics g) { demon.drawSelf(g); } else { - // TODO 移除 延迟删除逻辑 + // TODO 去掉 延迟删除逻辑 // enemyList.remove(demon); // 延迟删除 敌人和子弹 diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/AbstractLoopEvent.java b/gui/src/main/java/com/github/kuangcp/tank/util/AbstractLoopEvent.java new file mode 100644 index 00000000..ee6886f5 --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/util/AbstractLoopEvent.java @@ -0,0 +1,98 @@ +package com.github.kuangcp.tank.util; + +import lombok.extern.slf4j.Slf4j; + +import java.util.UUID; +import java.util.concurrent.Delayed; +import java.util.concurrent.TimeUnit; + +/** + * @author https://github.com/kuangcp on 2021-09-16 01:44 + * 均以 ms 驱动 + */ +@Slf4j +public abstract class AbstractLoopEvent implements LoopEvent { + + private static final long defaultDelay = 50; + /** + * 时间id + */ + public String id; + /** + * 下次事件触发时机 + */ + long nextTickTime; + + long fixedDelayTime; + + public AbstractLoopEvent() { + this.id = UUID.randomUUID().toString(); + this.nextTickTime = System.currentTimeMillis() + defaultDelay; + } + + public AbstractLoopEvent(String id, long fixedDelay) { + this.id = id; + this.fixedDelayTime = fixedDelay; + this.nextTickTime = System.currentTimeMillis(); + } + + public AbstractLoopEvent(String id, long delayStart, TimeUnit timeUnit) { + this.id = id; + this.nextTickTime = System.currentTimeMillis() + timeUnit.toMillis(delayStart); + } + + public AbstractLoopEvent(String id, long fixedDelay, long delayStart, TimeUnit timeUnit) { + this.id = id; + this.fixedDelayTime = fixedDelay; + this.nextTickTime = System.currentTimeMillis() + timeUnit.toMillis(delayStart); + } + + @Override + public void addDelay(long delay) { + this.nextTickTime += delay; + } + + @Override + public boolean addFixedDelay() { + if (fixedDelayTime <= 0) { + return false; + } + this.addDelay(fixedDelayTime); + return isContinue(); + } + + @Override + public boolean isStop() { + return this.nextTickTime <= 0; + } + + @Override + public boolean isContinue() { + return !isStop(); + } + + @Override + public void stop() { +// log.info("{} stop", this); + this.nextTickTime = 0; + } + + @Override + public int compareTo(Delayed o) { + return Long.compare(this.getDelay(TimeUnit.MILLISECONDS), o.getDelay(TimeUnit.MILLISECONDS)); + } + + @Override + public long getDelay(TimeUnit unit) { + return unit.convert(nextTickTime - System.currentTimeMillis(), TimeUnit.MILLISECONDS); + } + + @Override + public String toString() { + return "AbstractLoopEvent{" + + "id='" + id + '\'' + + ", nextTickTime=" + nextTickTime + + ", fixedDelayTime=" + fixedDelayTime + + '}'; + } +} diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/ExecutePool.java b/gui/src/main/java/com/github/kuangcp/tank/util/ExecutePool.java index 6705140c..cb51d5d4 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/util/ExecutePool.java +++ b/gui/src/main/java/com/github/kuangcp/tank/util/ExecutePool.java @@ -17,7 +17,13 @@ public class ExecutePool { * 敌人全部的子弹线程 */ public static final ExecutorService shotPool = ExecutePool.buildFixedPool("enemyShot", 5); -// public static final ExecutorService eventPool = ExecutePool.buildFixedPool("event", 2); + + public static final int EVENT_POOL_SIZE = 5; + + /** + * 循环事件线程池 + */ + public static final ExecutorService loopEventPool = ExecutePool.buildFixedPool("loopEvent", EVENT_POOL_SIZE); // ForkJoin // public static final ForkJoinPool forkJoinPool = new ForkJoinPool(65); diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/LoopEvent.java b/gui/src/main/java/com/github/kuangcp/tank/util/LoopEvent.java new file mode 100644 index 00000000..a2befff6 --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/util/LoopEvent.java @@ -0,0 +1,22 @@ +package com.github.kuangcp.tank.util; + +import java.util.concurrent.Delayed; + +/** + * @author https://github.com/kuangcp on 2021-09-16 01:21 + */ +public interface LoopEvent extends Runnable, Delayed { + + void addDelay(long delay); + + boolean addFixedDelay(); + + /** + * 事件需要被移除 + */ + boolean isStop(); + + boolean isContinue(); + + void stop(); +} diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/LoopEventExecutePool.java b/gui/src/main/java/com/github/kuangcp/tank/util/LoopEventExecutePool.java new file mode 100644 index 00000000..e464eab6 --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/util/LoopEventExecutePool.java @@ -0,0 +1,44 @@ +package com.github.kuangcp.tank.util; + +import lombok.extern.slf4j.Slf4j; + +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.DelayQueue; + +/** + * @author https://github.com/kuangcp on 2021-09-16 01:20 + */ +@Slf4j +public class LoopEventExecutePool { + + private static final BlockingQueue queue = new DelayQueue<>(); + + public static void init() { + final Runnable mainLoop = () -> { + while (true) { + try { + final AbstractLoopEvent event = queue.take(); +// log.info("event {}", event); + event.run(); + if (event.isContinue() && event.addFixedDelay()) { + queue.add(event); + } + } catch (InterruptedException e) { + log.error("", e); + break; + } + } + }; + + for (int i = 0; i < ExecutePool.EVENT_POOL_SIZE; i++) { + ExecutePool.loopEventPool.execute(mainLoop); + } + } + + public static void addLoopEvent(AbstractLoopEvent loopEvent) { +// log.info("add: loopEvent={}", loopEvent); + queue.add(loopEvent); + } + +} + diff --git a/gui/src/main/java/com/github/kuangcp/tank/v3/MainTankGame.java b/gui/src/main/java/com/github/kuangcp/tank/v3/MainTankGame.java index 8a5542c8..52078845 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v3/MainTankGame.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v3/MainTankGame.java @@ -1,5 +1,6 @@ package com.github.kuangcp.tank.v3; +import com.github.kuangcp.tank.util.LoopEventExecutePool; import lombok.extern.slf4j.Slf4j; import java.awt.*; @@ -13,6 +14,8 @@ public class MainTankGame { * https://stackoverflow.com/questions/6736906/why-does-java-swings-setvisible-take-so-long */ public static void main(String[] args) { + LoopEventExecutePool.init(); + EventQueue.invokeLater(new MainFrame()); } } diff --git a/gui/src/test/java/com/github/kuangcp/tank/util/LoopEventExecutePoolTest.java b/gui/src/test/java/com/github/kuangcp/tank/util/LoopEventExecutePoolTest.java new file mode 100644 index 00000000..66160d72 --- /dev/null +++ b/gui/src/test/java/com/github/kuangcp/tank/util/LoopEventExecutePoolTest.java @@ -0,0 +1,149 @@ +package com.github.kuangcp.tank.util; + +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; + +import java.util.Objects; +import java.util.UUID; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.DelayQueue; +import java.util.concurrent.Delayed; +import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.TimeUnit; + +/** + * @author https://github.com/kuangcp on 2021-09-16 01:28 + */ +@Slf4j +public class LoopEventExecutePoolTest { + + static class SampleTask implements Delayed { + String id; + long time; + + public SampleTask(String id, long time) { + this.time = time; + this.id = id; + } + + public void addDelay(long delay) { + this.time += delay; + } + + public long getTime() { + return time; + } + + @Override + public int compareTo(Delayed o) { + return Long.compare(this.getDelay(TimeUnit.MILLISECONDS), o.getDelay(TimeUnit.MILLISECONDS)); + } + + @Override + public long getDelay(TimeUnit unit) { + return unit.convert(time - System.currentTimeMillis(), TimeUnit.MILLISECONDS); + } + + @Override + public String toString() { + return "SampleTask{" + + "id='" + id + '\'' + + ", time=" + time + + '}'; + } + } + + static class EventTask extends AbstractLoopEvent { + public int sx; + public int sy; + public int direct = 0; + public static int speed = 3;//如果改动要记得按钮事件里也要改 + + public EventTask(String id, long fixedDelay, long delayStart, TimeUnit timeUnit, int x, int y) { + super(id, fixedDelay, delayStart, timeUnit); + this.sx = x; + this.sy = y; + } + + @Override + public void run() { + // 每个子弹发射的延迟运动的时间 +// TankTool.yieldMsTime(55); + + switch (direct) { + //上下左右 + case 0: + sy -= speed; + break; + case 1: + sy += speed; + break; + case 2: + sx -= speed; + break; + case 3: + sx += speed; + break; + } + + //判断子弹是否碰到边缘 + if (sx < 20 || sx > 740 || sy < 20 || sy > 540) { + this.stop(); + } + + if (sx < 440 && sx > 380 && sy < 540 && sy > 480) { + this.stop(); + } + log.info("sx={} sy={}", sx, sy); + } + } + + @Test + public void testAction() throws Exception { + for (int i = 0; i < ThreadLocalRandom.current().nextInt(100000) + 1000000; i++) { + Objects.hash(UUID.randomUUID()); + } + } + + @Test + public void testRun() throws Exception { + BlockingQueue delayQueue = new DelayQueue<>(); + long now = System.currentTimeMillis(); + delayQueue.put(new SampleTask("A", now + 1000)); + delayQueue.put(new SampleTask("B", now + 1500)); + delayQueue.put(new SampleTask("C", now + 1800)); + + while (true) { + final SampleTask tmp = delayQueue.take(); + now = System.currentTimeMillis(); + log.info("task={} delay={}", tmp, now - tmp.getTime()); + + for (int i = 0; i < ThreadLocalRandom.current().nextInt(100000) + 1000000; i++) { + Objects.hash(UUID.randomUUID()); + } + + tmp.addDelay(1000); + delayQueue.add(tmp); + } + } + + @Test + public void testShotBullet() throws Exception { + BlockingQueue delayQueue = new DelayQueue<>(); + delayQueue.add(new EventTask("xx", 55, 55, TimeUnit.MILLISECONDS, 60, 120)); + while (true) { + final EventTask task = delayQueue.take(); + task.run(); + if (task.isContinue()) { + task.addFixedDelay(); + delayQueue.add(task); + } + } + } + + @Test + public void testTime() throws Exception { + System.out.println(TimeUnit.SECONDS.toMillis(20)); + } + +} \ No newline at end of file From 4be35b8297fb1e7cbb7f2a838990269f59943beb Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Thu, 16 Sep 2021 03:57:10 +0800 Subject: [PATCH 228/476] *) optimize enemy action to event --- .../github/kuangcp/tank/domain/Bullet.java | 16 +- .../github/kuangcp/tank/domain/EnemyTank.java | 145 ++++++++++++++++-- .../com/github/kuangcp/tank/domain/Hero.java | 5 + .../com/github/kuangcp/tank/domain/Tank.java | 3 +- .../kuangcp/tank/panel/HeroInfoPanel.java | 10 ++ .../kuangcp/tank/panel/StageActionPanel.java | 12 +- .../kuangcp/tank/panel/TankGroundPanel.java | 27 ++-- .../kuangcp/tank/util/AbstractLoopEvent.java | 32 ++-- .../github/kuangcp/tank/util/ExecutePool.java | 4 +- .../tank/util/LoopEventExecutePool.java | 13 ++ .../github/kuangcp/tank/v1/MainPanelV1.java | 25 +++ .../tank/util/LoopEventExecutePoolTest.java | 27 +++- 12 files changed, 258 insertions(+), 61 deletions(-) diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/Bullet.java b/gui/src/main/java/com/github/kuangcp/tank/domain/Bullet.java index 6678e3b0..b29001be 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/domain/Bullet.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/Bullet.java @@ -4,9 +4,6 @@ import com.github.kuangcp.tank.util.TankTool; import lombok.extern.slf4j.Slf4j; -import java.util.UUID; -import java.util.concurrent.TimeUnit; - /** * 子弹类 对象做成了线程 */ @@ -19,8 +16,8 @@ public class Bullet extends AbstractLoopEvent { public static int speed = 3;//如果改动要记得按钮事件里也要改 public boolean alive = true;//是否还活着 - public static long fixedDelayTime = 50; - public static long delayStartTime = 50; + public static final long fixedDelayTime = 50; + public static final long delayStartTime = 50; public static int getSpeed() { return speed; @@ -31,18 +28,11 @@ public static void setSpeed(int s) { } public Bullet(int sx, int sy, int direct) { - super(UUID.randomUUID().toString(), fixedDelayTime, delayStartTime, TimeUnit.MILLISECONDS); - this.sx = sx; this.sy = sy; this.direct = direct; - } - public Bullet(String id, int sx, int sy, int direct) { - super(id, fixedDelayTime, delayStartTime, TimeUnit.MILLISECONDS); - this.sx = sx; - this.sy = sy; - this.direct = direct; + this.setFixedDelayTime(fixedDelayTime); } @Override diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java b/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java index 5ad500f8..12b2aab5 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java @@ -33,7 +33,7 @@ public class EnemyTank extends Tank implements Runnable { public List bulletList = Collections.synchronizedList(new ArrayList<>());//子弹集合 private long lastShotMs = 0; private long shotCDMs = 168; - public static int maxLiveShot = 3; //子弹线程存活的最大数 + public static int maxLiveShot = 30; //子弹线程存活的最大数 public boolean delayRemove = false; // 延迟回收内存,避免子弹线程执行中断 public List ets; @@ -68,6 +68,20 @@ public EnemyTank(int x, int y, int speed, int direct) { this.life = ThreadLocalRandom.current().nextInt(maxLife) + 1; this.id = counter.addAndGet(1); // log.info("create new Demons. {}", id); + + //TODO 动机算法 调整后的刷新率 +// this.setFixedDelayTime(320); + this.setFixedDelayTime(90); + + this.registerHook(() -> { + if (this.isAbort()) { + for (Bullet d : this.bulletList) { + d.alive = false; + } + } else { + PlayStageMgr.instance.hero.addPrize(1); + } + }); } @Override @@ -426,18 +440,24 @@ public boolean toRight() { //重写 int min = 0; - @Override - public void run() { -// actionModeRun(); - run2(); +// @Override +// public void run() { +//// actionModeRun(); +// run2(); +// +//// log.info("enemy die"); +// +// if (this.isAbort()) { +// for (Bullet d : this.bulletList) { +// d.alive = false; +// } +// } +// } -// log.info("enemy die"); - if (this.isAbort()) { - for (Bullet d : this.bulletList) { - d.alive = false; - } - } + @Override + public void run() { + newEventRun(); } // 运动 @@ -594,4 +614,107 @@ public void run2() { } } } + + + public void newEventRun() { + //判断坦克是否死亡 + if (!this.isAlive()) { + this.stop(); + return; + } + + //子弹发射的(时间)坐标间隔 + min++; + if (min > 10000) min -= 10000; +// System.out.println("x = "+this.x+" y = "+this.y); +// System.out.println(speed); 终于找出错误,speed没有初始化 + + //让坦克随机产生一个随机方向 + if (this.speed != 0) this.direct = (int) (Math.random() * 4); + + switch (this.direct) {//上下左右 + case 0://说明坦克正在向上移动 + for (int i = 0; i < 5; i++) { + min++; +// if(!bri)this.direct = (int)(Math.random()*4); + if (y > 30) { + if (TankTool.ablePass(this, PlayStageMgr.instance.hero) && overlap) y -= speed; +// if(bri)y-=speed; +// else {y+=speed;this.direct = 1;} +// else continue; + else break;//这样才不会有敌人坦克凑在你附近不动 +// else this.direct = (int)(Math.random()*4); + if (min % 27 == 0) + this.shotEnemy(); +// TankTool.yieldMsTime(50); + } + + } + + break; + case 1: + for (int i = 0; i < 5; i++) { + min++; +// if(!bri)this.direct = (int)(Math.random()*4); + + if (y < 530) { + if (TankTool.ablePass(this, PlayStageMgr.instance.hero) && overlap && bri) y += speed; +// if(bri) y+=speed; +// else {y-=speed;this.direct = 0;} +// else continue; + else break; + if (min % 27 == 0) { + this.shotEnemy(); + } +// TankTool.yieldMsTime(50); + } + + } + + break; + case 2: + for (int i = 0; i < 5; i++) { + min++; +// if(!bri)this.direct = (int)(Math.random()*4); + + if (x > 30) { + if (TankTool.ablePass(this, PlayStageMgr.instance.hero) && overlap && bri) x -= speed; +// if(bri)x-=speed; +// else{x+=speed;this.direct = 3;} +// else continue; + else break; + if (min % 27 == 0) + this.shotEnemy(); +// TankTool.yieldMsTime(50); + } + + } + + break; + case 3: + for (int i = 0; i < 5; i++) { + min++; +// if(!bri)this.direct = (int)(Math.random()*4); + + if (x < 710) { + if (TankTool.ablePass(this, PlayStageMgr.instance.hero) && overlap && bri) { + x += speed; + } +// if(bri)x+=speed; +// else{x-=speed;this.direct = 2;} + else break; +// else continue; + if (min % 27 == 0) + this.shotEnemy(); + +// TankTool.yieldMsTime(50); + } + + + } + break; + default: + break; + } + } } diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/Hero.java b/gui/src/main/java/com/github/kuangcp/tank/domain/Hero.java index 639aa290..c025a864 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/domain/Hero.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/Hero.java @@ -31,6 +31,11 @@ public Hero(int x, int y, int speed) { this.originY = y; } + @Override + public void run() { + + } + /** * 画出坦克的函数 XY是坦克中心的坐标,不是画图参照点 */ diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/Tank.java b/gui/src/main/java/com/github/kuangcp/tank/domain/Tank.java index 706278e6..f5ccac81 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/domain/Tank.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/Tank.java @@ -1,6 +1,7 @@ package com.github.kuangcp.tank.domain; import com.github.kuangcp.tank.constant.DirectType; +import com.github.kuangcp.tank.util.AbstractLoopEvent; import lombok.extern.slf4j.Slf4j; import java.awt.*; @@ -10,7 +11,7 @@ * 往后有继承 */ @Slf4j -public abstract class Tank { +public abstract class Tank extends AbstractLoopEvent { int x; // 坦克中心的横坐标 int y; // 坦克中心的纵坐标 int direct = 0; // 初始方向 diff --git a/gui/src/main/java/com/github/kuangcp/tank/panel/HeroInfoPanel.java b/gui/src/main/java/com/github/kuangcp/tank/panel/HeroInfoPanel.java index 0fb9d184..706eeeb9 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/panel/HeroInfoPanel.java +++ b/gui/src/main/java/com/github/kuangcp/tank/panel/HeroInfoPanel.java @@ -31,6 +31,11 @@ public void drawSelf(Graphics g) { g.setColor(Color.yellow); super.drawSelf(g); } + + @Override + public void run() { + + } }; final Tank enemyIcon = new Tank(100, 20, 0) { @Override @@ -38,6 +43,11 @@ public void drawSelf(Graphics g) { g.setColor(Color.cyan); super.drawSelf(g); } + + @Override + public void run() { + + } }; private volatile boolean exit = false; diff --git a/gui/src/main/java/com/github/kuangcp/tank/panel/StageActionPanel.java b/gui/src/main/java/com/github/kuangcp/tank/panel/StageActionPanel.java index efb0685b..8746ca0e 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/panel/StageActionPanel.java +++ b/gui/src/main/java/com/github/kuangcp/tank/panel/StageActionPanel.java @@ -4,12 +4,11 @@ import com.github.kuangcp.tank.constant.StageCommand; import com.github.kuangcp.tank.domain.Brick; +import com.github.kuangcp.tank.domain.Bullet; import com.github.kuangcp.tank.domain.EnemyTank; import com.github.kuangcp.tank.domain.Hero; import com.github.kuangcp.tank.domain.Iron; -import com.github.kuangcp.tank.domain.Bullet; import com.github.kuangcp.tank.util.Audio; -import com.github.kuangcp.tank.util.ExecutePool; import com.github.kuangcp.tank.util.Saved; import com.github.kuangcp.tank.v3.MainFrame; import com.github.kuangcp.tank.v3.PlayStageMgr; @@ -22,7 +21,6 @@ import java.awt.event.ActionListener; import java.util.List; import java.util.Objects; -import java.util.concurrent.ThreadPoolExecutor; /** * 用来放按钮的面板 @@ -119,10 +117,10 @@ private void startNewStage() { final int totalMaxShots = PlayStageMgr.getEnemySize() * EnemyTank.maxLiveShot; // 重新设置线程池大小 - final ThreadPoolExecutor pool = (ThreadPoolExecutor) ExecutePool.shotPool; - final int poolSize = (int) Math.max(PlayStageMgr.getEnemySize() * EnemyTank.maxLiveShot * 0.6, 10); - pool.setCorePoolSize(poolSize); - pool.setMaximumPoolSize(poolSize); +// final ThreadPoolExecutor pool = (ThreadPoolExecutor) ExecutePool.shotPool; +// final int poolSize = (int) Math.max(PlayStageMgr.getEnemySize() * EnemyTank.maxLiveShot * 0.6, 10); +// pool.setCorePoolSize(poolSize); +// pool.setMaximumPoolSize(poolSize); // 协程 // final int poolSize = (int) Math.max(totalMaxShots * 0.4, 10); diff --git a/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java b/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java index 1af65495..47edea1e 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java +++ b/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java @@ -13,6 +13,7 @@ import com.github.kuangcp.tank.util.ExecutePool; import com.github.kuangcp.tank.util.KeyListener; import com.github.kuangcp.tank.util.ListenEventGroup; +import com.github.kuangcp.tank.util.LoopEventExecutePool; import com.github.kuangcp.tank.util.TankTool; import com.github.kuangcp.tank.v3.PlayStageMgr; import lombok.extern.slf4j.Slf4j; @@ -110,9 +111,10 @@ public void startNewRound() { if (Objects.isNull(ett)) { continue; } - Thread t = new Thread(ett); - t.setName("enemyThread-" + ett.id); - t.start(); + LoopEventExecutePool.addLoopEvent(ett); +// Thread t = new Thread(ett); +// t.setName("enemyThread-" + ett.id); +// t.start(); //坦克加入集合 enemyList.add(ett); } @@ -123,10 +125,12 @@ public void startNewRound() { ett = new EnemyTank(enemyTankMap[i][0], enemyTankMap[i][1], 2, i % 4); ett.SetInfo(hero, enemyList, bricks, irons); - Thread t = new Thread(ett); - // TODO 改造成一个线程或者少数线程 是否使用时间轮 - t.setName("enemyThreadF-" + ett.id); - t.start(); + + LoopEventExecutePool.addLoopEvent(ett); + +// Thread t = new Thread(ett); +// t.setName("enemyThreadF-" + ett.id); +// t.start(); //坦克加入集合 enemyList.add(ett); // System.out.print("创建单个坦克地址:"+ett); @@ -254,6 +258,7 @@ public void paint(Graphics g) { } /*敌人子弹*/ + // FIXME ConcurrentModificationException for (EnemyTank et : enemyList) { for (int i = 0; i < et.bulletList.size(); i++) { Bullet myBullet = et.bulletList.get(i); @@ -286,9 +291,11 @@ public void paint(Graphics g) { for (int i = 0; i < 4; i++) { EnemyTank d = new EnemyTank(20 + (int) (Math.random() * 400), 20 + (int) (Math.random() * 300), 2, i % 4); d.SetInfo(hero, enemyList, bricks, irons); - Thread fillThread = new Thread(d); - fillThread.setName("fillEnemy" + d.id); - fillThread.start(); + + LoopEventExecutePool.addLoopEvent(d); +// Thread fillThread = new Thread(d); +// fillThread.setName("fillEnemy" + d.id); +// fillThread.start(); enemyList.add(d); } } diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/AbstractLoopEvent.java b/gui/src/main/java/com/github/kuangcp/tank/util/AbstractLoopEvent.java index ee6886f5..02524a49 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/util/AbstractLoopEvent.java +++ b/gui/src/main/java/com/github/kuangcp/tank/util/AbstractLoopEvent.java @@ -2,6 +2,7 @@ import lombok.extern.slf4j.Slf4j; +import java.util.Objects; import java.util.UUID; import java.util.concurrent.Delayed; import java.util.concurrent.TimeUnit; @@ -13,7 +14,7 @@ @Slf4j public abstract class AbstractLoopEvent implements LoopEvent { - private static final long defaultDelay = 50; + private static final long defaultDelay = 20; /** * 时间id */ @@ -23,30 +24,29 @@ public abstract class AbstractLoopEvent implements LoopEvent { */ long nextTickTime; - long fixedDelayTime; + long fixedDelayTime = 40; + + Runnable stopHook; public AbstractLoopEvent() { this.id = UUID.randomUUID().toString(); this.nextTickTime = System.currentTimeMillis() + defaultDelay; } - public AbstractLoopEvent(String id, long fixedDelay) { + // 构建任务的补充参数 + public void setId(String id) { this.id = id; - this.fixedDelayTime = fixedDelay; - this.nextTickTime = System.currentTimeMillis(); } - public AbstractLoopEvent(String id, long delayStart, TimeUnit timeUnit) { - this.id = id; - this.nextTickTime = System.currentTimeMillis() + timeUnit.toMillis(delayStart); + public void setFixedDelayTime(long fixedDelayTime) { + this.fixedDelayTime = fixedDelayTime; } - public AbstractLoopEvent(String id, long fixedDelay, long delayStart, TimeUnit timeUnit) { - this.id = id; - this.fixedDelayTime = fixedDelay; - this.nextTickTime = System.currentTimeMillis() + timeUnit.toMillis(delayStart); + public void setDelayStart(long delayStart, TimeUnit timeUnit) { + this.nextTickTime += timeUnit.toMillis(delayStart); } + // 业务动作 @Override public void addDelay(long delay) { this.nextTickTime += delay; @@ -75,6 +75,14 @@ public boolean isContinue() { public void stop() { // log.info("{} stop", this); this.nextTickTime = 0; + + if (Objects.nonNull(this.stopHook)) { + stopHook.run(); + } + } + + public void registerHook(Runnable stopHook) { + this.stopHook = stopHook; } @Override diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/ExecutePool.java b/gui/src/main/java/com/github/kuangcp/tank/util/ExecutePool.java index cb51d5d4..9d198096 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/util/ExecutePool.java +++ b/gui/src/main/java/com/github/kuangcp/tank/util/ExecutePool.java @@ -16,9 +16,9 @@ public class ExecutePool { /** * 敌人全部的子弹线程 */ - public static final ExecutorService shotPool = ExecutePool.buildFixedPool("enemyShot", 5); +// public static final ExecutorService shotPool = ExecutePool.buildFixedPool("enemyShot", 5); - public static final int EVENT_POOL_SIZE = 5; + public static final int EVENT_POOL_SIZE = 6; /** * 循环事件线程池 diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/LoopEventExecutePool.java b/gui/src/main/java/com/github/kuangcp/tank/util/LoopEventExecutePool.java index e464eab6..e9687234 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/util/LoopEventExecutePool.java +++ b/gui/src/main/java/com/github/kuangcp/tank/util/LoopEventExecutePool.java @@ -33,6 +33,19 @@ public static void init() { for (int i = 0; i < ExecutePool.EVENT_POOL_SIZE; i++) { ExecutePool.loopEventPool.execute(mainLoop); } + + final Thread thread = new Thread(() -> { + while (true) { + try { + Thread.sleep(10_000); + log.info("eventSize: {}", queue.size()); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + }); + thread.setName("eventPoolMonitor"); + thread.start(); } public static void addLoopEvent(AbstractLoopEvent loopEvent) { diff --git a/gui/src/main/java/com/github/kuangcp/tank/v1/MainPanelV1.java b/gui/src/main/java/com/github/kuangcp/tank/v1/MainPanelV1.java index 98f77f85..54161182 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v1/MainPanelV1.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v1/MainPanelV1.java @@ -46,6 +46,11 @@ public void drawSelf(Graphics g) { g.setColor(Color.WHITE); super.drawSelf(g); } + + @Override + public void run() { + + } }.drawSelf(g); new Tank(80, 20, 0) { @Override @@ -53,6 +58,11 @@ public void drawSelf(Graphics g) { g.setColor(new Color(93, 217, 41)); super.drawSelf(g); } + + @Override + public void run() { + + } }.drawSelf(g); new Tank(110, 20, 0) { @Override @@ -60,6 +70,11 @@ public void drawSelf(Graphics g) { g.setColor(new Color(34, 155, 234)); super.drawSelf(g); } + + @Override + public void run() { + + } }.drawSelf(g); new Tank(140, 20, 0) { @Override @@ -67,6 +82,11 @@ public void drawSelf(Graphics g) { g.setColor(new Color(155, 62, 202)); super.drawSelf(g); } + + @Override + public void run() { + + } }.drawSelf(g); new Tank(170, 20, 0) { @Override @@ -74,6 +94,11 @@ public void drawSelf(Graphics g) { g.setColor(new Color(240, 57, 23)); super.drawSelf(g); } + + @Override + public void run() { + + } }.drawSelf(g); //画出子弹 diff --git a/gui/src/test/java/com/github/kuangcp/tank/util/LoopEventExecutePoolTest.java b/gui/src/test/java/com/github/kuangcp/tank/util/LoopEventExecutePoolTest.java index 66160d72..6bf1b8be 100644 --- a/gui/src/test/java/com/github/kuangcp/tank/util/LoopEventExecutePoolTest.java +++ b/gui/src/test/java/com/github/kuangcp/tank/util/LoopEventExecutePoolTest.java @@ -1,8 +1,13 @@ package com.github.kuangcp.tank.util; +import com.github.kuangcp.tank.constant.DirectType; +import com.github.kuangcp.tank.domain.EnemyTank; +import com.github.kuangcp.tank.domain.Hero; +import com.github.kuangcp.tank.v3.PlayStageMgr; import lombok.extern.slf4j.Slf4j; import org.junit.Test; +import java.util.Collections; import java.util.Objects; import java.util.UUID; import java.util.concurrent.BlockingQueue; @@ -60,9 +65,12 @@ static class EventTask extends AbstractLoopEvent { public static int speed = 3;//如果改动要记得按钮事件里也要改 public EventTask(String id, long fixedDelay, long delayStart, TimeUnit timeUnit, int x, int y) { - super(id, fixedDelay, delayStart, timeUnit); this.sx = x; this.sy = y; + + this.setId(id); + this.setFixedDelayTime(fixedDelay); + this.setDelayStart(delayStart, timeUnit); } @Override @@ -130,8 +138,12 @@ public void testRun() throws Exception { @Test public void testShotBullet() throws Exception { BlockingQueue delayQueue = new DelayQueue<>(); - delayQueue.add(new EventTask("xx", 55, 55, TimeUnit.MILLISECONDS, 60, 120)); - while (true) { + final EventTask originTask = new EventTask("xx", 55, 55, TimeUnit.MILLISECONDS, 60, 120); + originTask.registerHook(() -> { + System.out.println("end"); + }); + delayQueue.add(originTask); + while (!delayQueue.isEmpty()) { final EventTask task = delayQueue.take(); task.run(); if (task.isContinue()) { @@ -141,9 +153,14 @@ public void testShotBullet() throws Exception { } } + @Test - public void testTime() throws Exception { - System.out.println(TimeUnit.SECONDS.toMillis(20)); + public void testEnemyTank() throws Exception { + final EnemyTank enemyTank = new EnemyTank(30, 30, 2, DirectType.RIGHT); + LoopEventExecutePool.init(); + PlayStageMgr.init(new Hero(240, 50, 1), Collections.emptyList(), Collections.emptyList(), Collections.emptyList()); + LoopEventExecutePool.addLoopEvent(enemyTank); + Thread.sleep(1000000); } } \ No newline at end of file From 46b7e809c775f1e98bf71d690285bde6b84c1f4a Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Fri, 17 Sep 2021 01:32:43 +0800 Subject: [PATCH 229/476] *) common pause mark --- .../github/kuangcp/jigsaw/ButtonListener.java | 4 +- .../com/github/kuangcp/jigsaw/MainFrame.java | 4 +- .../kuangcp/tank/constant/ButtonCommand.java | 17 ++++++ .../kuangcp/tank/constant/StageCommand.java | 9 --- .../github/kuangcp/tank/domain/Bullet.java | 6 +- .../github/kuangcp/tank/domain/EnemyTank.java | 7 ++- .../com/github/kuangcp/tank/domain/Hero.java | 4 +- .../com/github/kuangcp/tank/domain/Tank.java | 2 +- .../kuangcp/tank/panel/StageActionPanel.java | 36 ++++++------ .../kuangcp/tank/panel/TankGroundPanel.java | 14 +++-- .../kuangcp/tank/resource/ResourceMgr.java | 2 +- .../github/kuangcp/tank/util/ExecutePool.java | 8 +-- .../tank/util/LoopEventExecutePool.java | 57 ------------------- .../{ => executor}/AbstractLoopEvent.java | 2 +- .../util/executor/CommonEventExecutor.java | 27 +++++++++ .../tank/util/{ => executor}/LoopEvent.java | 2 +- .../tank/util/executor/LoopEventExecutor.java | 50 ++++++++++++++++ .../tank/util/executor/MonitorExecutor.java | 38 +++++++++++++ .../com/github/kuangcp/tank/v3/MainFrame.java | 25 ++++---- .../github/kuangcp/tank/v3/MainTankGame.java | 6 +- .../github/kuangcp/tank/v3/PlayStageMgr.java | 4 ++ ...olTest.java => LoopEventExecutorTest.java} | 8 ++- 22 files changed, 205 insertions(+), 127 deletions(-) create mode 100644 gui/src/main/java/com/github/kuangcp/tank/constant/ButtonCommand.java delete mode 100644 gui/src/main/java/com/github/kuangcp/tank/constant/StageCommand.java delete mode 100644 gui/src/main/java/com/github/kuangcp/tank/util/LoopEventExecutePool.java rename gui/src/main/java/com/github/kuangcp/tank/util/{ => executor}/AbstractLoopEvent.java (98%) create mode 100644 gui/src/main/java/com/github/kuangcp/tank/util/executor/CommonEventExecutor.java rename gui/src/main/java/com/github/kuangcp/tank/util/{ => executor}/LoopEvent.java (87%) create mode 100644 gui/src/main/java/com/github/kuangcp/tank/util/executor/LoopEventExecutor.java create mode 100644 gui/src/main/java/com/github/kuangcp/tank/util/executor/MonitorExecutor.java rename gui/src/test/java/com/github/kuangcp/tank/util/{LoopEventExecutePoolTest.java => LoopEventExecutorTest.java} (95%) diff --git a/gui/src/main/java/com/github/kuangcp/jigsaw/ButtonListener.java b/gui/src/main/java/com/github/kuangcp/jigsaw/ButtonListener.java index e451e019..129a1bb4 100644 --- a/gui/src/main/java/com/github/kuangcp/jigsaw/ButtonListener.java +++ b/gui/src/main/java/com/github/kuangcp/jigsaw/ButtonListener.java @@ -1,6 +1,6 @@ package com.github.kuangcp.jigsaw; -import com.github.kuangcp.tank.constant.StageCommand; +import com.github.kuangcp.tank.constant.ButtonCommand; import lombok.extern.slf4j.Slf4j; import java.awt.event.ActionEvent; @@ -16,7 +16,7 @@ public class ButtonListener implements ActionListener { public void actionPerformed(ActionEvent e) { log.debug("开始监听按钮的点击"); - if (e.getActionCommand().equals(StageCommand.START)) { + if (e.getActionCommand().equals(ButtonCommand.START)) { log.debug("开始游戏"); } } diff --git a/gui/src/main/java/com/github/kuangcp/jigsaw/MainFrame.java b/gui/src/main/java/com/github/kuangcp/jigsaw/MainFrame.java index 424f69ac..72e15e6e 100644 --- a/gui/src/main/java/com/github/kuangcp/jigsaw/MainFrame.java +++ b/gui/src/main/java/com/github/kuangcp/jigsaw/MainFrame.java @@ -1,6 +1,6 @@ package com.github.kuangcp.jigsaw; -import com.github.kuangcp.tank.constant.StageCommand; +import com.github.kuangcp.tank.constant.ButtonCommand; import javax.swing.*; import java.awt.*; @@ -21,7 +21,7 @@ private MainFrame() throws HeadlessException { ButtonListener button = new ButtonListener(); JButton startBtn = new JButton("开始游戏"); startBtn.addActionListener(button);//注册监听 - startBtn.setActionCommand(StageCommand.START);//指定特定的命令 + startBtn.setActionCommand(ButtonCommand.START);//指定特定的命令 MainPanel btnPanel = new MainPanel(); btnPanel.add(startBtn, BorderLayout.SOUTH); diff --git a/gui/src/main/java/com/github/kuangcp/tank/constant/ButtonCommand.java b/gui/src/main/java/com/github/kuangcp/tank/constant/ButtonCommand.java new file mode 100644 index 00000000..16b2956a --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/constant/ButtonCommand.java @@ -0,0 +1,17 @@ +package com.github.kuangcp.tank.constant; + +/** + * @author https://github.com/kuangcp on 2021-09-11 17:45 + */ +public interface ButtonCommand { + + // 流程按钮事件 + String START = "start"; + String EXIT = "exit"; + String ABORT = "abort"; + String PAUSE = "pause"; + String RESUME = "resume"; + + // 菜单 触发事件 + String SETTING_FRAME = "setting"; +} diff --git a/gui/src/main/java/com/github/kuangcp/tank/constant/StageCommand.java b/gui/src/main/java/com/github/kuangcp/tank/constant/StageCommand.java deleted file mode 100644 index eb189fed..00000000 --- a/gui/src/main/java/com/github/kuangcp/tank/constant/StageCommand.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.github.kuangcp.tank.constant; - -/** - * @author https://github.com/kuangcp on 2021-09-11 17:45 - */ -public interface StageCommand { - - String START = "start"; -} diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/Bullet.java b/gui/src/main/java/com/github/kuangcp/tank/domain/Bullet.java index b29001be..a2deba6c 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/domain/Bullet.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/Bullet.java @@ -1,7 +1,8 @@ package com.github.kuangcp.tank.domain; -import com.github.kuangcp.tank.util.AbstractLoopEvent; +import com.github.kuangcp.tank.util.executor.AbstractLoopEvent; import com.github.kuangcp.tank.util.TankTool; +import com.github.kuangcp.tank.v3.PlayStageMgr; import lombok.extern.slf4j.Slf4j; /** @@ -41,6 +42,9 @@ public void run() { } private void newRun() { + if (PlayStageMgr.pause) { + return; + } switch (direct) { //上下左右 case 0: diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java b/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java index 12b2aab5..5cf0eaee 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java @@ -3,7 +3,7 @@ import com.github.kuangcp.tank.constant.DirectType; -import com.github.kuangcp.tank.util.LoopEventExecutePool; +import com.github.kuangcp.tank.util.executor.LoopEventExecutor; import com.github.kuangcp.tank.util.TankTool; import com.github.kuangcp.tank.v3.PlayStageMgr; import lombok.extern.slf4j.Slf4j; @@ -154,7 +154,7 @@ public void shotEnemy() { break; } } - LoopEventExecutePool.addLoopEvent(s); + LoopEventExecutor.addLoopEvent(s); // 常规线程池 // ExecutePool.shotPool.execute(s); @@ -617,6 +617,9 @@ public void run2() { public void newEventRun() { + if (PlayStageMgr.pause) { + return; + } //判断坦克是否死亡 if (!this.isAlive()) { this.stop(); diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/Hero.java b/gui/src/main/java/com/github/kuangcp/tank/domain/Hero.java index c025a864..c90bbc2f 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/domain/Hero.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/Hero.java @@ -2,7 +2,7 @@ import com.github.kuangcp.tank.constant.DirectType; -import com.github.kuangcp.tank.util.LoopEventExecutePool; +import com.github.kuangcp.tank.util.executor.LoopEventExecutor; import com.github.kuangcp.tank.v3.PlayStageMgr; import lombok.extern.slf4j.Slf4j; @@ -100,7 +100,7 @@ public void shotEnemy() { } //启动子弹线程 // shotExecutePool.execute(bullet); - LoopEventExecutePool.addLoopEvent(bullet); + LoopEventExecutor.addLoopEvent(bullet); lastShotMs = nowMs; } diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/Tank.java b/gui/src/main/java/com/github/kuangcp/tank/domain/Tank.java index f5ccac81..b1bd19fc 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/domain/Tank.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/Tank.java @@ -1,7 +1,7 @@ package com.github.kuangcp.tank.domain; import com.github.kuangcp.tank.constant.DirectType; -import com.github.kuangcp.tank.util.AbstractLoopEvent; +import com.github.kuangcp.tank.util.executor.AbstractLoopEvent; import lombok.extern.slf4j.Slf4j; import java.awt.*; diff --git a/gui/src/main/java/com/github/kuangcp/tank/panel/StageActionPanel.java b/gui/src/main/java/com/github/kuangcp/tank/panel/StageActionPanel.java index 8746ca0e..4fcd9240 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/panel/StageActionPanel.java +++ b/gui/src/main/java/com/github/kuangcp/tank/panel/StageActionPanel.java @@ -2,7 +2,7 @@ package com.github.kuangcp.tank.panel; -import com.github.kuangcp.tank.constant.StageCommand; +import com.github.kuangcp.tank.constant.ButtonCommand; import com.github.kuangcp.tank.domain.Brick; import com.github.kuangcp.tank.domain.Bullet; import com.github.kuangcp.tank.domain.EnemyTank; @@ -43,42 +43,39 @@ public class StageActionPanel extends JPanel implements ActionListener { public void actionPerformed(ActionEvent ae) { //上面是不合理的,会有隐藏的bug在里面,这种做法要抛弃 - if (ae.getActionCommand().equals(StageCommand.START)) { + if (ae.getActionCommand().equals(ButtonCommand.START)) { this.startNewStage(); } - if (ae.getActionCommand().equals("结束")) { + if (ae.getActionCommand().equals(ButtonCommand.EXIT)) { System.out.println("结束"); System.exit(0); } - if (ae.getActionCommand().equals("暂停")) { + + if (ae.getActionCommand().equals(ButtonCommand.PAUSE)) { System.out.println("暂停"); - frame.groundPanel.hero.setSpeed(0); - Bullet.setSpeed(0); - for (EnemyTank et : ets) { - et.setSpeed(0); - } + PlayStageMgr.pause = true; frame.requestFocus(); } - if (ae.getActionCommand().equals("继续")) { + + if (ae.getActionCommand().equals(ButtonCommand.RESUME)) { System.out.println("继续"); - frame.groundPanel.hero.setSpeed(3); - Bullet.setSpeed(8); - for (EnemyTank et : ets) { - et.setSpeed(2); - } - frame.requestFocus();//把焦点还给JFrame + PlayStageMgr.pause = false; + frame.requestFocus(); } - if (ae.getActionCommand().equals("exit")) { + + if (ae.getActionCommand().equals(ButtonCommand.ABORT)) { //不能把下面的saveExit口令放到这里来或,会出现先后顺序的一些错误 //口令与口令之间应该独立,不要有或,与这样的复杂逻辑结构 System.out.println("退出游戏"); System.exit(0); } - if (ae.getActionCommand().equals("Help")) { + + if (ae.getActionCommand().equals(ButtonCommand.SETTING_FRAME)) { SettingFrame.activeFocus(hero); } + if (ae.getActionCommand().equals("saveExit")) { System.out.println("按下了 保存并退出 按钮"); Saved s = new Saved(ets, hero, bricks, irons, ETS, myself); @@ -88,9 +85,10 @@ public void actionPerformed(ActionEvent ae) { System.out.println("已经退出游戏"); System.exit(0); } + if (ae.getActionCommand().equals("Continue")) { //重新开启一个画板 - System.out.println(StageCommand.START); + System.out.println(ButtonCommand.START); frame.firstStart = false; TankGroundPanel.newStage = false; Bullet.setSpeed(8); diff --git a/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java b/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java index 47edea1e..0ac0cb3a 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java +++ b/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java @@ -13,7 +13,7 @@ import com.github.kuangcp.tank.util.ExecutePool; import com.github.kuangcp.tank.util.KeyListener; import com.github.kuangcp.tank.util.ListenEventGroup; -import com.github.kuangcp.tank.util.LoopEventExecutePool; +import com.github.kuangcp.tank.util.executor.LoopEventExecutor; import com.github.kuangcp.tank.util.TankTool; import com.github.kuangcp.tank.v3.PlayStageMgr; import lombok.extern.slf4j.Slf4j; @@ -75,7 +75,7 @@ public void startNewRound() { hero.setPrize(myself[3]); } - log.info("hero={}", hero); +// log.info("hero={}", hero); PlayStageMgr.init(hero, enemyList, bricks, irons); //多键监听实现 @@ -111,7 +111,7 @@ public void startNewRound() { if (Objects.isNull(ett)) { continue; } - LoopEventExecutePool.addLoopEvent(ett); + LoopEventExecutor.addLoopEvent(ett); // Thread t = new Thread(ett); // t.setName("enemyThread-" + ett.id); // t.start(); @@ -126,7 +126,7 @@ public void startNewRound() { ett = new EnemyTank(enemyTankMap[i][0], enemyTankMap[i][1], 2, i % 4); ett.SetInfo(hero, enemyList, bricks, irons); - LoopEventExecutePool.addLoopEvent(ett); + LoopEventExecutor.addLoopEvent(ett); // Thread t = new Thread(ett); // t.setName("enemyThreadF-" + ett.id); @@ -292,7 +292,7 @@ public void paint(Graphics g) { EnemyTank d = new EnemyTank(20 + (int) (Math.random() * 400), 20 + (int) (Math.random() * 300), 2, i % 4); d.SetInfo(hero, enemyList, bricks, irons); - LoopEventExecutePool.addLoopEvent(d); + LoopEventExecutor.addLoopEvent(d); // Thread fillThread = new Thread(d); // fillThread.setName("fillEnemy" + d.id); // fillThread.start(); @@ -352,6 +352,10 @@ public void paint(Graphics g) { */ @Override public void keyPressed(KeyEvent e) { + if (PlayStageMgr.pause) { + return; + } + //加了if(内层的)限制后 实现了墙的限制(如果是游戏中的道具,该怎么办) if (e.getKeyChar() == KeyEvent.VK_SPACE) { ListenEventGroup.instance.setShot(true); diff --git a/gui/src/main/java/com/github/kuangcp/tank/resource/ResourceMgr.java b/gui/src/main/java/com/github/kuangcp/tank/resource/ResourceMgr.java index a844d120..9061b7fa 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/resource/ResourceMgr.java +++ b/gui/src/main/java/com/github/kuangcp/tank/resource/ResourceMgr.java @@ -10,7 +10,7 @@ public class ResourceMgr { public static void loadResource() { - log.info("start load resource"); + log.info("[init] start load resource"); // image BombMgr.instance.loadImg(); DefeatImgMgr.instance.loadImg(); diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/ExecutePool.java b/gui/src/main/java/com/github/kuangcp/tank/util/ExecutePool.java index 9d198096..d4b9cb24 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/util/ExecutePool.java +++ b/gui/src/main/java/com/github/kuangcp/tank/util/ExecutePool.java @@ -11,6 +11,7 @@ */ public class ExecutePool { + // TODO replace by event pool public static final ExecutorService delayPool = ExecutePool.buildFixedPool("enemyDelay", 4); /** @@ -18,13 +19,6 @@ public class ExecutePool { */ // public static final ExecutorService shotPool = ExecutePool.buildFixedPool("enemyShot", 5); - public static final int EVENT_POOL_SIZE = 6; - - /** - * 循环事件线程池 - */ - public static final ExecutorService loopEventPool = ExecutePool.buildFixedPool("loopEvent", EVENT_POOL_SIZE); - // ForkJoin // public static final ForkJoinPool forkJoinPool = new ForkJoinPool(65); diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/LoopEventExecutePool.java b/gui/src/main/java/com/github/kuangcp/tank/util/LoopEventExecutePool.java deleted file mode 100644 index e9687234..00000000 --- a/gui/src/main/java/com/github/kuangcp/tank/util/LoopEventExecutePool.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.github.kuangcp.tank.util; - -import lombok.extern.slf4j.Slf4j; - -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.DelayQueue; - -/** - * @author https://github.com/kuangcp on 2021-09-16 01:20 - */ -@Slf4j -public class LoopEventExecutePool { - - private static final BlockingQueue queue = new DelayQueue<>(); - - public static void init() { - final Runnable mainLoop = () -> { - while (true) { - try { - final AbstractLoopEvent event = queue.take(); -// log.info("event {}", event); - event.run(); - if (event.isContinue() && event.addFixedDelay()) { - queue.add(event); - } - } catch (InterruptedException e) { - log.error("", e); - break; - } - } - }; - - for (int i = 0; i < ExecutePool.EVENT_POOL_SIZE; i++) { - ExecutePool.loopEventPool.execute(mainLoop); - } - - final Thread thread = new Thread(() -> { - while (true) { - try { - Thread.sleep(10_000); - log.info("eventSize: {}", queue.size()); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - }); - thread.setName("eventPoolMonitor"); - thread.start(); - } - - public static void addLoopEvent(AbstractLoopEvent loopEvent) { -// log.info("add: loopEvent={}", loopEvent); - queue.add(loopEvent); - } - -} - diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/AbstractLoopEvent.java b/gui/src/main/java/com/github/kuangcp/tank/util/executor/AbstractLoopEvent.java similarity index 98% rename from gui/src/main/java/com/github/kuangcp/tank/util/AbstractLoopEvent.java rename to gui/src/main/java/com/github/kuangcp/tank/util/executor/AbstractLoopEvent.java index 02524a49..8ac68269 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/util/AbstractLoopEvent.java +++ b/gui/src/main/java/com/github/kuangcp/tank/util/executor/AbstractLoopEvent.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.tank.util; +package com.github.kuangcp.tank.util.executor; import lombok.extern.slf4j.Slf4j; diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/executor/CommonEventExecutor.java b/gui/src/main/java/com/github/kuangcp/tank/util/executor/CommonEventExecutor.java new file mode 100644 index 00000000..507a9f64 --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/util/executor/CommonEventExecutor.java @@ -0,0 +1,27 @@ +package com.github.kuangcp.tank.util.executor; + +import lombok.extern.slf4j.Slf4j; + +import java.util.concurrent.BlockingQueue; + +/** + * @author https://github.com/kuangcp on 2021-09-17 01:06 + */ +@Slf4j +public class CommonEventExecutor { + + public static void simpleMainLoop(BlockingQueue queue) { + while (true) { + try { + final AbstractLoopEvent event = queue.take(); + event.run(); + if (event.isContinue() && event.addFixedDelay()) { + queue.add(event); + } + } catch (InterruptedException e) { + log.error("", e); + break; + } + } + } +} diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/LoopEvent.java b/gui/src/main/java/com/github/kuangcp/tank/util/executor/LoopEvent.java similarity index 87% rename from gui/src/main/java/com/github/kuangcp/tank/util/LoopEvent.java rename to gui/src/main/java/com/github/kuangcp/tank/util/executor/LoopEvent.java index a2befff6..28dc9709 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/util/LoopEvent.java +++ b/gui/src/main/java/com/github/kuangcp/tank/util/executor/LoopEvent.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.tank.util; +package com.github.kuangcp.tank.util.executor; import java.util.concurrent.Delayed; diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/executor/LoopEventExecutor.java b/gui/src/main/java/com/github/kuangcp/tank/util/executor/LoopEventExecutor.java new file mode 100644 index 00000000..fb20f3c0 --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/util/executor/LoopEventExecutor.java @@ -0,0 +1,50 @@ +package com.github.kuangcp.tank.util.executor; + +import com.github.kuangcp.tank.util.ExecutePool; +import lombok.extern.slf4j.Slf4j; + +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.DelayQueue; +import java.util.concurrent.ExecutorService; + +/** + * @author https://github.com/kuangcp on 2021-09-16 01:20 + */ +@Slf4j +public class LoopEventExecutor { + + static final BlockingQueue queue = new DelayQueue<>(); + + private static final int EVENT_POOL_SIZE = 6; + + /** + * 循环事件线程池 + */ + private static final ExecutorService loopEventPool = ExecutePool.buildFixedPool("loopEvent", EVENT_POOL_SIZE); + + public static void init() { + for (int i = 0; i < EVENT_POOL_SIZE; i++) { + loopEventPool.execute(() -> CommonEventExecutor.simpleMainLoop(queue)); + } + +// final Thread thread = new Thread(() -> { +// while (true) { +// try { +// Thread.sleep(10_000); +// log.info("eventSize: {}", queue.size()); +// } catch (InterruptedException e) { +// e.printStackTrace(); +// } +// } +// }); +// thread.setName("eventPoolMonitor"); +// thread.start(); + } + + public static void addLoopEvent(AbstractLoopEvent loopEvent) { +// log.info("add: loopEvent={}", loopEvent); + queue.add(loopEvent); + } + +} + diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/executor/MonitorExecutor.java b/gui/src/main/java/com/github/kuangcp/tank/util/executor/MonitorExecutor.java new file mode 100644 index 00000000..646a7a04 --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/util/executor/MonitorExecutor.java @@ -0,0 +1,38 @@ +package com.github.kuangcp.tank.util.executor; + +import com.github.kuangcp.tank.util.ExecutePool; +import lombok.extern.slf4j.Slf4j; + +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.DelayQueue; +import java.util.concurrent.ExecutorService; + +/** + * @author https://github.com/kuangcp on 2021-09-17 00:58 + */ +@Slf4j +public class MonitorExecutor { + + private static final BlockingQueue queue = new DelayQueue<>(); + /** + * 循环事件线程池 + */ + private static final ExecutorService monitorEventPool = ExecutePool.buildFixedPool("monitorEvent", 1); + + public static void init() { + registerEventMonitor(); + + monitorEventPool.execute(() -> CommonEventExecutor.simpleMainLoop(queue)); + } + + private static void registerEventMonitor() { + final AbstractLoopEvent loopEventMonitor = new AbstractLoopEvent() { + @Override + public void run() { + log.info("loopEvent:{} monitor:{}", LoopEventExecutor.queue.size(), queue.size()); + } + }; + loopEventMonitor.setFixedDelayTime(5_000); + queue.add(loopEventMonitor); + } +} diff --git a/gui/src/main/java/com/github/kuangcp/tank/v3/MainFrame.java b/gui/src/main/java/com/github/kuangcp/tank/v3/MainFrame.java index 8c8f8875..775bd588 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v3/MainFrame.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v3/MainFrame.java @@ -1,6 +1,6 @@ package com.github.kuangcp.tank.v3; -import com.github.kuangcp.tank.constant.StageCommand; +import com.github.kuangcp.tank.constant.ButtonCommand; import com.github.kuangcp.tank.panel.HeroInfoPanel; import com.github.kuangcp.tank.panel.StageActionPanel; import com.github.kuangcp.tank.panel.StarterPanel; @@ -61,7 +61,7 @@ public class MainFrame extends JFrame implements Runnable { JMenuItem jmi3 = null; JMenuItem jmi4 = null; //帮助窗口 - JMenuItem Help = null; + JMenuItem setting = null; public MainFrame() { ResourceMgr.loadResource(); @@ -109,11 +109,11 @@ public void run() { jmi2 = new JMenuItem("Pause"); jmi3 = new JMenuItem("Save & Exit(C)"); jmi4 = new JMenuItem("ContinueLast(S)"); - Help = new JMenuItem("Setting"); + setting = new JMenuItem("Setting"); // - Help.addActionListener(actionPanel); - Help.setActionCommand("Help"); + setting.addActionListener(actionPanel); + setting.setActionCommand(ButtonCommand.SETTING_FRAME); //注册监听 jmi4.addActionListener(actionPanel); jmi4.setActionCommand("Continue"); @@ -126,32 +126,33 @@ public void run() { jmi2.setMnemonic('E'); //对jmil相应 jmil.addActionListener(actionPanel); - jmil.setActionCommand(StageCommand.START); + jmil.setActionCommand(ButtonCommand.START); jm1.add(jmil); // jm1.add(jmi2); jm1.add(jmi3); jm1.add(jmi4); - jm2.add(Help); + jm2.add(setting); jmb.add(jm1); jmb.add(jm2); jb3 = new JButton("暂停游戏"); jb3.addActionListener(actionPanel); //注册监听 - jb3.setActionCommand("暂停"); + jb3.setActionCommand(ButtonCommand.PAUSE); + jb4 = new JButton("继续游戏"); jb4.addActionListener(actionPanel); //注册监听 - jb4.setActionCommand("继续"); + jb4.setActionCommand(ButtonCommand.RESUME); jb2 = new JButton("退出游戏"); jb2.addActionListener(actionPanel); //注册监听 - jb2.setActionCommand("结束"); + jb2.setActionCommand(ButtonCommand.EXIT); startBtn = new JButton(firstStart ? "游戏开始" : "重新开始"); startBtn.addActionListener(actionPanel); - startBtn.setActionCommand(StageCommand.START); + startBtn.setActionCommand(ButtonCommand.START); actionPanel.add(startBtn); actionPanel.add(jb2); @@ -200,6 +201,6 @@ public void run() { this.setVisible(true); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); final long now = System.currentTimeMillis(); - log.info("total:{} visible:{}", (now - start), (now - beforeVisible)); + log.info("[init] total:{} visible:{}", (now - start), (now - beforeVisible)); } } \ No newline at end of file diff --git a/gui/src/main/java/com/github/kuangcp/tank/v3/MainTankGame.java b/gui/src/main/java/com/github/kuangcp/tank/v3/MainTankGame.java index 52078845..56376338 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v3/MainTankGame.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v3/MainTankGame.java @@ -1,6 +1,7 @@ package com.github.kuangcp.tank.v3; -import com.github.kuangcp.tank.util.LoopEventExecutePool; +import com.github.kuangcp.tank.util.executor.LoopEventExecutor; +import com.github.kuangcp.tank.util.executor.MonitorExecutor; import lombok.extern.slf4j.Slf4j; import java.awt.*; @@ -14,7 +15,8 @@ public class MainTankGame { * https://stackoverflow.com/questions/6736906/why-does-java-swings-setvisible-take-so-long */ public static void main(String[] args) { - LoopEventExecutePool.init(); + MonitorExecutor.init(); + LoopEventExecutor.init(); EventQueue.invokeLater(new MainFrame()); } diff --git a/gui/src/main/java/com/github/kuangcp/tank/v3/PlayStageMgr.java b/gui/src/main/java/com/github/kuangcp/tank/v3/PlayStageMgr.java index 2b2dacb2..35c53677 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v3/PlayStageMgr.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v3/PlayStageMgr.java @@ -28,6 +28,10 @@ public class PlayStageMgr { public int roundPrize = 0; static int round = 0; + /** + * 全局游戏暂停 + */ + public static volatile boolean pause = false; // 游戏参数 配置 /** diff --git a/gui/src/test/java/com/github/kuangcp/tank/util/LoopEventExecutePoolTest.java b/gui/src/test/java/com/github/kuangcp/tank/util/LoopEventExecutorTest.java similarity index 95% rename from gui/src/test/java/com/github/kuangcp/tank/util/LoopEventExecutePoolTest.java rename to gui/src/test/java/com/github/kuangcp/tank/util/LoopEventExecutorTest.java index 6bf1b8be..6c02d99b 100644 --- a/gui/src/test/java/com/github/kuangcp/tank/util/LoopEventExecutePoolTest.java +++ b/gui/src/test/java/com/github/kuangcp/tank/util/LoopEventExecutorTest.java @@ -3,6 +3,8 @@ import com.github.kuangcp.tank.constant.DirectType; import com.github.kuangcp.tank.domain.EnemyTank; import com.github.kuangcp.tank.domain.Hero; +import com.github.kuangcp.tank.util.executor.AbstractLoopEvent; +import com.github.kuangcp.tank.util.executor.LoopEventExecutor; import com.github.kuangcp.tank.v3.PlayStageMgr; import lombok.extern.slf4j.Slf4j; import org.junit.Test; @@ -20,7 +22,7 @@ * @author https://github.com/kuangcp on 2021-09-16 01:28 */ @Slf4j -public class LoopEventExecutePoolTest { +public class LoopEventExecutorTest { static class SampleTask implements Delayed { String id; @@ -157,9 +159,9 @@ public void testShotBullet() throws Exception { @Test public void testEnemyTank() throws Exception { final EnemyTank enemyTank = new EnemyTank(30, 30, 2, DirectType.RIGHT); - LoopEventExecutePool.init(); + LoopEventExecutor.init(); PlayStageMgr.init(new Hero(240, 50, 1), Collections.emptyList(), Collections.emptyList(), Collections.emptyList()); - LoopEventExecutePool.addLoopEvent(enemyTank); + LoopEventExecutor.addLoopEvent(enemyTank); Thread.sleep(1000000); } From a8455067532c7a3b097c385614ae3407a220020c Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Fri, 17 Sep 2021 01:53:31 +0800 Subject: [PATCH 230/476] *) remove delay thread pool --- .../kuangcp/tank/panel/TankGroundPanel.java | 26 ++++++++++------ .../github/kuangcp/tank/util/ExecutePool.java | 12 +++----- .../util/executor/AbstractDelayEvent.java | 30 +++++++++++++++++++ .../tank/util/executor/AbstractLoopEvent.java | 1 - .../util/executor/CommonEventExecutor.java | 16 +++++++++- .../tank/util/executor/DelayEvent.java | 9 ++++++ .../tank/util/executor/DelayExecutor.java | 28 +++++++++++++++++ .../tank/util/executor/LoopEventExecutor.java | 15 +--------- .../tank/util/executor/MonitorExecutor.java | 11 ++++--- .../github/kuangcp/tank/v3/MainTankGame.java | 2 -- 10 files changed, 111 insertions(+), 39 deletions(-) create mode 100644 gui/src/main/java/com/github/kuangcp/tank/util/executor/AbstractDelayEvent.java create mode 100644 gui/src/main/java/com/github/kuangcp/tank/util/executor/DelayEvent.java create mode 100644 gui/src/main/java/com/github/kuangcp/tank/util/executor/DelayExecutor.java diff --git a/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java b/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java index 0ac0cb3a..15b15c0c 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java +++ b/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java @@ -3,18 +3,19 @@ import com.github.kuangcp.tank.domain.Brick; +import com.github.kuangcp.tank.domain.Bullet; import com.github.kuangcp.tank.domain.EnemyTank; import com.github.kuangcp.tank.domain.Hero; import com.github.kuangcp.tank.domain.Iron; -import com.github.kuangcp.tank.domain.Bullet; import com.github.kuangcp.tank.mgr.BombMgr; import com.github.kuangcp.tank.resource.AvatarImgMgr; import com.github.kuangcp.tank.thread.ExitFlagRunnable; -import com.github.kuangcp.tank.util.ExecutePool; import com.github.kuangcp.tank.util.KeyListener; import com.github.kuangcp.tank.util.ListenEventGroup; -import com.github.kuangcp.tank.util.executor.LoopEventExecutor; import com.github.kuangcp.tank.util.TankTool; +import com.github.kuangcp.tank.util.executor.AbstractDelayEvent; +import com.github.kuangcp.tank.util.executor.DelayExecutor; +import com.github.kuangcp.tank.util.executor.LoopEventExecutor; import com.github.kuangcp.tank.v3.PlayStageMgr; import lombok.extern.slf4j.Slf4j; @@ -329,13 +330,20 @@ public void paint(Graphics g) { continue; } demon.delayRemove = true; - ExecutePool.delayPool.execute(() -> { - try { - TimeUnit.SECONDS.sleep(8); - } catch (InterruptedException e) { - log.error("", e); +// ExecutePool.delayPool.execute(() -> { +// try { +// TimeUnit.SECONDS.sleep(8); +// } catch (InterruptedException e) { +// log.error("", e); +// } +// enemyList.remove(demon); +// }); + + DelayExecutor.addEvent(new AbstractDelayEvent(8_000) { + @Override + public void run() { + enemyList.remove(demon); } - enemyList.remove(demon); }); } } diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/ExecutePool.java b/gui/src/main/java/com/github/kuangcp/tank/util/ExecutePool.java index d4b9cb24..5f1b27d7 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/util/ExecutePool.java +++ b/gui/src/main/java/com/github/kuangcp/tank/util/ExecutePool.java @@ -11,18 +11,14 @@ */ public class ExecutePool { - // TODO replace by event pool - public static final ExecutorService delayPool = ExecutePool.buildFixedPool("enemyDelay", 4); - - /** - * 敌人全部的子弹线程 - */ + // 敌人全部的子弹线程实现并发,目前选用事件延迟队列。原有方案: + // 1. 线程池 // public static final ExecutorService shotPool = ExecutePool.buildFixedPool("enemyShot", 5); - // ForkJoin + // 2. ForkJoin // public static final ForkJoinPool forkJoinPool = new ForkJoinPool(65); - // http://docs.paralleluniverse.co/quasar/ 协程池 + // 3. 协程池 http://docs.paralleluniverse.co/quasar/ // 在当前场景下,没有优势,底层一样使用 类似JDK的ForkJoinPool实现, 对比50敌人150子弹情况下一样启动了63个真实线程,和上述没区别 // public static FiberForkJoinScheduler shotScheduler = new FiberForkJoinScheduler("enemyShot", 20, null, false); diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/executor/AbstractDelayEvent.java b/gui/src/main/java/com/github/kuangcp/tank/util/executor/AbstractDelayEvent.java new file mode 100644 index 00000000..d3d64d93 --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/util/executor/AbstractDelayEvent.java @@ -0,0 +1,30 @@ +package com.github.kuangcp.tank.util.executor; + +import java.util.concurrent.Delayed; +import java.util.concurrent.TimeUnit; + +/** + * @author https://github.com/kuangcp on 2021-09-17 01:39 + */ +public abstract class AbstractDelayEvent implements DelayEvent { + + /** + * 下次事件触发时机 + */ + long delayTime; + + public AbstractDelayEvent(long delayTime) { + this.delayTime = delayTime; + } + + + @Override + public int compareTo(Delayed o) { + return Long.compare(this.getDelay(TimeUnit.MILLISECONDS), o.getDelay(TimeUnit.MILLISECONDS)); + } + + @Override + public long getDelay(TimeUnit unit) { + return unit.convert(delayTime - System.currentTimeMillis(), TimeUnit.MILLISECONDS); + } +} diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/executor/AbstractLoopEvent.java b/gui/src/main/java/com/github/kuangcp/tank/util/executor/AbstractLoopEvent.java index 8ac68269..3c466d3d 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/util/executor/AbstractLoopEvent.java +++ b/gui/src/main/java/com/github/kuangcp/tank/util/executor/AbstractLoopEvent.java @@ -9,7 +9,6 @@ /** * @author https://github.com/kuangcp on 2021-09-16 01:44 - * 均以 ms 驱动 */ @Slf4j public abstract class AbstractLoopEvent implements LoopEvent { diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/executor/CommonEventExecutor.java b/gui/src/main/java/com/github/kuangcp/tank/util/executor/CommonEventExecutor.java index 507a9f64..82c22e48 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/util/executor/CommonEventExecutor.java +++ b/gui/src/main/java/com/github/kuangcp/tank/util/executor/CommonEventExecutor.java @@ -5,12 +5,14 @@ import java.util.concurrent.BlockingQueue; /** + * TODO 消费任务时,延迟情况告警 + * * @author https://github.com/kuangcp on 2021-09-17 01:06 */ @Slf4j public class CommonEventExecutor { - public static void simpleMainLoop(BlockingQueue queue) { + public static void loopEventSpin(BlockingQueue queue) { while (true) { try { final AbstractLoopEvent event = queue.take(); @@ -24,4 +26,16 @@ public static void simpleMainLoop(BlockingQueue queue) { } } } + + public static void delayEventSpin(BlockingQueue queue) { + while (true) { + try { + final AbstractDelayEvent event = queue.take(); + event.run(); + } catch (InterruptedException e) { + log.error("", e); + break; + } + } + } } diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/executor/DelayEvent.java b/gui/src/main/java/com/github/kuangcp/tank/util/executor/DelayEvent.java new file mode 100644 index 00000000..eda7a48b --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/util/executor/DelayEvent.java @@ -0,0 +1,9 @@ +package com.github.kuangcp.tank.util.executor; + +import java.util.concurrent.Delayed; + +/** + * @author https://github.com/kuangcp on 2021-09-17 01:35 + */ +public interface DelayEvent extends Runnable, Delayed { +} diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/executor/DelayExecutor.java b/gui/src/main/java/com/github/kuangcp/tank/util/executor/DelayExecutor.java new file mode 100644 index 00000000..1aee74ab --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/util/executor/DelayExecutor.java @@ -0,0 +1,28 @@ +package com.github.kuangcp.tank.util.executor; + +import com.github.kuangcp.tank.util.ExecutePool; + +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.DelayQueue; +import java.util.concurrent.ExecutorService; + +/** + * @author https://github.com/kuangcp on 2021-09-17 01:35 + */ +public class DelayExecutor { + + private static final BlockingQueue queue = new DelayQueue<>(); + + /** + * 循环事件线程池 + */ + private static final ExecutorService delayEventPool = ExecutePool.buildFixedPool("delayEvent", 4); + + public static void init() { + delayEventPool.execute(() -> CommonEventExecutor.delayEventSpin(queue)); + } + + public static void addEvent(AbstractDelayEvent event) { + queue.add(event); + } +} diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/executor/LoopEventExecutor.java b/gui/src/main/java/com/github/kuangcp/tank/util/executor/LoopEventExecutor.java index fb20f3c0..9c22d0ca 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/util/executor/LoopEventExecutor.java +++ b/gui/src/main/java/com/github/kuangcp/tank/util/executor/LoopEventExecutor.java @@ -24,21 +24,8 @@ public class LoopEventExecutor { public static void init() { for (int i = 0; i < EVENT_POOL_SIZE; i++) { - loopEventPool.execute(() -> CommonEventExecutor.simpleMainLoop(queue)); + loopEventPool.execute(() -> CommonEventExecutor.loopEventSpin(queue)); } - -// final Thread thread = new Thread(() -> { -// while (true) { -// try { -// Thread.sleep(10_000); -// log.info("eventSize: {}", queue.size()); -// } catch (InterruptedException e) { -// e.printStackTrace(); -// } -// } -// }); -// thread.setName("eventPoolMonitor"); -// thread.start(); } public static void addLoopEvent(AbstractLoopEvent loopEvent) { diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/executor/MonitorExecutor.java b/gui/src/main/java/com/github/kuangcp/tank/util/executor/MonitorExecutor.java index 646a7a04..311a4eda 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/util/executor/MonitorExecutor.java +++ b/gui/src/main/java/com/github/kuangcp/tank/util/executor/MonitorExecutor.java @@ -14,18 +14,21 @@ public class MonitorExecutor { private static final BlockingQueue queue = new DelayQueue<>(); - /** - * 循环事件线程池 - */ private static final ExecutorService monitorEventPool = ExecutePool.buildFixedPool("monitorEvent", 1); public static void init() { + // 1. 初始化其他 执行器 + DelayExecutor.init(); + LoopEventExecutor.init(); + + // 2. 注册管理任务 registerEventMonitor(); - monitorEventPool.execute(() -> CommonEventExecutor.simpleMainLoop(queue)); + monitorEventPool.execute(() -> CommonEventExecutor.loopEventSpin(queue)); } private static void registerEventMonitor() { + // TODO 更新到主背景图上 final AbstractLoopEvent loopEventMonitor = new AbstractLoopEvent() { @Override public void run() { diff --git a/gui/src/main/java/com/github/kuangcp/tank/v3/MainTankGame.java b/gui/src/main/java/com/github/kuangcp/tank/v3/MainTankGame.java index 56376338..cc1dc561 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v3/MainTankGame.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v3/MainTankGame.java @@ -1,6 +1,5 @@ package com.github.kuangcp.tank.v3; -import com.github.kuangcp.tank.util.executor.LoopEventExecutor; import com.github.kuangcp.tank.util.executor.MonitorExecutor; import lombok.extern.slf4j.Slf4j; @@ -16,7 +15,6 @@ public class MainTankGame { */ public static void main(String[] args) { MonitorExecutor.init(); - LoopEventExecutor.init(); EventQueue.invokeLater(new MainFrame()); } From 45b9986b2071ec27dcf097df14509aed14a40dd4 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Fri, 17 Sep 2021 02:31:57 +0800 Subject: [PATCH 231/476] *) remove info panel thread --- .../kuangcp/tank/panel/HeroInfoPanel.java | 74 +++++++++---------- .../kuangcp/tank/panel/TankGroundPanel.java | 6 +- .../event/HeroInfoPanelRefreshEvent.java | 27 +++++++ .../util/executor/CommonEventExecutor.java | 5 ++ .../tank/util/executor/MonitorExecutor.java | 4 + .../com/github/kuangcp/tank/v3/MainFrame.java | 39 ++++++---- 6 files changed, 99 insertions(+), 56 deletions(-) create mode 100644 gui/src/main/java/com/github/kuangcp/tank/panel/event/HeroInfoPanelRefreshEvent.java diff --git a/gui/src/main/java/com/github/kuangcp/tank/panel/HeroInfoPanel.java b/gui/src/main/java/com/github/kuangcp/tank/panel/HeroInfoPanel.java index 706eeeb9..226cc6f4 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/panel/HeroInfoPanel.java +++ b/gui/src/main/java/com/github/kuangcp/tank/panel/HeroInfoPanel.java @@ -3,14 +3,14 @@ import com.github.kuangcp.tank.domain.EnemyTank; import com.github.kuangcp.tank.domain.Tank; -import com.github.kuangcp.tank.thread.ExitFlagRunnable; -import com.github.kuangcp.tank.util.TankTool; +import com.github.kuangcp.tank.panel.event.HeroInfoPanelRefreshEvent; import com.github.kuangcp.tank.v3.PlayStageMgr; import lombok.extern.slf4j.Slf4j; import javax.swing.*; import java.awt.*; import java.util.List; +import java.util.Objects; /** * 就是一个用来显示属性的画板 @@ -19,13 +19,14 @@ */ @Slf4j @SuppressWarnings("serial") -public class HeroInfoPanel extends JPanel implements ExitFlagRunnable { +public class HeroInfoPanel extends JPanel { - JLabel jl1; + JLabel tankCounter; JLabel prizeNo; List ets; + HeroInfoPanelRefreshEvent refreshEvent; - final Tank heroIcon = new Tank(20, 20, 0) { + static final Tank heroIcon = new Tank(20, 20, 0) { @Override public void drawSelf(Graphics g) { g.setColor(Color.yellow); @@ -37,7 +38,7 @@ public void run() { } }; - final Tank enemyIcon = new Tank(100, 20, 0) { + static final Tank enemyIcon = new Tank(100, 20, 0) { @Override public void drawSelf(Graphics g) { g.setColor(Color.cyan); @@ -50,14 +51,18 @@ public void run() { } }; - private volatile boolean exit = false; - public void exit() { - this.exit = true; + if (Objects.nonNull(refreshEvent)) { + this.refreshEvent.stop(); + } + } + + public void setEts(List ets) { + this.ets = ets; } - public HeroInfoPanel(JLabel jl1, List ets, JLabel prizeNo) { - this.jl1 = jl1; + public HeroInfoPanel(JLabel tankCounter, List ets, JLabel prizeNo) { + this.tankCounter = tankCounter; this.ets = ets; this.prizeNo = prizeNo; // jl1 =new JLabel("生命值: "+Hero.Life/3); @@ -72,37 +77,24 @@ public void paint(Graphics g) { enemyIcon.drawSelf(g); } - public void run() { - while (!exit) { - TankTool.yieldMsTime(500); - if (PlayStageMgr.waitStart()) { - continue; - } - /* - * 如果是下面的方法的话,来不及设置成0 就退出线程了,所以把设置0放在了退出线程那里更好 - */ -// if(!hero.getisLive()){ -// jl1.setText("生命值: 0"); -// }else { -// jl1.setText("生命值: "+hero.getLife()); -// } - - if (PlayStageMgr.instance.hero.isAlive()) {//生命值的自动更改 - jl1.setText(" :" + PlayStageMgr.instance.hero.getLife() + " : " + ets.size()); - } else { - jl1.setText(" :0" + " : " + ets.size()); - } - prizeNo.setText("战绩:" + PlayStageMgr.instance.hero.getPrize()); -// TankGame3.mp3.add(TankGame3.jl1 ); -// System.out.println("画板执行了"); - repaint(); - - //不加的话线程没有退出 -// if(hero.getLife() <= 0){ -// jl1.setText(" :0"+" : "+ets.size()); -// break;//退出线程 -// } + public void refreshData() { + if (PlayStageMgr.waitStart() || PlayStageMgr.pause) { + return; } + + final String format = " :%d" + " : %d"; + final String txt; + if (PlayStageMgr.instance.hero.isAlive()) { + txt = String.format(format, PlayStageMgr.instance.hero.getLife(), ets.size()); + } else { + txt = String.format(format, 0, ets.size()); + } + tankCounter.setText(txt); + + prizeNo.setText("战绩:" + PlayStageMgr.instance.hero.getPrize()); } + public void setRefreshEvent(HeroInfoPanelRefreshEvent refreshEvent) { + this.refreshEvent = refreshEvent; + } } diff --git a/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java b/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java index 15b15c0c..96167182 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java +++ b/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java @@ -82,7 +82,7 @@ public void startNewRound() { //多键监听实现 keyListener = new KeyListener(ListenEventGroup.instance, hero, this); Thread p = new Thread(keyListener); - p.setName("keyEventGroup"); + p.setName("playerKeyEventListener"); p.start(); // 创建 敌人的坦克 @@ -452,6 +452,8 @@ public void run() { } // clean listener - keyListener.exit(); + if (Objects.nonNull(keyListener)) { + keyListener.exit(); + } } } \ No newline at end of file diff --git a/gui/src/main/java/com/github/kuangcp/tank/panel/event/HeroInfoPanelRefreshEvent.java b/gui/src/main/java/com/github/kuangcp/tank/panel/event/HeroInfoPanelRefreshEvent.java new file mode 100644 index 00000000..58e05a4d --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/panel/event/HeroInfoPanelRefreshEvent.java @@ -0,0 +1,27 @@ +package com.github.kuangcp.tank.panel.event; + +import com.github.kuangcp.tank.panel.HeroInfoPanel; +import com.github.kuangcp.tank.util.executor.AbstractLoopEvent; +import lombok.extern.slf4j.Slf4j; + +/** + * @author https://github.com/kuangcp on 2021-09-17 01:58 + */ +@Slf4j +public class HeroInfoPanelRefreshEvent extends AbstractLoopEvent { + + public static final long fixedDelayTime = 600; + + private final HeroInfoPanel heroInfoPanel; + + public HeroInfoPanelRefreshEvent(HeroInfoPanel heroInfoPanel) { + this.heroInfoPanel = heroInfoPanel; + + this.setFixedDelayTime(fixedDelayTime); + } + + @Override + public void run() { + heroInfoPanel.refreshData(); + } +} diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/executor/CommonEventExecutor.java b/gui/src/main/java/com/github/kuangcp/tank/util/executor/CommonEventExecutor.java index 82c22e48..9c29ca9a 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/util/executor/CommonEventExecutor.java +++ b/gui/src/main/java/com/github/kuangcp/tank/util/executor/CommonEventExecutor.java @@ -3,6 +3,7 @@ import lombok.extern.slf4j.Slf4j; import java.util.concurrent.BlockingQueue; +import java.util.concurrent.TimeUnit; /** * TODO 消费任务时,延迟情况告警 @@ -16,6 +17,10 @@ public static void loopEventSpin(BlockingQueue queue) { while (true) { try { final AbstractLoopEvent event = queue.take(); + final long delay = event.getDelay(TimeUnit.MILLISECONDS); + if (delay < -200) { + log.info("delay {}", delay); + } event.run(); if (event.isContinue() && event.addFixedDelay()) { queue.add(event); diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/executor/MonitorExecutor.java b/gui/src/main/java/com/github/kuangcp/tank/util/executor/MonitorExecutor.java index 311a4eda..8fe1ecaf 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/util/executor/MonitorExecutor.java +++ b/gui/src/main/java/com/github/kuangcp/tank/util/executor/MonitorExecutor.java @@ -38,4 +38,8 @@ public void run() { loopEventMonitor.setFixedDelayTime(5_000); queue.add(loopEventMonitor); } + + public static void addLoopEvent(AbstractLoopEvent loopEvent) { + queue.add(loopEvent); + } } diff --git a/gui/src/main/java/com/github/kuangcp/tank/v3/MainFrame.java b/gui/src/main/java/com/github/kuangcp/tank/v3/MainFrame.java index 775bd588..801b733c 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v3/MainFrame.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v3/MainFrame.java @@ -5,8 +5,10 @@ import com.github.kuangcp.tank.panel.StageActionPanel; import com.github.kuangcp.tank.panel.StarterPanel; import com.github.kuangcp.tank.panel.TankGroundPanel; +import com.github.kuangcp.tank.panel.event.HeroInfoPanelRefreshEvent; import com.github.kuangcp.tank.resource.AvatarImgMgr; import com.github.kuangcp.tank.resource.ResourceMgr; +import com.github.kuangcp.tank.util.executor.MonitorExecutor; import lombok.extern.slf4j.Slf4j; import javax.swing.*; @@ -42,11 +44,15 @@ public class MainFrame extends JFrame implements Runnable { public volatile TankGroundPanel groundPanel = null;//坦克的主画板 public StageActionPanel actionPanel = null;//放按钮的画板 + public HeroInfoPanel heroInfoPanel = null; //显示属性的画板 + private HeroInfoPanelRefreshEvent loopEvent = null; + + public StarterPanel starterPanel; public JButton startBtn = null, jb2 = null, jb3, jb4; //按钮 public JSplitPane centerPanel, rightAreaPanel;//拆分窗格 - public JLabel jl1 = null, jl2 = null, jl3 = null, me = null, prizeNo = null; + public JLabel tankCounter = null, jl2 = null, jl3 = null, me = null, prizeNo = null; public boolean firstStart = true; //判断是否首次运行, 通过开始按钮触发 //作出我需要的菜单 @@ -72,29 +78,36 @@ public void run() { if (Objects.nonNull(groundPanel)) { groundPanel.exit(); + } else { + groundPanel = new TankGroundPanel(); } + if (Objects.nonNull(heroInfoPanel)) { - heroInfoPanel.exit(); + loopEvent.stop(); + heroInfoPanel.setEts(groundPanel.enemyList); + } else { + //提示信息 + tankCounter = new JLabel(" : : " + groundPanel.enemyList.size()); + prizeNo = new JLabel("已击杀 :");//战绩的标签 + me = new JLabel("Myth"); + + heroInfoPanel = new HeroInfoPanel(tankCounter, groundPanel.enemyList, prizeNo); } - groundPanel = new TankGroundPanel(); actionPanel = new StageActionPanel(this, groundPanel.hero, groundPanel.enemyList, groundPanel.bricks, groundPanel.irons, TankGroundPanel.enemyTankMap, TankGroundPanel.myself);//放按钮的画布 - //提示信息 - jl1 = new JLabel(" : : " + groundPanel.enemyList.size()); - prizeNo = new JLabel("已击杀 :");//战绩的标签 - me = new JLabel("Myth"); - - heroInfoPanel = new HeroInfoPanel(jl1, groundPanel.enemyList, prizeNo);//显示一些属性 + loopEvent = new HeroInfoPanelRefreshEvent(heroInfoPanel); + heroInfoPanel.setRefreshEvent(loopEvent); + MonitorExecutor.addLoopEvent(loopEvent); if (!firstStart) { //已经成为一个线程 要启动它 Thread t = new Thread(groundPanel); t.setName("windowPanel"); t.start(); - Thread t2 = new Thread(heroInfoPanel); - t2.setName("heroInfoPanel"); - t2.start(); +// Thread t2 = new Thread(heroInfoPanel); +// t2.setName("heroInfoPanel"); +// t2.start(); } //创建菜单及菜单选项 @@ -162,7 +175,7 @@ public void run() { //显示属性的窗体: heroInfoPanel.setLayout(new GridLayout(6, 1, 0, 0)); - heroInfoPanel.add(jl1);//网格布局 + heroInfoPanel.add(tankCounter);//网格布局 heroInfoPanel.add(prizeNo); // mp3.add(jl2); // mp3.add(jl3); From 37dee64f87430f152bfa0350c3026bd7bbde1cb0 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Fri, 17 Sep 2021 02:47:58 +0800 Subject: [PATCH 232/476] x) delay remove --- .../github/kuangcp/tank/panel/TankGroundPanel.java | 12 ++---------- .../tank/util/executor/AbstractDelayEvent.java | 2 +- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java b/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java index 96167182..e8e87c04 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java +++ b/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java @@ -330,16 +330,8 @@ public void paint(Graphics g) { continue; } demon.delayRemove = true; -// ExecutePool.delayPool.execute(() -> { -// try { -// TimeUnit.SECONDS.sleep(8); -// } catch (InterruptedException e) { -// log.error("", e); -// } -// enemyList.remove(demon); -// }); - - DelayExecutor.addEvent(new AbstractDelayEvent(8_000) { + + DelayExecutor.addEvent(new AbstractDelayEvent(7_000) { @Override public void run() { enemyList.remove(demon); diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/executor/AbstractDelayEvent.java b/gui/src/main/java/com/github/kuangcp/tank/util/executor/AbstractDelayEvent.java index d3d64d93..a47cabb2 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/util/executor/AbstractDelayEvent.java +++ b/gui/src/main/java/com/github/kuangcp/tank/util/executor/AbstractDelayEvent.java @@ -14,7 +14,7 @@ public abstract class AbstractDelayEvent implements DelayEvent { long delayTime; public AbstractDelayEvent(long delayTime) { - this.delayTime = delayTime; + this.delayTime = delayTime + System.currentTimeMillis(); } From 5278ad9ffc51539d81b88ffaedbb417c7c1c8cc8 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Fri, 17 Sep 2021 03:01:34 +0800 Subject: [PATCH 233/476] x) more delay --- .../java/com/github/kuangcp/tank/Readme.md | 6 ++++++ .../kuangcp/tank/panel/TankGroundPanel.java | 3 ++- .../com/github/kuangcp/tank/v3/MainFrame.java | 19 ++++++++++--------- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/gui/src/main/java/com/github/kuangcp/tank/Readme.md b/gui/src/main/java/com/github/kuangcp/tank/Readme.md index f34f0cc0..33c46a1b 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/Readme.md +++ b/gui/src/main/java/com/github/kuangcp/tank/Readme.md @@ -6,3 +6,9 @@ 3. run: `./gui/bin/gui` -javaagent:/path/to/quasar-core-0.7.4-jdk8.jar + +## TODO +1. 碰撞监测 +1. 对方运动算法 +1. 网络联机 +1. 持久化存储 \ No newline at end of file diff --git a/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java b/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java index e8e87c04..ceee4447 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java +++ b/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java @@ -206,7 +206,8 @@ public void paint(Graphics g) { } } - g.setColor(new Color(190, 60, 50)); +// g.setColor(new Color(190, 60, 50)); + g.setColor(new Color(188, 112, 50)); for (int i = 0; i < bricks.size(); i++) { Brick bs = bricks.get(i); if (bs.getAlive()) { diff --git a/gui/src/main/java/com/github/kuangcp/tank/v3/MainFrame.java b/gui/src/main/java/com/github/kuangcp/tank/v3/MainFrame.java index 801b733c..e98021e0 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v3/MainFrame.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v3/MainFrame.java @@ -78,9 +78,13 @@ public void run() { if (Objects.nonNull(groundPanel)) { groundPanel.exit(); - } else { - groundPanel = new TankGroundPanel(); } + // FIXME 复用对象导致性能缓慢 +// else { +// groundPanel = new TankGroundPanel(); +// } + + groundPanel = new TankGroundPanel(); if (Objects.nonNull(heroInfoPanel)) { loopEvent.stop(); @@ -94,20 +98,17 @@ public void run() { heroInfoPanel = new HeroInfoPanel(tankCounter, groundPanel.enemyList, prizeNo); } - actionPanel = new StageActionPanel(this, groundPanel.hero, groundPanel.enemyList, groundPanel.bricks, groundPanel.irons, TankGroundPanel.enemyTankMap, TankGroundPanel.myself);//放按钮的画布 + actionPanel = new StageActionPanel(this, groundPanel.hero, groundPanel.enemyList, groundPanel.bricks, + groundPanel.irons, TankGroundPanel.enemyTankMap, TankGroundPanel.myself);//放按钮的画布 loopEvent = new HeroInfoPanelRefreshEvent(heroInfoPanel); heroInfoPanel.setRefreshEvent(loopEvent); MonitorExecutor.addLoopEvent(loopEvent); if (!firstStart) { - //已经成为一个线程 要启动它 Thread t = new Thread(groundPanel); - t.setName("windowPanel"); + t.setName("tankGroundPanel"); t.start(); -// Thread t2 = new Thread(heroInfoPanel); -// t2.setName("heroInfoPanel"); -// t2.start(); } //创建菜单及菜单选项 @@ -214,6 +215,6 @@ public void run() { this.setVisible(true); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); final long now = System.currentTimeMillis(); - log.info("[init] total:{} visible:{}", (now - start), (now - beforeVisible)); + log.info("[init] mainFrame. total:{} visible:{}", (now - start), (now - beforeVisible)); } } \ No newline at end of file From 01ced24b229aaae52eab468d2882a2a1eff66b7a Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Tue, 21 Sep 2021 11:40:02 +0800 Subject: [PATCH 234/476] *) use config file --- gui/.gitignore | 4 +++ .../java/com/github/kuangcp/tank/Readme.md | 2 ++ .../com/github/kuangcp/tank/mgr/BombMgr.java | 7 +++- .../tank/resource/AbstractImgListMgr.java | 7 +++- .../kuangcp/tank/resource/AvatarImgMgr.java | 5 +-- .../kuangcp/tank/resource/DefeatImgMgr.java | 6 ++-- .../kuangcp/tank/resource/PropertiesMgr.java | 34 +++++++++++++++++++ .../kuangcp/tank/resource/ResourceMgr.java | 2 ++ .../kuangcp/tank/resource/VictoryImgMgr.java | 6 +++- .../com/github/kuangcp/tank/v3/MainFrame.java | 2 +- .../github/kuangcp/tank/v3/MainTankGame.java | 2 ++ .../main/resources/tank/conf/img.properties | 4 +++ 12 files changed, 73 insertions(+), 8 deletions(-) create mode 100644 gui/.gitignore create mode 100644 gui/src/main/java/com/github/kuangcp/tank/resource/PropertiesMgr.java create mode 100644 gui/src/main/resources/tank/conf/img.properties diff --git a/gui/.gitignore b/gui/.gitignore new file mode 100644 index 00000000..63fbb586 --- /dev/null +++ b/gui/.gitignore @@ -0,0 +1,4 @@ +*.jar +*.tar +*.zip + diff --git a/gui/src/main/java/com/github/kuangcp/tank/Readme.md b/gui/src/main/java/com/github/kuangcp/tank/Readme.md index 33c46a1b..4deac783 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/Readme.md +++ b/gui/src/main/java/com/github/kuangcp/tank/Readme.md @@ -7,6 +7,8 @@ -javaagent:/path/to/quasar-core-0.7.4-jdk8.jar +direct run: `ge tank -x test && cp out/build/distributions/gui.tar . && tar xf gui.tar && ./gui/bin/gui` + ## TODO 1. 碰撞监测 1. 对方运动算法 diff --git a/gui/src/main/java/com/github/kuangcp/tank/mgr/BombMgr.java b/gui/src/main/java/com/github/kuangcp/tank/mgr/BombMgr.java index d8adb92f..cf7612f8 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/mgr/BombMgr.java +++ b/gui/src/main/java/com/github/kuangcp/tank/mgr/BombMgr.java @@ -6,6 +6,8 @@ import com.github.kuangcp.tank.domain.Hero; import com.github.kuangcp.tank.domain.Tank; import com.github.kuangcp.tank.resource.AbstractImgListMgr; +import com.github.kuangcp.tank.resource.PropertiesMgr; + import lombok.extern.slf4j.Slf4j; import javax.swing.*; @@ -24,7 +26,10 @@ public class BombMgr extends AbstractImgListMgr { public static BombMgr instance = new BombMgr(); public BombMgr() { - super.imgPathArr = new String[]{"/images/bomb_1.gif", "/images/bomb_2.gif", "/images/bomb_3.gif"}; + } + + public String getConfigKey(){ + return PropertiesMgr.Key.Img.bomb; } //定义炸弹爆炸集合 diff --git a/gui/src/main/java/com/github/kuangcp/tank/resource/AbstractImgListMgr.java b/gui/src/main/java/com/github/kuangcp/tank/resource/AbstractImgListMgr.java index 607ae43d..494695d1 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/resource/AbstractImgListMgr.java +++ b/gui/src/main/java/com/github/kuangcp/tank/resource/AbstractImgListMgr.java @@ -16,17 +16,21 @@ public abstract class AbstractImgListMgr { public Image curImg = null; public Image[] imgArr = null; - public String[] imgPathArr; + private String[] imgPathArr; // 实际渲染大小 public int width; public int height; public void loadImg() { + Object val = PropertiesMgr.imgProperties.get(this.getConfigKey()); + imgPathArr = val.toString().split(","); + if (Objects.isNull(imgPathArr) || imgPathArr.length < 1) { log.warn("no image {}", this.getClass().getSimpleName()); return; } + try { this.imgArr = new Image[imgPathArr.length]; for (int i = 0; i < imgPathArr.length; i++) { @@ -38,4 +42,5 @@ public void loadImg() { curImg = this.imgArr[(int) (Math.random() * imgPathArr.length)]; } + public abstract String getConfigKey(); } diff --git a/gui/src/main/java/com/github/kuangcp/tank/resource/AvatarImgMgr.java b/gui/src/main/java/com/github/kuangcp/tank/resource/AvatarImgMgr.java index d912d5d4..8dd46d89 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/resource/AvatarImgMgr.java +++ b/gui/src/main/java/com/github/kuangcp/tank/resource/AvatarImgMgr.java @@ -13,8 +13,9 @@ public class AvatarImgMgr extends AbstractImgListMgr { public AvatarImgMgr() { super.width = 60; super.height = 60; + } - super.imgPathArr = new String[]{"/images/Me.jpg", "/images/Me2.jpg", "/images/Me3.jpg", "/images/Me4.jpg", - "/images/Me5.jpg"}; + public String getConfigKey(){ + return PropertiesMgr.Key.Img.avatar; } } diff --git a/gui/src/main/java/com/github/kuangcp/tank/resource/DefeatImgMgr.java b/gui/src/main/java/com/github/kuangcp/tank/resource/DefeatImgMgr.java index b04798c3..fcb4a0b6 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/resource/DefeatImgMgr.java +++ b/gui/src/main/java/com/github/kuangcp/tank/resource/DefeatImgMgr.java @@ -8,9 +8,11 @@ public class DefeatImgMgr extends AbstractImgListMgr { public static DefeatImgMgr instance = new DefeatImgMgr(); public DefeatImgMgr() { - super.imgPathArr = new String[]{"/images/Over2.jpg", "/images/Over4.jpg"}; - super.width = 760; super.height = 650; } + + public String getConfigKey(){ + return PropertiesMgr.Key.Img.defeat; + } } diff --git a/gui/src/main/java/com/github/kuangcp/tank/resource/PropertiesMgr.java b/gui/src/main/java/com/github/kuangcp/tank/resource/PropertiesMgr.java new file mode 100644 index 00000000..c674aeca --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/resource/PropertiesMgr.java @@ -0,0 +1,34 @@ +package com.github.kuangcp.tank.resource; + +import lombok.extern.slf4j.Slf4j; + +import java.io.IOException; +import java.util.Properties; + +/** + * @author https://github.com/kuangcp on 2021-09-21 10:05 + */ +@Slf4j +public class PropertiesMgr { + + public static class Key { + + public static interface Img { + String avatar = "round.avatar"; + String defeat = "round.defeat"; + String victory = "round.victory"; + String bomb = "animation.bomb"; + } + } + + public static Properties imgProperties = null; + + public static void init(){ + imgProperties = new Properties(); + try { + imgProperties.load(PropertiesMgr.class.getResourceAsStream("/tank/conf/img.properties")); + } catch (IOException e) { + log.error("", e); + } + } +} diff --git a/gui/src/main/java/com/github/kuangcp/tank/resource/ResourceMgr.java b/gui/src/main/java/com/github/kuangcp/tank/resource/ResourceMgr.java index 9061b7fa..a6f1c1b0 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/resource/ResourceMgr.java +++ b/gui/src/main/java/com/github/kuangcp/tank/resource/ResourceMgr.java @@ -10,6 +10,8 @@ public class ResourceMgr { public static void loadResource() { + PropertiesMgr.init(); + log.info("[init] start load resource"); // image BombMgr.instance.loadImg(); diff --git a/gui/src/main/java/com/github/kuangcp/tank/resource/VictoryImgMgr.java b/gui/src/main/java/com/github/kuangcp/tank/resource/VictoryImgMgr.java index 0de24dfb..7e0b5bc3 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/resource/VictoryImgMgr.java +++ b/gui/src/main/java/com/github/kuangcp/tank/resource/VictoryImgMgr.java @@ -14,6 +14,10 @@ public VictoryImgMgr() { super.width = 760; super.height = 650; - super.imgPathArr = new String[]{"/images/Win2.jpg"}; + // super.imgPathArr = new String[]{"/images/Win2.jpg"}; + } + + public String getConfigKey(){ + return PropertiesMgr.Key.Img.victory; } } diff --git a/gui/src/main/java/com/github/kuangcp/tank/v3/MainFrame.java b/gui/src/main/java/com/github/kuangcp/tank/v3/MainFrame.java index e98021e0..6af3b80a 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v3/MainFrame.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v3/MainFrame.java @@ -70,7 +70,7 @@ public class MainFrame extends JFrame implements Runnable { JMenuItem setting = null; public MainFrame() { - ResourceMgr.loadResource(); + } public void run() { diff --git a/gui/src/main/java/com/github/kuangcp/tank/v3/MainTankGame.java b/gui/src/main/java/com/github/kuangcp/tank/v3/MainTankGame.java index cc1dc561..9ef3e047 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v3/MainTankGame.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v3/MainTankGame.java @@ -1,5 +1,6 @@ package com.github.kuangcp.tank.v3; +import com.github.kuangcp.tank.resource.ResourceMgr; import com.github.kuangcp.tank.util.executor.MonitorExecutor; import lombok.extern.slf4j.Slf4j; @@ -15,6 +16,7 @@ public class MainTankGame { */ public static void main(String[] args) { MonitorExecutor.init(); + ResourceMgr.loadResource(); EventQueue.invokeLater(new MainFrame()); } diff --git a/gui/src/main/resources/tank/conf/img.properties b/gui/src/main/resources/tank/conf/img.properties new file mode 100644 index 00000000..7c49971a --- /dev/null +++ b/gui/src/main/resources/tank/conf/img.properties @@ -0,0 +1,4 @@ +round.avatar=/images/Me4.jpg +round.defeat=/images/Over.jpg +round.victory=/images/Win2.jpg +animation.bomb=/images/bomb_1.gif,/images/bomb_2.gif,/images/bomb_3.gif \ No newline at end of file From 8febe358f0fe3e1ca788aedcb1399deb57c4e8d3 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Tue, 21 Sep 2021 22:39:19 +0800 Subject: [PATCH 235/476] *) move to new dir --- .../com/github/kuangcp/tank/mgr/BombMgr.java | 2 +- .../tank/resource/AbstractImgListMgr.java | 20 ++++++++---------- .../kuangcp/tank/resource/AvatarImgMgr.java | 2 +- .../kuangcp/tank/resource/DefeatImgMgr.java | 2 +- .../kuangcp/tank/resource/PropertiesMgr.java | 15 +++++++------ .../kuangcp/tank/resource/VictoryImgMgr.java | 2 +- .../main/resources/tank/conf/img.properties | 8 +++---- .../resources/{images => tank/img}/bomb_1.gif | Bin .../resources/{images => tank/img}/bomb_2.gif | Bin .../resources/{images => tank/img}/bomb_3.gif | Bin 10 files changed, 25 insertions(+), 26 deletions(-) rename gui/src/main/resources/{images => tank/img}/bomb_1.gif (100%) rename gui/src/main/resources/{images => tank/img}/bomb_2.gif (100%) rename gui/src/main/resources/{images => tank/img}/bomb_3.gif (100%) diff --git a/gui/src/main/java/com/github/kuangcp/tank/mgr/BombMgr.java b/gui/src/main/java/com/github/kuangcp/tank/mgr/BombMgr.java index cf7612f8..9ac600c4 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/mgr/BombMgr.java +++ b/gui/src/main/java/com/github/kuangcp/tank/mgr/BombMgr.java @@ -29,7 +29,7 @@ public BombMgr() { } public String getConfigKey(){ - return PropertiesMgr.Key.Img.bomb; + return PropertiesMgr.Key.Img.ANIMATION_BOMB; } //定义炸弹爆炸集合 diff --git a/gui/src/main/java/com/github/kuangcp/tank/resource/AbstractImgListMgr.java b/gui/src/main/java/com/github/kuangcp/tank/resource/AbstractImgListMgr.java index 494695d1..b4c85353 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/resource/AbstractImgListMgr.java +++ b/gui/src/main/java/com/github/kuangcp/tank/resource/AbstractImgListMgr.java @@ -5,7 +5,6 @@ import javax.imageio.ImageIO; import java.awt.*; import java.io.IOException; -import java.util.Objects; /** * @author https://github.com/kuangcp on 2021-09-13 01:17 @@ -16,30 +15,29 @@ public abstract class AbstractImgListMgr { public Image curImg = null; public Image[] imgArr = null; - private String[] imgPathArr; - // 实际渲染大小 public int width; public int height; public void loadImg() { - Object val = PropertiesMgr.imgProperties.get(this.getConfigKey()); - imgPathArr = val.toString().split(","); + try { + String val = PropertiesMgr.imgProperties.getProperty(this.getConfigKey()); + String[] imgPathArr = val.split(","); - if (Objects.isNull(imgPathArr) || imgPathArr.length < 1) { - log.warn("no image {}", this.getClass().getSimpleName()); - return; - } + if (imgPathArr.length < 1) { + log.warn("no image {}", this.getClass().getSimpleName()); + return; + } - try { this.imgArr = new Image[imgPathArr.length]; for (int i = 0; i < imgPathArr.length; i++) { this.imgArr[i] = ImageIO.read(getClass().getResourceAsStream(imgPathArr[i])); } + + curImg = this.imgArr[(int) (Math.random() * imgPathArr.length)]; } catch (IOException e) { log.error("", e); } - curImg = this.imgArr[(int) (Math.random() * imgPathArr.length)]; } public abstract String getConfigKey(); diff --git a/gui/src/main/java/com/github/kuangcp/tank/resource/AvatarImgMgr.java b/gui/src/main/java/com/github/kuangcp/tank/resource/AvatarImgMgr.java index 8dd46d89..f3460894 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/resource/AvatarImgMgr.java +++ b/gui/src/main/java/com/github/kuangcp/tank/resource/AvatarImgMgr.java @@ -16,6 +16,6 @@ public AvatarImgMgr() { } public String getConfigKey(){ - return PropertiesMgr.Key.Img.avatar; + return PropertiesMgr.Key.Img.ROUND_AVATAR; } } diff --git a/gui/src/main/java/com/github/kuangcp/tank/resource/DefeatImgMgr.java b/gui/src/main/java/com/github/kuangcp/tank/resource/DefeatImgMgr.java index fcb4a0b6..24c9e5ed 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/resource/DefeatImgMgr.java +++ b/gui/src/main/java/com/github/kuangcp/tank/resource/DefeatImgMgr.java @@ -13,6 +13,6 @@ public DefeatImgMgr() { } public String getConfigKey(){ - return PropertiesMgr.Key.Img.defeat; + return PropertiesMgr.Key.Img.ROUND_DEFEAT; } } diff --git a/gui/src/main/java/com/github/kuangcp/tank/resource/PropertiesMgr.java b/gui/src/main/java/com/github/kuangcp/tank/resource/PropertiesMgr.java index c674aeca..dece8d67 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/resource/PropertiesMgr.java +++ b/gui/src/main/java/com/github/kuangcp/tank/resource/PropertiesMgr.java @@ -11,19 +11,20 @@ @Slf4j public class PropertiesMgr { - public static class Key { + public interface Key { - public static interface Img { - String avatar = "round.avatar"; - String defeat = "round.defeat"; - String victory = "round.victory"; - String bomb = "animation.bomb"; + interface Img { + String ROUND_AVATAR = "round.avatar"; + String ROUND_DEFEAT = "round.defeat"; + String ROUND_VICTORY = "round.victory"; + + String ANIMATION_BOMB = "animation.bomb"; } } public static Properties imgProperties = null; - public static void init(){ + public static void init() { imgProperties = new Properties(); try { imgProperties.load(PropertiesMgr.class.getResourceAsStream("/tank/conf/img.properties")); diff --git a/gui/src/main/java/com/github/kuangcp/tank/resource/VictoryImgMgr.java b/gui/src/main/java/com/github/kuangcp/tank/resource/VictoryImgMgr.java index 7e0b5bc3..2591143c 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/resource/VictoryImgMgr.java +++ b/gui/src/main/java/com/github/kuangcp/tank/resource/VictoryImgMgr.java @@ -18,6 +18,6 @@ public VictoryImgMgr() { } public String getConfigKey(){ - return PropertiesMgr.Key.Img.victory; + return PropertiesMgr.Key.Img.ROUND_VICTORY; } } diff --git a/gui/src/main/resources/tank/conf/img.properties b/gui/src/main/resources/tank/conf/img.properties index 7c49971a..eb128930 100644 --- a/gui/src/main/resources/tank/conf/img.properties +++ b/gui/src/main/resources/tank/conf/img.properties @@ -1,4 +1,4 @@ -round.avatar=/images/Me4.jpg -round.defeat=/images/Over.jpg -round.victory=/images/Win2.jpg -animation.bomb=/images/bomb_1.gif,/images/bomb_2.gif,/images/bomb_3.gif \ No newline at end of file +round.avatar=/tank/img/Me4.jpg +round.defeat=/tank/img/Over.jpg +round.victory=/tank/img/Win2.jpg +animation.bomb=/tank/img/bomb_1.gif,/tank/img/bomb_2.gif,/tank/img/bomb_3.gif \ No newline at end of file diff --git a/gui/src/main/resources/images/bomb_1.gif b/gui/src/main/resources/tank/img/bomb_1.gif similarity index 100% rename from gui/src/main/resources/images/bomb_1.gif rename to gui/src/main/resources/tank/img/bomb_1.gif diff --git a/gui/src/main/resources/images/bomb_2.gif b/gui/src/main/resources/tank/img/bomb_2.gif similarity index 100% rename from gui/src/main/resources/images/bomb_2.gif rename to gui/src/main/resources/tank/img/bomb_2.gif diff --git a/gui/src/main/resources/images/bomb_3.gif b/gui/src/main/resources/tank/img/bomb_3.gif similarity index 100% rename from gui/src/main/resources/images/bomb_3.gif rename to gui/src/main/resources/tank/img/bomb_3.gif From d41a73ee477ef47abcb9a2776f654a38a77cea02 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Tue, 21 Sep 2021 23:09:32 +0800 Subject: [PATCH 236/476] +) val label --- .../kuangcp/tank/panel/HeroInfoPanel.java | 2 +- .../kuangcp/tank/panel/StageActionPanel.java | 2 +- .../kuangcp/tank/panel/TankGroundPanel.java | 2 +- .../github/kuangcp/tank/v3/PlayStageMgr.java | 2 +- .../github/kuangcp/tank/v3/SettingFrame.java | 77 ++++++++++--------- 5 files changed, 43 insertions(+), 42 deletions(-) diff --git a/gui/src/main/java/com/github/kuangcp/tank/panel/HeroInfoPanel.java b/gui/src/main/java/com/github/kuangcp/tank/panel/HeroInfoPanel.java index 226cc6f4..2eeee7a9 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/panel/HeroInfoPanel.java +++ b/gui/src/main/java/com/github/kuangcp/tank/panel/HeroInfoPanel.java @@ -78,7 +78,7 @@ public void paint(Graphics g) { } public void refreshData() { - if (PlayStageMgr.waitStart() || PlayStageMgr.pause) { + if (PlayStageMgr.stageNoneStart() || PlayStageMgr.pause) { return; } diff --git a/gui/src/main/java/com/github/kuangcp/tank/panel/StageActionPanel.java b/gui/src/main/java/com/github/kuangcp/tank/panel/StageActionPanel.java index 4fcd9240..1da97023 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/panel/StageActionPanel.java +++ b/gui/src/main/java/com/github/kuangcp/tank/panel/StageActionPanel.java @@ -73,7 +73,7 @@ public void actionPerformed(ActionEvent ae) { } if (ae.getActionCommand().equals(ButtonCommand.SETTING_FRAME)) { - SettingFrame.activeFocus(hero); + SettingFrame.activeFocus(); } if (ae.getActionCommand().equals("saveExit")) { diff --git a/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java b/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java index ceee4447..c3c5b771 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java +++ b/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java @@ -182,7 +182,7 @@ public void startNewRound() { @Override public void paint(Graphics g) { super.paint(g); - if (PlayStageMgr.waitStart() || Objects.isNull(hero)) { + if (PlayStageMgr.stageNoneStart() || Objects.isNull(hero)) { PlayStageMgr.drawStopIgnore(g, this); return; } diff --git a/gui/src/main/java/com/github/kuangcp/tank/v3/PlayStageMgr.java b/gui/src/main/java/com/github/kuangcp/tank/v3/PlayStageMgr.java index 35c53677..9dcf0f0c 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v3/PlayStageMgr.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v3/PlayStageMgr.java @@ -51,7 +51,7 @@ public class PlayStageMgr { /** * 等待stage启动 */ - public static boolean waitStart() { + public static boolean stageNoneStart() { return Objects.isNull(PlayStageMgr.instance) || !PlayStageMgr.instance.startLogic; } diff --git a/gui/src/main/java/com/github/kuangcp/tank/v3/SettingFrame.java b/gui/src/main/java/com/github/kuangcp/tank/v3/SettingFrame.java index 2beafaef..4ca4206d 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v3/SettingFrame.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v3/SettingFrame.java @@ -5,68 +5,57 @@ import com.github.kuangcp.tank.domain.Hero; import lombok.extern.slf4j.Slf4j; -import javax.imageio.ImageIO; import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import java.awt.image.BufferedImage; -import java.io.IOException; import java.util.Objects; +import java.util.Optional; @Slf4j public class SettingFrame extends JFrame implements ActionListener { public static SettingFrame instance = null; - JPanel up, ss; - JLabel U; + JPanel buttonArea; JButton[] incrArr = new JButton[4]; JButton[] decrArr = new JButton[4]; JLabel[] labelArr = new JLabel[4]; - Hero hero; + JLabel[] valArr = new JLabel[]{new JLabel(), new JLabel(), new JLabel(), new JLabel()}; - public static synchronized void activeFocus(Hero hero) { - final SettingFrame frame = getInstance(hero); + public static synchronized void activeFocus() { + final SettingFrame frame = getInstance(); frame.setVisible(true); } - private static synchronized SettingFrame getInstance(Hero hero) { + private static synchronized SettingFrame getInstance() { if (Objects.isNull(instance)) { - instance = new SettingFrame(hero); + instance = new SettingFrame(); } - instance.hero = hero; return instance; } - public SettingFrame(Hero hero) { - this.hero = hero; - try { - final BufferedImage iconImg = ImageIO.read(getClass().getResourceAsStream("/images/Me4.jpg")); - U = new JLabel(new ImageIcon(iconImg)); - } catch (IOException e) { - log.error("", e); - } - + public SettingFrame() { for (int i = 0; i < 4; i++) { incrArr[i] = new JButton("+"); decrArr[i] = new JButton("-"); } labelArr[0] = new JLabel("子弹速度");//+Shot.getSpeed() - labelArr[1] = new JLabel("̹坦克速度");//+hero.getSpeed() - labelArr[2] = new JLabel("坦克生命");//+this.hero.getLife() labelArr[3] = new JLabel("敌人数量");//+MyPanel3.getEnSize() + labelArr[1] = new JLabel("玩家速度");//+hero.getSpeed() + labelArr[2] = new JLabel("玩家生命");//+this.hero.getLife() - up = new JPanel(); - ss = new JPanel(); - - up.add(U); + buttonArea = new JPanel(); - ss.setLayout(new GridLayout(4, 3, 0, 0)); + buttonArea.setLayout(new GridLayout(4, 4, 0, 0)); + // 四行 for (int i = 0; i < 4; i++) { - ss.add(labelArr[i]); - ss.add(incrArr[i]); - ss.add(decrArr[i]); + // 四列 + buttonArea.add(labelArr[i]); + buttonArea.add(valArr[i]); + buttonArea.add(incrArr[i]); + buttonArea.add(decrArr[i]); + incrArr[i].addActionListener(this); decrArr[i].addActionListener(this); } @@ -81,17 +70,29 @@ public SettingFrame(Hero hero) { decrArr[2].setActionCommand(SettingCommand.TANK_HP_DECREMENT); decrArr[3].setActionCommand(SettingCommand.DEMONS_COUNT_DECREMENT); - this.add(up, BorderLayout.NORTH); - this.add(ss, BorderLayout.CENTER); + this.refreshValLabel(); + + this.add(buttonArea, BorderLayout.CENTER); this.setLocation(800, 300); this.setSize(260, 280); this.setTitle("Setting"); -// this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); this.setVisible(true); } + public void refreshValLabel() { + final Optional heroOpt = Optional.ofNullable(PlayStageMgr.instance).map(v -> v.hero); + this.valArr[0].setText(Bullet.getSpeed() + ""); + this.valArr[1].setText(heroOpt.map(v -> v.getSpeed() + "").orElse("")); + this.valArr[2].setText(heroOpt.map(v -> v.getLife() + "").orElse("")); + this.valArr[3].setText(PlayStageMgr.enemySize + ""); + } + @Override public void actionPerformed(ActionEvent event) { + if (PlayStageMgr.stageNoneStart()) { + return; + } + if (event.getActionCommand().equals(SettingCommand.SHOT_SPEED_INCREMENT)) { Bullet.setSpeed(Bullet.getSpeed() + 1); } @@ -100,26 +101,26 @@ public void actionPerformed(ActionEvent event) { } if (event.getActionCommand().equals(SettingCommand.TANK_SPEED_INCREMENT)) { - hero.addSpeed(1); + PlayStageMgr.instance.hero.addSpeed(1); } if (event.getActionCommand().equals(SettingCommand.TANK_SPEED_DECREMENT)) { - hero.addSpeed(-1); + PlayStageMgr.instance.hero.addSpeed(-1); } if (event.getActionCommand().equals(SettingCommand.TANK_HP_INCREMENT)) { - this.hero.addLife(1); + PlayStageMgr.instance.hero.addLife(1); } if (event.getActionCommand().equals(SettingCommand.TANK_HP_DECREMENT)) { - this.hero.addLife(-1); + PlayStageMgr.instance.hero.addLife(-1); } if (event.getActionCommand().equals(SettingCommand.DEMONS_COUNT_INCREMENT)) { PlayStageMgr.addEnemySize(1); - log.info("{}", PlayStageMgr.getEnemySize()); } if (event.getActionCommand().equals(SettingCommand.DEMONS_COUNT_DECREMENT)) { PlayStageMgr.addEnemySize(-1); } + this.refreshValLabel(); } } \ No newline at end of file From 9e4bb84aefe81dc0422a39dcda9f2b89a21dbd20 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Tue, 21 Sep 2021 23:11:23 +0800 Subject: [PATCH 237/476] *) v1 --- .../github/kuangcp/tank/v1/MainPanelV1.java | 10 +++-- .../com/github/kuangcp/tank/v2/TankGame2.java | 39 ------------------- .../github/kuangcp/tank/v2/TankGameV2.java | 29 ++++++++++++++ 3 files changed, 35 insertions(+), 43 deletions(-) delete mode 100644 gui/src/main/java/com/github/kuangcp/tank/v2/TankGame2.java create mode 100644 gui/src/main/java/com/github/kuangcp/tank/v2/TankGameV2.java diff --git a/gui/src/main/java/com/github/kuangcp/tank/v1/MainPanelV1.java b/gui/src/main/java/com/github/kuangcp/tank/v1/MainPanelV1.java index 54161182..d3c14fc6 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v1/MainPanelV1.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v1/MainPanelV1.java @@ -111,12 +111,14 @@ public void run() { s.drawSelf(g); } - // 画出砖块 + //画出砖块 + g.setColor(new Color(188, 112, 50)); + for (int i = 0; i < 9; i++) { + g.fill3DRect(60, 60 + 10 * i, 20, 10, false); + } + g.setColor(Color.yellow); g.drawRect(0, 0, 400, 300); - for (int j = 0; j < 20; j++) { - g.draw3DRect(60 + 8 * j, 50, 5, 10, false); - } } diff --git a/gui/src/main/java/com/github/kuangcp/tank/v2/TankGame2.java b/gui/src/main/java/com/github/kuangcp/tank/v2/TankGame2.java deleted file mode 100644 index c3777f1f..00000000 --- a/gui/src/main/java/com/github/kuangcp/tank/v2/TankGame2.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.github.kuangcp.tank.v2; -/** - * 能控制坦克运动,有墙的机制 - *

- *

- * 坦克2.0 版本 目标:按下j键发射直线的子弹 - * 子弹是一个对象,线程 - */ - -import javax.swing.*; - -@SuppressWarnings("serial") -public class TankGame2 extends JFrame { - - public static MyPanel mp = null;//画板 - - @SuppressWarnings("unused") - public static void main(String[] args) { - TankGame2 Tank = new TankGame2(); - } - - //最外层JFrame的构造函数 - public TankGame2() { - mp = new MyPanel();//已经成为一个线程 要启动它 - Thread t = new Thread(mp); - t.start(); - - this.add(mp); - - //注册键盘监听 - //下面的语句翻译为 :当前类的监听者是mp - this.addKeyListener(mp); - - this.setLocation(900, 200); - this.setSize(500, 400); - this.setVisible(true); - this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - } -} diff --git a/gui/src/main/java/com/github/kuangcp/tank/v2/TankGameV2.java b/gui/src/main/java/com/github/kuangcp/tank/v2/TankGameV2.java new file mode 100644 index 00000000..ccf67769 --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/v2/TankGameV2.java @@ -0,0 +1,29 @@ +package com.github.kuangcp.tank.v2; + +import javax.swing.*; + +/** + * 1 绘画出坦克 并且能做到对它的移动操作 + * 2 移动最好封装成函数(面向对象思想)这是坦克的行为属性 可扩展 + * 3 加上对坦克运动的边界限定 + */ +@SuppressWarnings("serial") +public class TankGameV2 extends JFrame { + + @SuppressWarnings("unused") + public static void main(String[] args) { + TankGameV2 Tank = new TankGameV2(); + } + + //最外层JFrame的构造函数 + public TankGameV2() { + MainPanelV2 panel = new MainPanelV2(); + this.add(panel); + this.addKeyListener(panel); + + this.setLocation(900, 200); + this.setSize(405, 333); + this.setVisible(true); + this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + } +} \ No newline at end of file From 43f55095921a17ad7017629a61ab165c005a1e22 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Tue, 21 Sep 2021 23:28:55 +0800 Subject: [PATCH 238/476] *) avoid dep hero --- .../github/kuangcp/tank/domain/EnemyTank.java | 10 +- .../java/com/github/kuangcp/tank/v1/Readme.md | 1 + .../com/github/kuangcp/tank/v2/MyPanel.java | 181 ------------------ .../java/com/github/kuangcp/tank/v2/Readme.md | 1 + .../github/kuangcp/tank/v2/TankGameV2.java | 6 + .../github/kuangcp/tank/v3/PlayStageMgr.java | 11 +- 6 files changed, 22 insertions(+), 188 deletions(-) create mode 100644 gui/src/main/java/com/github/kuangcp/tank/v1/Readme.md delete mode 100644 gui/src/main/java/com/github/kuangcp/tank/v2/MyPanel.java create mode 100644 gui/src/main/java/com/github/kuangcp/tank/v2/Readme.md diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java b/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java index 5cf0eaee..81d2ba10 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java @@ -641,7 +641,7 @@ public void newEventRun() { min++; // if(!bri)this.direct = (int)(Math.random()*4); if (y > 30) { - if (TankTool.ablePass(this, PlayStageMgr.instance.hero) && overlap) y -= speed; + if (PlayStageMgr.hasTouchHero(this) && overlap) y -= speed; // if(bri)y-=speed; // else {y+=speed;this.direct = 1;} // else continue; @@ -661,7 +661,7 @@ public void newEventRun() { // if(!bri)this.direct = (int)(Math.random()*4); if (y < 530) { - if (TankTool.ablePass(this, PlayStageMgr.instance.hero) && overlap && bri) y += speed; + if (PlayStageMgr.hasTouchHero(this) && overlap && bri) y += speed; // if(bri) y+=speed; // else {y-=speed;this.direct = 0;} // else continue; @@ -681,7 +681,7 @@ public void newEventRun() { // if(!bri)this.direct = (int)(Math.random()*4); if (x > 30) { - if (TankTool.ablePass(this, PlayStageMgr.instance.hero) && overlap && bri) x -= speed; + if (PlayStageMgr.hasTouchHero(this) && overlap && bri) x -= speed; // if(bri)x-=speed; // else{x+=speed;this.direct = 3;} // else continue; @@ -700,7 +700,7 @@ public void newEventRun() { // if(!bri)this.direct = (int)(Math.random()*4); if (x < 710) { - if (TankTool.ablePass(this, PlayStageMgr.instance.hero) && overlap && bri) { + if (PlayStageMgr.hasTouchHero(this) && overlap && bri) { x += speed; } // if(bri)x+=speed; @@ -712,8 +712,6 @@ public void newEventRun() { // TankTool.yieldMsTime(50); } - - } break; default: diff --git a/gui/src/main/java/com/github/kuangcp/tank/v1/Readme.md b/gui/src/main/java/com/github/kuangcp/tank/v1/Readme.md new file mode 100644 index 00000000..ab19a45b --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/v1/Readme.md @@ -0,0 +1 @@ +画出基本图形 \ No newline at end of file diff --git a/gui/src/main/java/com/github/kuangcp/tank/v2/MyPanel.java b/gui/src/main/java/com/github/kuangcp/tank/v2/MyPanel.java deleted file mode 100644 index d63723fd..00000000 --- a/gui/src/main/java/com/github/kuangcp/tank/v2/MyPanel.java +++ /dev/null @@ -1,181 +0,0 @@ -package com.github.kuangcp.tank.v2; - - -import com.github.kuangcp.tank.domain.EnemyTank; -import com.github.kuangcp.tank.domain.Hero; -import com.github.kuangcp.tank.domain.Bullet; -import com.github.kuangcp.tank.util.TankTool; - -import javax.swing.*; -import java.awt.*; -import java.awt.event.KeyEvent; -import java.awt.event.KeyListener; -import java.util.Vector; - - -@SuppressWarnings("serial") -public class MyPanel extends JPanel implements KeyListener, Runnable { - - //定义我的坦克 - Hero hero = null; - //定义一个 泛型的集合类 表示敌人坦克集合 - Vector ets = new Vector<>(); - - //画板的构造函数 放图形的对象 - public MyPanel() { - } - - //重写paint - public void paint(Graphics g) { - super.paint(g); - - hero = new Hero(20, 20, 4); - - g.fillRect(0, 0, 500, 400); - - //调用函数绘画出主坦克 - this.drawTank(hero.getX(), hero.getY(), g, hero.getDirect(), hero.getType()); - - //从ss 这个子弹集合中 取出每颗子弹,并画出来 - for (int i = 0; i < this.hero.bulletList.size(); i++) { - Bullet myBullet = hero.bulletList.get(i); - // 以下代码只能画出一颗子弹 不完善 - if (myBullet != null) { - g.draw3DRect(hero.bullet.sx, hero.bullet.sy, 1, 1, false); - } - } - - //画出敌人坦克 - for (EnemyTank s : ets) { - this.drawTank(s.getX(), s.getY(), g, s.getDirect(), s.getType()); - } - //画出砖块 - g.setColor(Color.yellow); - g.drawRect(0, 0, 400, 300); - for (int j = 0; j < 20; j++) { - g.draw3DRect(60 + 8 * j, 50, 5, 10, false); - } - - } - - //画出坦克的函数 XY是坦克中心的坐标,不是画图参照点 - public void drawTank(int X, int Y, Graphics g, int direct, int type) { - //判断坦克类型 - int x, y;//系统画图函数的参照点 - switch (type) { - case 1: - g.setColor(Color.cyan); - break; - case 0: - g.setColor(Color.yellow); - break; - } - - //判断方向 - switch (direct) { - - case 0: {//向上VK_UP - x = X - 10; - y = Y - 15; - //1.左边的矩形 - g.fill3DRect(x, y, 5, 30, false); - //2.画出右边矩形 - g.fill3DRect(x + 15, y, 5, 30, false); - //3.画出中间矩形 - g.fill3DRect(x + 5, y + 5, 10, 20, false); - //4.画出圆形 - g.fillOval(x + 4, y + 10, 10, 10); - //5.画出线 - g.drawLine(x + 9, y + 15, x + 9, y); - break; - } - case 1: {//向下 VK_DOWN - x = X - 10; - y = Y - 15; - g.fill3DRect(x, y, 5, 30, false); - g.fill3DRect(x + 15, y, 5, 30, false); - g.fill3DRect(x + 5, y + 5, 10, 20, false); - g.fillOval(x + 4, y + 10, 10, 10); - g.drawLine(x + 9, y + 15, x + 9, y + 30); - break; - } - case 2: {//向左 VK_LEFT - x = X - 15; - y = Y - 10; - g.fill3DRect(x, y, 30, 5, false); - g.fill3DRect(x, y + 15, 30, 5, false); - g.fill3DRect(x + 5, y + 5, 20, 10, false); - g.fillOval(x + 10, y + 4, 10, 10); - g.drawLine(x + 15, y + 9, x, y + 9); - break; - } - case 3: {//向右VK_RIGHT - x = X - 15; - y = Y - 10; - g.fill3DRect(x, y, 30, 5, false); - g.fill3DRect(x, y + 15, 30, 5, false); - g.fill3DRect(x + 5, y + 5, 20, 10, false); - g.fillOval(x + 10, y + 4, 10, 10); - g.drawLine(x + 15, y + 9, x + 30, y + 9); - break; - } - } - } - - //当按下键盘上的键时监听者的处理函数 - public void keyPressed(KeyEvent e) { -// 加了if条件后 实现了墙的限制(如果是游戏中的道具,该怎么办) - - if (e.getKeyCode() == KeyEvent.VK_A) { - hero.setDirect(2); - if ((hero.getX() - 10) > 0) - hero.moveLeft(); - - } - if (e.getKeyCode() == KeyEvent.VK_D) { - hero.setDirect(3); - if ((hero.getX() + 15) < 405) - hero.moveRight(); - } - - if (e.getKeyCode() == KeyEvent.VK_W) { - hero.setDirect(0); - if ((hero.getY() - 13) > 0) - hero.moveUp(); - - } - if (e.getKeyCode() == KeyEvent.VK_S) { - hero.setDirect(1); - if ((hero.getY() - 15) < 275) - hero.moveDown(); - - } - this.repaint(); - if (e.getKeyChar() == KeyEvent.VK_J) { - System.out.println("按下 开火键"); - hero.shotEnemy(); - repaint(); - } - - - //必须重新绘制窗口,不然上面的方法不能视觉上动起来 - this.repaint(); - - } - - public void keyReleased(KeyEvent arg0) { - } - - public void keyTyped(KeyEvent arg0) { - } - - //画板做成线程 画布进行刷新 - public void run() { - //每隔100ms重绘 - while (true) { - TankTool.yieldMsTime(7); - //进行重绘 - this.repaint(); - } - } -} \ No newline at end of file diff --git a/gui/src/main/java/com/github/kuangcp/tank/v2/Readme.md b/gui/src/main/java/com/github/kuangcp/tank/v2/Readme.md new file mode 100644 index 00000000..2430f16e --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/v2/Readme.md @@ -0,0 +1 @@ +敌人算法实验 \ No newline at end of file diff --git a/gui/src/main/java/com/github/kuangcp/tank/v2/TankGameV2.java b/gui/src/main/java/com/github/kuangcp/tank/v2/TankGameV2.java index ccf67769..24a79c8a 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v2/TankGameV2.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v2/TankGameV2.java @@ -1,5 +1,7 @@ package com.github.kuangcp.tank.v2; +import com.github.kuangcp.tank.util.executor.MonitorExecutor; + import javax.swing.*; /** @@ -24,6 +26,10 @@ public TankGameV2() { this.setLocation(900, 200); this.setSize(405, 333); this.setVisible(true); + + MonitorExecutor.init(); + final Thread panelThread = new Thread(panel); + panelThread.start(); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } } \ No newline at end of file diff --git a/gui/src/main/java/com/github/kuangcp/tank/v3/PlayStageMgr.java b/gui/src/main/java/com/github/kuangcp/tank/v3/PlayStageMgr.java index 9dcf0f0c..97426528 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v3/PlayStageMgr.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v3/PlayStageMgr.java @@ -4,6 +4,7 @@ import com.github.kuangcp.tank.domain.EnemyTank; import com.github.kuangcp.tank.domain.Hero; import com.github.kuangcp.tank.domain.Iron; +import com.github.kuangcp.tank.domain.Tank; import com.github.kuangcp.tank.resource.DefeatImgMgr; import com.github.kuangcp.tank.resource.VictoryImgMgr; import com.github.kuangcp.tank.util.TankTool; @@ -37,7 +38,7 @@ public class PlayStageMgr { /** * 敌人的数量 */ - static int enemySize = 36; + static int enemySize = 8; /** * 无敌状态 时间 */ @@ -148,4 +149,12 @@ public static void addEnemySize(int delta) { public static long getInvincibleMs() { return invincibleMs; } + + public static boolean hasTouchHero(Tank t) { + if (Objects.isNull(instance) || Objects.isNull(instance.hero) || !instance.hero.isAlive()) { + return true; + } + + return TankTool.ablePass(t, instance.hero); + } } From 0946e84f6bc0c8e6c4b2fcafed8333a0a9c05607 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Tue, 21 Sep 2021 23:29:17 +0800 Subject: [PATCH 239/476] +) new panel --- .../github/kuangcp/tank/v2/MainPanelV2.java | 128 ++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 gui/src/main/java/com/github/kuangcp/tank/v2/MainPanelV2.java diff --git a/gui/src/main/java/com/github/kuangcp/tank/v2/MainPanelV2.java b/gui/src/main/java/com/github/kuangcp/tank/v2/MainPanelV2.java new file mode 100644 index 00000000..b539cbb3 --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/v2/MainPanelV2.java @@ -0,0 +1,128 @@ +package com.github.kuangcp.tank.v2; + +import com.github.kuangcp.tank.constant.DirectType; +import com.github.kuangcp.tank.domain.EnemyTank; +import com.github.kuangcp.tank.domain.Hero; +import com.github.kuangcp.tank.util.TankTool; +import com.github.kuangcp.tank.util.executor.LoopEventExecutor; +import lombok.extern.slf4j.Slf4j; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; +import java.util.Objects; +import java.util.Vector; +import java.util.concurrent.TimeUnit; + +/** + * @author https://github.com/kuangcp on 2021-09-11 23:15 + */ +@Slf4j +@SuppressWarnings("serial") +class MainPanelV2 extends JPanel implements KeyListener, Runnable { + + //定义我的坦克 + Hero hero = null; + //定义泛型的集合类 + Vector ets = new Vector<>(); + + //画板的构造函数 放图形的对象 + public MainPanelV2() { + ets.add(new EnemyTank(250, 60, 4, DirectType.UP)); + } + + //重写paint + public void paint(Graphics g) { + super.paint(g); + + if (Objects.isNull(hero)) { + hero = new Hero(20, 20, 4); + } + g.fillRect(0, 0, 500, 400); + + //调用函数绘画出主坦克 + hero.drawSelf(g); + + //画出子弹 + if (hero.bullet != null) { + g.draw3DRect(hero.bullet.sx, hero.bullet.sy, 1, 1, false); + } + + //画出敌人坦克 + for (EnemyTank s : ets) { + s.drawSelf(g); + LoopEventExecutor.addLoopEvent(s); + } + + //画出砖块 + g.setColor(new Color(188, 112, 50)); + for (int i = 0; i < 9; i++) { + g.fill3DRect(60, 60 + 10 * i, 20, 10, false); + } + + g.setColor(Color.yellow); + g.drawRect(0, 0, 400, 300); + } + + //当按下键盘上的键时监听者的处理函数 + public void keyPressed(KeyEvent e) { + //加了if条件后 实现了墙的限制(如果是游戏中的道具,该怎么办) + if (e.getKeyCode() == KeyEvent.VK_A) { + hero.setDirect(2); + if ((hero.getX() - 10) > 0) + hero.moveLeft(); + + } + if (e.getKeyCode() == KeyEvent.VK_D) { + hero.setDirect(3); + if ((hero.getX() + 15) < 405) + hero.moveRight(); + } + + if (e.getKeyCode() == KeyEvent.VK_W) { + hero.setDirect(0); + if ((hero.getY() - 13) > 0) + hero.moveUp(); + + } + if (e.getKeyCode() == KeyEvent.VK_S) { + hero.setDirect(1); + if ((hero.getY() - 15) < 275) + hero.moveDown(); + + } + //必须重新绘制窗口,不然上面的方法不能视觉上动起来 + this.repaint(); + } + + public void keyReleased(KeyEvent arg0) { + } + + public void keyTyped(KeyEvent arg0) { + } + + @Override + public void run() { + int maxFPS = 60; + long fpsTime = (long) ((1000.0 / maxFPS) * 1000000); + + long now; + //每隔100ms重绘 + while (true) { + now = System.nanoTime(); + + // 进行重绘 + this.repaint(); + final long waste = System.nanoTime() - now; + if (waste > fpsTime) { + continue; + } + + TankTool.yieldTime(fpsTime - waste, TimeUnit.NANOSECONDS); + if (Objects.nonNull(hero) && !hero.isAlive()) { + break; + } + } + } +} From 24967a7a5c77162c759f343ffeb7fa0453714ff8 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Tue, 21 Sep 2021 23:57:27 +0800 Subject: [PATCH 240/476] -) remove magic number --- .../github/kuangcp/tank/util/KeyListener.java | 16 ++++----- .../github/kuangcp/tank/v2/MainPanelV2.java | 2 +- .../github/kuangcp/tank/v3/PlayStageMgr.java | 24 +++++++++++++ .../github/kuangcp/tank/v3/StageBorder.java | 35 +++++++++++++++++++ 4 files changed, 66 insertions(+), 11 deletions(-) create mode 100644 gui/src/main/java/com/github/kuangcp/tank/v3/StageBorder.java diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/KeyListener.java b/gui/src/main/java/com/github/kuangcp/tank/util/KeyListener.java index 0093af40..1b4cb802 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/util/KeyListener.java +++ b/gui/src/main/java/com/github/kuangcp/tank/util/KeyListener.java @@ -43,28 +43,28 @@ public void run() { if (eventGroup.isLeft()) { hero.setDirect(DirectType.LEFT); - if ((hero.getX() - 10) > 20 && PlayStageMgr.instance.ableToMove(hero)) { + if (PlayStageMgr.instance.willInBorder(hero) && PlayStageMgr.instance.ableToMove(hero)) { hero.moveLeft(); } } if (eventGroup.isRight()) { hero.setDirect(DirectType.RIGHT); - if ((hero.getX() + 15) < 742 && PlayStageMgr.instance.ableToMove(hero)) { + if (PlayStageMgr.instance.willInBorder(hero) && PlayStageMgr.instance.ableToMove(hero)) { hero.moveRight(); } } if (eventGroup.isDown()) { hero.setDirect(DirectType.DOWN); - if ((hero.getY() - 15) < 515 && PlayStageMgr.instance.ableToMove(hero)) { + if (PlayStageMgr.instance.willInBorder(hero) && PlayStageMgr.instance.ableToMove(hero)) { hero.moveDown(); } } if (eventGroup.isUp()) { hero.setDirect(DirectType.UP); - if ((hero.getY() - 13) > 20 && PlayStageMgr.instance.ableToMove(hero)) { + if (PlayStageMgr.instance.willInBorder(hero) && PlayStageMgr.instance.ableToMove(hero)) { hero.moveUp(); } } @@ -75,12 +75,8 @@ public void run() { tankGroundPanel.repaint(); - try { - // 动作的延迟 1000 / 77 fps - Thread.sleep(29); - } catch (InterruptedException e) { - log.error("", e); - } + // 动作的延迟 1000 / 77 fps + TankTool.yieldMsTime(33); } } } diff --git a/gui/src/main/java/com/github/kuangcp/tank/v2/MainPanelV2.java b/gui/src/main/java/com/github/kuangcp/tank/v2/MainPanelV2.java index b539cbb3..fdfe304d 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v2/MainPanelV2.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v2/MainPanelV2.java @@ -105,7 +105,7 @@ public void keyTyped(KeyEvent arg0) { @Override public void run() { int maxFPS = 60; - long fpsTime = (long) ((1000.0 / maxFPS) * 1000000); + long fpsTime = (long) ((1000.0 / maxFPS) * 1_000_000); long now; //每隔100ms重绘 diff --git a/gui/src/main/java/com/github/kuangcp/tank/v3/PlayStageMgr.java b/gui/src/main/java/com/github/kuangcp/tank/v3/PlayStageMgr.java index 97426528..2ca53e81 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v3/PlayStageMgr.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v3/PlayStageMgr.java @@ -1,5 +1,6 @@ package com.github.kuangcp.tank.v3; +import com.github.kuangcp.tank.constant.DirectType; import com.github.kuangcp.tank.domain.Brick; import com.github.kuangcp.tank.domain.EnemyTank; import com.github.kuangcp.tank.domain.Hero; @@ -24,6 +25,7 @@ public class PlayStageMgr { public static PlayStageMgr instance = null; public Hero hero; + public StageBorder border = null; public boolean startLogic = false; public boolean winCurRound = false; public int roundPrize = 0; @@ -66,8 +68,13 @@ public void markStartLogic() { log.info("start round:{}", round); } + /** + * start new stage + */ public static void init(Hero hero, List enemyTanks, List bricks, List irons) { instance = new PlayStageMgr(hero, enemyTanks, bricks, irons); + + instance.border = new StageBorder(20, 742, 20, 545); } private PlayStageMgr(Hero hero, List enemyTanks, List bricks, List irons) { @@ -134,6 +141,23 @@ public boolean ableToMove(Hero hero) { return enemyTanks.stream().allMatch(v -> TankTool.ablePass(hero, v)); } + public boolean willInBorder(Tank tank) { + if (Objects.isNull(tank)) { + return false; + } + switch (tank.getDirect()) { + case DirectType.UP: + return tank.getY() - tank.getHalfHeight() - tank.getSpeed() > border.getMinY(); + case DirectType.DOWN: + return tank.getY() + tank.getHalfHeight() + tank.getSpeed() < border.getMaxY(); + case DirectType.LEFT: + return tank.getX() - tank.getHalfHeight() - tank.getSpeed() > border.getMinX(); + case DirectType.RIGHT: + return tank.getX() + tank.getHalfHeight() + tank.getSpeed() < border.getMaxX(); + } + return false; + } + public static int getEnemySize() { return enemySize; } diff --git a/gui/src/main/java/com/github/kuangcp/tank/v3/StageBorder.java b/gui/src/main/java/com/github/kuangcp/tank/v3/StageBorder.java new file mode 100644 index 00000000..c640c728 --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/v3/StageBorder.java @@ -0,0 +1,35 @@ +package com.github.kuangcp.tank.v3; + +/** + * @author https://github.com/kuangcp on 2021-09-21 23:35 + */ + +public class StageBorder { + private int minX; + private int maxX; + private int minY; + private int maxY; + + public StageBorder(int minX, int maxX, int minY, int maxY) { + this.minX = minX; + this.maxX = maxX; + this.minY = minY; + this.maxY = maxY; + } + + public int getMinX() { + return minX; + } + + public int getMaxX() { + return maxX; + } + + public int getMinY() { + return minY; + } + + public int getMaxY() { + return maxY; + } +} From 4ec6808f659d4cd10213fed02f62d86d987ed0f6 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Thu, 23 Sep 2021 01:28:49 +0800 Subject: [PATCH 241/476] +) simple enemey bot --- .../github/kuangcp/tank/domain/Bullet.java | 14 +- .../github/kuangcp/tank/domain/EnemyTank.java | 139 +++++++++++++++--- .../com/github/kuangcp/tank/domain/Tank.java | 3 +- .../kuangcp/tank/domain/VisualItem.java | 11 ++ .../tank/domain/robot/EnemyActionContext.java | 70 +++++++++ .../kuangcp/tank/domain/robot/RobotRate.java | 11 ++ .../tank/domain/robot/RoundActionEnum.java | 8 + .../kuangcp/tank/panel/StarterPanel.java | 2 +- .../util/executor/CommonEventExecutor.java | 7 +- .../github/kuangcp/tank/v2/MainPanelV2.java | 56 +++++-- .../github/kuangcp/tank/v2/TankGameV2.java | 16 +- .../github/kuangcp/tank/v3/PlayStageMgr.java | 2 +- .../github/kuangcp/tank/v3/StageBorder.java | 8 +- 13 files changed, 297 insertions(+), 50 deletions(-) create mode 100644 gui/src/main/java/com/github/kuangcp/tank/domain/VisualItem.java create mode 100644 gui/src/main/java/com/github/kuangcp/tank/domain/robot/EnemyActionContext.java create mode 100644 gui/src/main/java/com/github/kuangcp/tank/domain/robot/RobotRate.java create mode 100644 gui/src/main/java/com/github/kuangcp/tank/domain/robot/RoundActionEnum.java diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/Bullet.java b/gui/src/main/java/com/github/kuangcp/tank/domain/Bullet.java index a2deba6c..d3266c3c 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/domain/Bullet.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/Bullet.java @@ -5,11 +5,16 @@ import com.github.kuangcp.tank.v3.PlayStageMgr; import lombok.extern.slf4j.Slf4j; +import java.awt.*; + /** - * 子弹类 对象做成了线程 + * 子弹 + *

+ * TODO 对象池(弹夹) 复用对象 + * TODO 抽象类,多种子弹 */ @Slf4j -public class Bullet extends AbstractLoopEvent { +public class Bullet extends AbstractLoopEvent implements VisualItem { public int sx; public int sy; @@ -106,4 +111,9 @@ private void originRun() { } while (this.alive); // log.info("bullet die"); } + + @Override + public void drawSelf(Graphics g) { + g.draw3DRect(this.sx, this.sy, 1, 1, false); + } } diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java b/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java index 81d2ba10..a7c786d5 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java @@ -3,8 +3,11 @@ import com.github.kuangcp.tank.constant.DirectType; -import com.github.kuangcp.tank.util.executor.LoopEventExecutor; +import com.github.kuangcp.tank.domain.robot.EnemyActionContext; +import com.github.kuangcp.tank.domain.robot.RobotRate; +import com.github.kuangcp.tank.domain.robot.RoundActionEnum; import com.github.kuangcp.tank.util.TankTool; +import com.github.kuangcp.tank.util.executor.LoopEventExecutor; import com.github.kuangcp.tank.v3.PlayStageMgr; import lombok.extern.slf4j.Slf4j; @@ -22,7 +25,7 @@ * 可以继续延伸做出多样化的坦克 */ @Slf4j -public class EnemyTank extends Tank implements Runnable { +public class EnemyTank extends Tank implements Runnable, RobotRate { private static final AtomicLong counter = new AtomicLong(); // 敌人 id @@ -33,8 +36,12 @@ public class EnemyTank extends Tank implements Runnable { public List bulletList = Collections.synchronizedList(new ArrayList<>());//子弹集合 private long lastShotMs = 0; private long shotCDMs = 168; - public static int maxLiveShot = 30; //子弹线程存活的最大数 + + + public static int maxLiveShot = 30; // 活跃子弹 最大数 public boolean delayRemove = false; // 延迟回收内存,避免子弹线程执行中断 + public int moveRate; + public int shotRate; public List ets; public List bricks; @@ -58,6 +65,22 @@ public class EnemyTank extends Tank implements Runnable { colorMap.put(5, new Color(240, 57, 23)); } + public EnemyTank(int x, int y, int direct) { + super(x, y, 2); + type = 1; + this.direct = direct; + this.alive = true; + this.life = ThreadLocalRandom.current().nextInt(maxLife) + 1; + this.speed = maxLife - life + 1; + this.id = counter.addAndGet(1); + + this.setFixedDelayTime(60); + this.moveRate = 1; + this.shotRate = 17; + + this.afterBuild(); + } + //继承了属性,即使直接使用父类的构造器,构造器也一定要显式声明 public EnemyTank(int x, int y, int speed, int direct) { super(x, y, speed); @@ -67,12 +90,15 @@ public EnemyTank(int x, int y, int speed, int direct) { this.alive = true; this.life = ThreadLocalRandom.current().nextInt(maxLife) + 1; this.id = counter.addAndGet(1); -// log.info("create new Demons. {}", id); - //TODO 动机算法 调整后的刷新率 -// this.setFixedDelayTime(320); - this.setFixedDelayTime(90); + this.setFixedDelayTime(60); + this.moveRate = 1; + this.shotRate = 17; + + this.afterBuild(); + } + private void afterBuild() { this.registerHook(() -> { if (this.isAbort()) { for (Bullet d : this.bulletList) { @@ -84,6 +110,16 @@ public EnemyTank(int x, int y, int speed, int direct) { }); } + @Override + public int getMoveRate() { + return this.moveRate; + } + + @Override + public int getShotRate() { + return this.shotRate; + } + @Override public void drawSelf(Graphics g) { g.setColor(colorMap.getOrDefault(this.life, Color.cyan)); @@ -122,13 +158,13 @@ public void setBri(boolean bri) { /** * 发射子弹 函数 */ - public void shotEnemy() { + public void finalShotAction() { //判断坦克方向来 初始化子弹的起始发射位置 final long nowMs = System.currentTimeMillis(); if (lastShotMs != 0 && nowMs - lastShotMs < shotCDMs) { return; } - if (this.bulletList.size() >= this.maxLiveShot || !this.isAlive()) { + if (this.bulletList.size() >= maxLiveShot || !this.isAlive()) { return; } @@ -457,7 +493,8 @@ public boolean toRight() { @Override public void run() { - newEventRun(); +// newEventRun(); + moveOrShot(); } // 运动 @@ -501,9 +538,63 @@ public void actionModeRun() { } } - // 攻击 - public void actionModeAttack() { + private final EnemyActionContext actionContext = new EnemyActionContext(); + + public void moveOrShot() { + if (!this.isAlive()) { + this.stop(); + return; + } + + if (PlayStageMgr.pause || this.speed <= 0) { + return; + } + + final RoundActionEnum roundActionEnum = actionContext.roundAction(this); +// log.info("mode={}", roundActionEnum); + switch (roundActionEnum) { + case MOVE: + this.finalMoveAction(); + return; + case SHOT: + this.finalShotAction(); + return; + case STAY: + return; + default: + log.warn("not support"); + } + } + + private void finalMoveAction() { + if (actionContext.getSameDirectCounter() > actionContext.getCurRoundStep() + || !PlayStageMgr.instance.willInBorder(this)) { + this.direct = (int) (Math.random() * 4); + actionContext.reset(); + return; + } + switch (this.direct) { + case DirectType.UP: + y -= this.speed; + actionContext.addCount(); + break; + case DirectType.DOWN: + y += this.speed; + actionContext.addCount(); + break; + case DirectType.LEFT: + x -= this.speed; + actionContext.addCount(); + break; + case DirectType.RIGHT: + x += this.speed; + actionContext.addCount(); + break; + default: + log.warn("not exist direct"); + break; + } } public void run2() { @@ -531,7 +622,7 @@ public void run2() { else break;//这样才不会有敌人坦克凑在你附近不动 // else this.direct = (int)(Math.random()*4); if (min % 27 == 0) - this.shotEnemy(); + this.finalShotAction(); TankTool.yieldMsTime(50); } @@ -550,7 +641,7 @@ public void run2() { // else continue; else break; if (min % 27 == 0) { - this.shotEnemy(); + this.finalShotAction(); } TankTool.yieldMsTime(50); } @@ -570,7 +661,7 @@ public void run2() { // else continue; else break; if (min % 27 == 0) - this.shotEnemy(); + this.finalShotAction(); TankTool.yieldMsTime(50); } @@ -591,7 +682,7 @@ public void run2() { else break; // else continue; if (min % 27 == 0) - this.shotEnemy(); + this.finalShotAction(); TankTool.yieldMsTime(50); } @@ -641,14 +732,14 @@ public void newEventRun() { min++; // if(!bri)this.direct = (int)(Math.random()*4); if (y > 30) { - if (PlayStageMgr.hasTouchHero(this) && overlap) y -= speed; + if (PlayStageMgr.ablePassByHero(this) && overlap) y -= speed; // if(bri)y-=speed; // else {y+=speed;this.direct = 1;} // else continue; else break;//这样才不会有敌人坦克凑在你附近不动 // else this.direct = (int)(Math.random()*4); if (min % 27 == 0) - this.shotEnemy(); + this.finalShotAction(); // TankTool.yieldMsTime(50); } @@ -661,13 +752,13 @@ public void newEventRun() { // if(!bri)this.direct = (int)(Math.random()*4); if (y < 530) { - if (PlayStageMgr.hasTouchHero(this) && overlap && bri) y += speed; + if (PlayStageMgr.ablePassByHero(this) && overlap && bri) y += speed; // if(bri) y+=speed; // else {y-=speed;this.direct = 0;} // else continue; else break; if (min % 27 == 0) { - this.shotEnemy(); + this.finalShotAction(); } // TankTool.yieldMsTime(50); } @@ -681,13 +772,13 @@ public void newEventRun() { // if(!bri)this.direct = (int)(Math.random()*4); if (x > 30) { - if (PlayStageMgr.hasTouchHero(this) && overlap && bri) x -= speed; + if (PlayStageMgr.ablePassByHero(this) && overlap && bri) x -= speed; // if(bri)x-=speed; // else{x+=speed;this.direct = 3;} // else continue; else break; if (min % 27 == 0) - this.shotEnemy(); + this.finalShotAction(); // TankTool.yieldMsTime(50); } @@ -700,7 +791,7 @@ public void newEventRun() { // if(!bri)this.direct = (int)(Math.random()*4); if (x < 710) { - if (PlayStageMgr.hasTouchHero(this) && overlap && bri) { + if (PlayStageMgr.ablePassByHero(this) && overlap && bri) { x += speed; } // if(bri)x+=speed; @@ -708,7 +799,7 @@ public void newEventRun() { else break; // else continue; if (min % 27 == 0) - this.shotEnemy(); + this.finalShotAction(); // TankTool.yieldMsTime(50); } diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/Tank.java b/gui/src/main/java/com/github/kuangcp/tank/domain/Tank.java index b1bd19fc..81ed4607 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/domain/Tank.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/Tank.java @@ -11,7 +11,7 @@ * 往后有继承 */ @Slf4j -public abstract class Tank extends AbstractLoopEvent { +public abstract class Tank extends AbstractLoopEvent implements VisualItem { int x; // 坦克中心的横坐标 int y; // 坦克中心的纵坐标 int direct = 0; // 初始方向 @@ -108,6 +108,7 @@ public void setY(int y) { this.y = y; } + public Tank(int x, int y, int speed) { this.x = x; this.y = y; diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/VisualItem.java b/gui/src/main/java/com/github/kuangcp/tank/domain/VisualItem.java new file mode 100644 index 00000000..076ccb12 --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/VisualItem.java @@ -0,0 +1,11 @@ +package com.github.kuangcp.tank.domain; + +import java.awt.*; + +/** + * @author https://github.com/kuangcp on 2021-09-23 00:57 + */ +public interface VisualItem { + + void drawSelf(Graphics g); +} diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/robot/EnemyActionContext.java b/gui/src/main/java/com/github/kuangcp/tank/domain/robot/EnemyActionContext.java new file mode 100644 index 00000000..52e7328d --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/robot/EnemyActionContext.java @@ -0,0 +1,70 @@ +package com.github.kuangcp.tank.domain.robot; + +import java.util.concurrent.ThreadLocalRandom; + +/** + * @author https://github.com/kuangcp on 2021-09-22 23:56 + */ +public class EnemyActionContext { + + public static final int MAX_DIRECT_STEP = 40; + public static final int MIN_DIRECT_STEP = 3; + + private long lastShotTime; + private long round; + private int curRoundStep = 3; + + private int sameDirectCounter = 0; + + public int getSameDirectCounter() { + return sameDirectCounter; + } + + public long getRound() { + return round; + } + + public void addRound() { + this.round += 1; + } + + private long getAndAddCount() { + final long origin = this.round; + this.round += 1; + return origin; + } + + public RoundActionEnum roundAction(RobotRate rate) { + final long round = this.getAndAddCount(); + if (round % rate.getMoveRate() == rate.getMoveRate() - 1) { + return RoundActionEnum.MOVE; + } else if (round % rate.getShotRate() == rate.getShotRate() - 1) { + return RoundActionEnum.SHOT; + } else { + return RoundActionEnum.STAY; + } + } + + public boolean isShotRound() { + return true; + } + + public void addCount() { + this.sameDirectCounter += 1; + } + + public void reset() { + this.sameDirectCounter = 0; + final ThreadLocalRandom random = ThreadLocalRandom.current(); + final int curRoundStep = random.nextInt(EnemyActionContext.MIN_DIRECT_STEP, EnemyActionContext.MAX_DIRECT_STEP); + this.setCurRoundStep(curRoundStep); + } + + public int getCurRoundStep() { + return curRoundStep; + } + + public void setCurRoundStep(int curRoundStep) { + this.curRoundStep = curRoundStep; + } +} diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/robot/RobotRate.java b/gui/src/main/java/com/github/kuangcp/tank/domain/robot/RobotRate.java new file mode 100644 index 00000000..e5672aef --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/robot/RobotRate.java @@ -0,0 +1,11 @@ +package com.github.kuangcp.tank.domain.robot; + +/** + * @author https://github.com/kuangcp on 2021-09-23 00:45 + */ +public interface RobotRate { + + int getMoveRate(); + + int getShotRate(); +} diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/robot/RoundActionEnum.java b/gui/src/main/java/com/github/kuangcp/tank/domain/robot/RoundActionEnum.java new file mode 100644 index 00000000..db1c9f9d --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/robot/RoundActionEnum.java @@ -0,0 +1,8 @@ +package com.github.kuangcp.tank.domain.robot; + +/** + * @author https://github.com/kuangcp on 2021-09-23 00:14 + */ +public enum RoundActionEnum { + MOVE, SHOT, STAY +} diff --git a/gui/src/main/java/com/github/kuangcp/tank/panel/StarterPanel.java b/gui/src/main/java/com/github/kuangcp/tank/panel/StarterPanel.java index 79d96b41..046a1e27 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/panel/StarterPanel.java +++ b/gui/src/main/java/com/github/kuangcp/tank/panel/StarterPanel.java @@ -20,7 +20,7 @@ public class StarterPanel extends JPanel { public StarterPanel() { try { - First = ImageIO.read(getClass().getResource("/images/Tank.jpg")); + First = ImageIO.read(getClass().getResource("/tank/img/Tank.jpg")); } catch (IOException e) { log.error("", e); } diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/executor/CommonEventExecutor.java b/gui/src/main/java/com/github/kuangcp/tank/util/executor/CommonEventExecutor.java index 9c29ca9a..5da92570 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/util/executor/CommonEventExecutor.java +++ b/gui/src/main/java/com/github/kuangcp/tank/util/executor/CommonEventExecutor.java @@ -19,9 +19,14 @@ public static void loopEventSpin(BlockingQueue queue) { final AbstractLoopEvent event = queue.take(); final long delay = event.getDelay(TimeUnit.MILLISECONDS); if (delay < -200) { - log.info("delay {}", delay); + log.info("delay {}ms", delay); } + final long start = System.nanoTime(); event.run(); + final long taskWaste = System.nanoTime() - start; + if (taskWaste > event.fixedDelayTime * 1000_000) { + log.warn("task run out of fixed delay. waste:{}ns, fixed:{}ms", taskWaste, event.fixedDelayTime); + } if (event.isContinue() && event.addFixedDelay()) { queue.add(event); } diff --git a/gui/src/main/java/com/github/kuangcp/tank/v2/MainPanelV2.java b/gui/src/main/java/com/github/kuangcp/tank/v2/MainPanelV2.java index fdfe304d..b402cad8 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v2/MainPanelV2.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v2/MainPanelV2.java @@ -1,16 +1,20 @@ package com.github.kuangcp.tank.v2; import com.github.kuangcp.tank.constant.DirectType; +import com.github.kuangcp.tank.domain.Bullet; import com.github.kuangcp.tank.domain.EnemyTank; import com.github.kuangcp.tank.domain.Hero; import com.github.kuangcp.tank.util.TankTool; import com.github.kuangcp.tank.util.executor.LoopEventExecutor; +import com.github.kuangcp.tank.v3.PlayStageMgr; +import com.github.kuangcp.tank.v3.StageBorder; import lombok.extern.slf4j.Slf4j; import javax.swing.*; import java.awt.*; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; +import java.util.Iterator; import java.util.Objects; import java.util.Vector; import java.util.concurrent.TimeUnit; @@ -23,23 +27,36 @@ class MainPanelV2 extends JPanel implements KeyListener, Runnable { //定义我的坦克 - Hero hero = null; + Hero hero; //定义泛型的集合类 Vector ets = new Vector<>(); //画板的构造函数 放图形的对象 public MainPanelV2() { - ets.add(new EnemyTank(250, 60, 4, DirectType.UP)); +// ets.add(new EnemyTank(250, 60, 1, DirectType.UP)); +// ets.add(new EnemyTank(250, 60, 2, DirectType.UP)); +// ets.add(new EnemyTank(250, 60, 3, DirectType.UP)); +// ets.add(new EnemyTank(250, 260, 4, DirectType.UP)); +// ets.add(new EnemyTank(250, 260, 6, DirectType.UP)); + + ets.add(new EnemyTank(250, 60, DirectType.UP)); + ets.add(new EnemyTank(250, 70, DirectType.UP)); + ets.add(new EnemyTank(250, 80, DirectType.UP)); + ets.add(new EnemyTank(250, 90, DirectType.UP)); + ets.add(new EnemyTank(250, 100, DirectType.UP)); + + for (EnemyTank et : ets) { + LoopEventExecutor.addLoopEvent(et); + } + hero = new Hero(160, 160, 4); } //重写paint public void paint(Graphics g) { super.paint(g); - if (Objects.isNull(hero)) { - hero = new Hero(20, 20, 4); - } - g.fillRect(0, 0, 500, 400); + final StageBorder border = PlayStageMgr.instance.border; + g.fillRect(0, 0, border.getMaxX() + border.getMinX(), border.getMaxY() + border.getMinY()); //调用函数绘画出主坦克 hero.drawSelf(g); @@ -52,7 +69,20 @@ public void paint(Graphics g) { //画出敌人坦克 for (EnemyTank s : ets) { s.drawSelf(g); - LoopEventExecutor.addLoopEvent(s); + final Iterator iterator = s.bulletList.iterator(); + while (iterator.hasNext()) { + final Bullet next = iterator.next(); + if (!next.alive) { + iterator.remove(); + } + g.setColor(Color.cyan); + next.drawSelf(g); + } + for (Bullet bullet : s.bulletList) { + g.setColor(Color.cyan); + bullet.drawSelf(g); + } + } //画出砖块 @@ -61,8 +91,10 @@ public void paint(Graphics g) { g.fill3DRect(60, 60 + 10 * i, 20, 10, false); } + // border g.setColor(Color.yellow); - g.drawRect(0, 0, 400, 300); + g.drawRect(border.getMinX(), border.getMinY(), + border.getMaxX() - border.getMinX(), border.getMaxY() - border.getMinY()); } //当按下键盘上的键时监听者的处理函数 @@ -70,25 +102,25 @@ public void keyPressed(KeyEvent e) { //加了if条件后 实现了墙的限制(如果是游戏中的道具,该怎么办) if (e.getKeyCode() == KeyEvent.VK_A) { hero.setDirect(2); - if ((hero.getX() - 10) > 0) + if (PlayStageMgr.instance.willInBorder(hero)) hero.moveLeft(); } if (e.getKeyCode() == KeyEvent.VK_D) { hero.setDirect(3); - if ((hero.getX() + 15) < 405) + if (PlayStageMgr.instance.willInBorder(hero)) hero.moveRight(); } if (e.getKeyCode() == KeyEvent.VK_W) { hero.setDirect(0); - if ((hero.getY() - 13) > 0) + if (PlayStageMgr.instance.willInBorder(hero)) hero.moveUp(); } if (e.getKeyCode() == KeyEvent.VK_S) { hero.setDirect(1); - if ((hero.getY() - 15) < 275) + if (PlayStageMgr.instance.willInBorder(hero)) hero.moveDown(); } diff --git a/gui/src/main/java/com/github/kuangcp/tank/v2/TankGameV2.java b/gui/src/main/java/com/github/kuangcp/tank/v2/TankGameV2.java index 24a79c8a..e3bb6a07 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v2/TankGameV2.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v2/TankGameV2.java @@ -1,8 +1,12 @@ package com.github.kuangcp.tank.v2; +import com.github.kuangcp.tank.domain.Hero; import com.github.kuangcp.tank.util.executor.MonitorExecutor; +import com.github.kuangcp.tank.v3.PlayStageMgr; +import com.github.kuangcp.tank.v3.StageBorder; import javax.swing.*; +import java.util.Collections; /** * 1 绘画出坦克 并且能做到对它的移动操作 @@ -23,13 +27,17 @@ public TankGameV2() { this.add(panel); this.addKeyListener(panel); - this.setLocation(900, 200); - this.setSize(405, 333); - this.setVisible(true); - MonitorExecutor.init(); + PlayStageMgr.init(new Hero(50, 20, 5), Collections.emptyList(), Collections.emptyList(), Collections.emptyList()); final Thread panelThread = new Thread(panel); panelThread.start(); + + final StageBorder border = PlayStageMgr.instance.border; + + this.setLocation(900, 200); + this.setSize(border.getMaxX() + border.getMinX(), border.getMaxY() + border.getMinY() * 2); + this.setVisible(true); + this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } } \ No newline at end of file diff --git a/gui/src/main/java/com/github/kuangcp/tank/v3/PlayStageMgr.java b/gui/src/main/java/com/github/kuangcp/tank/v3/PlayStageMgr.java index 2ca53e81..8332b33c 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v3/PlayStageMgr.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v3/PlayStageMgr.java @@ -174,7 +174,7 @@ public static long getInvincibleMs() { return invincibleMs; } - public static boolean hasTouchHero(Tank t) { + public static boolean ablePassByHero(Tank t) { if (Objects.isNull(instance) || Objects.isNull(instance.hero) || !instance.hero.isAlive()) { return true; } diff --git a/gui/src/main/java/com/github/kuangcp/tank/v3/StageBorder.java b/gui/src/main/java/com/github/kuangcp/tank/v3/StageBorder.java index c640c728..c3b5c33a 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v3/StageBorder.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v3/StageBorder.java @@ -5,10 +5,10 @@ */ public class StageBorder { - private int minX; - private int maxX; - private int minY; - private int maxY; + private final int minX; + private final int maxX; + private final int minY; + private final int maxY; public StageBorder(int minX, int maxX, int minY, int maxY) { this.minX = minX; From 58035a6e51212bf2ccfc29609ddfe527e5447848 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Thu, 23 Sep 2021 02:01:14 +0800 Subject: [PATCH 242/476] +) speed up --- .../kuangcp/tank/constant/DirectType.java | 20 +++++++++++++++++++ .../github/kuangcp/tank/domain/Bullet.java | 14 +++++-------- .../github/kuangcp/tank/domain/EnemyTank.java | 16 ++++++++++----- .../tank/domain/robot/EnemyActionContext.java | 10 ++++++---- .../github/kuangcp/tank/v2/MainPanelV2.java | 9 ++++----- .../github/kuangcp/tank/v2/TankGameV2.java | 8 ++++---- .../github/kuangcp/tank/v3/PlayStageMgr.java | 10 ++++++++++ 7 files changed, 60 insertions(+), 27 deletions(-) diff --git a/gui/src/main/java/com/github/kuangcp/tank/constant/DirectType.java b/gui/src/main/java/com/github/kuangcp/tank/constant/DirectType.java index 8032fe9f..ab1418a9 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/constant/DirectType.java +++ b/gui/src/main/java/com/github/kuangcp/tank/constant/DirectType.java @@ -8,4 +8,24 @@ public interface DirectType { int DOWN = 1; int LEFT = 2; int RIGHT = 3; + + int[] UP_SELECT = new int[]{UP, LEFT, RIGHT}; + int[] DOWN_SELECT = new int[]{DOWN, LEFT, RIGHT}; + int[] LEFT_SELECT = new int[]{UP, DOWN, LEFT}; + int[] RIGHT_SELECT = new int[]{UP, DOWN, RIGHT}; + + static int[] turnSelection(int direct) { + switch (direct) { + case UP: + return UP_SELECT; + case DOWN: + return DOWN_SELECT; + case LEFT: + return LEFT_SELECT; + case RIGHT: + return RIGHT_SELECT; + default: + return UP_SELECT; + } + } } diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/Bullet.java b/gui/src/main/java/com/github/kuangcp/tank/domain/Bullet.java index d3266c3c..8cf30270 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/domain/Bullet.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/Bullet.java @@ -1,7 +1,7 @@ package com.github.kuangcp.tank.domain; -import com.github.kuangcp.tank.util.executor.AbstractLoopEvent; import com.github.kuangcp.tank.util.TankTool; +import com.github.kuangcp.tank.util.executor.AbstractLoopEvent; import com.github.kuangcp.tank.v3.PlayStageMgr; import lombok.extern.slf4j.Slf4j; @@ -10,7 +10,7 @@ /** * 子弹 *

- * TODO 对象池(弹夹) 复用对象 + * TODO 对象池(弹夹) 复用对象 * TODO 抽象类,多种子弹 */ @Slf4j @@ -66,13 +66,9 @@ private void newRun() { break; } - //判断子弹是否碰到边缘 - if (sx < 20 || sx > 740 || sy < 20 || sy > 540) { - this.alive = false; - this.stop(); - } - - if (sx < 440 && sx > 380 && sy < 540 && sy > 480) { + final boolean hitHome = sx < 440 && sx > 380 && sy < 540 && sy > 480; + //判断子弹是否碰到边缘 或者命中基地 + if (PlayStageMgr.instance.willInBorder(this) || hitHome) { this.alive = false; this.stop(); } diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java b/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java index a7c786d5..6caed026 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java @@ -74,8 +74,8 @@ public EnemyTank(int x, int y, int direct) { this.speed = maxLife - life + 1; this.id = counter.addAndGet(1); - this.setFixedDelayTime(60); - this.moveRate = 1; + this.setFixedDelayTime(40); + this.moveRate = 2; this.shotRate = 17; this.afterBuild(); @@ -91,8 +91,8 @@ public EnemyTank(int x, int y, int speed, int direct) { this.life = ThreadLocalRandom.current().nextInt(maxLife) + 1; this.id = counter.addAndGet(1); - this.setFixedDelayTime(60); - this.moveRate = 1; + this.setFixedDelayTime(40); + this.moveRate = 2; this.shotRate = 17; this.afterBuild(); @@ -148,6 +148,12 @@ public void setBri(boolean bri) { this.bri = bri; } + @Override + public void addLife(int delta) { + super.addLife(delta); + this.speed = maxLife - this.life + 1; + } + /** * 视频上的思想是 在panel上写一个工具函数 形参是 子弹和坦克 * 把函数放在 Run函数内跑 @@ -569,7 +575,7 @@ public void moveOrShot() { private void finalMoveAction() { if (actionContext.getSameDirectCounter() > actionContext.getCurRoundStep() || !PlayStageMgr.instance.willInBorder(this)) { - this.direct = (int) (Math.random() * 4); + this.direct = DirectType.turnSelection(this.direct)[ThreadLocalRandom.current().nextInt(3)]; actionContext.reset(); return; } diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/robot/EnemyActionContext.java b/gui/src/main/java/com/github/kuangcp/tank/domain/robot/EnemyActionContext.java index 52e7328d..b6e3ef33 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/domain/robot/EnemyActionContext.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/robot/EnemyActionContext.java @@ -7,7 +7,7 @@ */ public class EnemyActionContext { - public static final int MAX_DIRECT_STEP = 40; + public static final int MAX_DIRECT_STEP = 20; public static final int MIN_DIRECT_STEP = 3; private long lastShotTime; @@ -38,11 +38,13 @@ public RoundActionEnum roundAction(RobotRate rate) { final long round = this.getAndAddCount(); if (round % rate.getMoveRate() == rate.getMoveRate() - 1) { return RoundActionEnum.MOVE; - } else if (round % rate.getShotRate() == rate.getShotRate() - 1) { + } + + if (round % rate.getShotRate() == rate.getShotRate() - 1) { return RoundActionEnum.SHOT; - } else { - return RoundActionEnum.STAY; } + + return RoundActionEnum.STAY; } public boolean isShotRound() { diff --git a/gui/src/main/java/com/github/kuangcp/tank/v2/MainPanelV2.java b/gui/src/main/java/com/github/kuangcp/tank/v2/MainPanelV2.java index b402cad8..c67d1623 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v2/MainPanelV2.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v2/MainPanelV2.java @@ -45,6 +45,10 @@ public MainPanelV2() { ets.add(new EnemyTank(250, 90, DirectType.UP)); ets.add(new EnemyTank(250, 100, DirectType.UP)); + for (int i = 0; i < 700; i++) { + ets.add(new EnemyTank(20 + i * 40 % PlayStageMgr.instance.border.getMaxX(), 100 + i * 5 % PlayStageMgr.instance.border.getMaxY(), DirectType.UP)); + } + for (EnemyTank et : ets) { LoopEventExecutor.addLoopEvent(et); } @@ -78,11 +82,6 @@ public void paint(Graphics g) { g.setColor(Color.cyan); next.drawSelf(g); } - for (Bullet bullet : s.bulletList) { - g.setColor(Color.cyan); - bullet.drawSelf(g); - } - } //画出砖块 diff --git a/gui/src/main/java/com/github/kuangcp/tank/v2/TankGameV2.java b/gui/src/main/java/com/github/kuangcp/tank/v2/TankGameV2.java index e3bb6a07..e5b29552 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v2/TankGameV2.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v2/TankGameV2.java @@ -23,18 +23,18 @@ public static void main(String[] args) { //最外层JFrame的构造函数 public TankGameV2() { + MonitorExecutor.init(); + PlayStageMgr.init(new Hero(50, 20, 5), Collections.emptyList(), Collections.emptyList(), Collections.emptyList()); + MainPanelV2 panel = new MainPanelV2(); this.add(panel); this.addKeyListener(panel); - - MonitorExecutor.init(); - PlayStageMgr.init(new Hero(50, 20, 5), Collections.emptyList(), Collections.emptyList(), Collections.emptyList()); final Thread panelThread = new Thread(panel); panelThread.start(); final StageBorder border = PlayStageMgr.instance.border; - this.setLocation(900, 200); + this.setLocation(200, 50); this.setSize(border.getMaxX() + border.getMinX(), border.getMaxY() + border.getMinY() * 2); this.setVisible(true); diff --git a/gui/src/main/java/com/github/kuangcp/tank/v3/PlayStageMgr.java b/gui/src/main/java/com/github/kuangcp/tank/v3/PlayStageMgr.java index 8332b33c..ce7fdc56 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v3/PlayStageMgr.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v3/PlayStageMgr.java @@ -2,6 +2,7 @@ import com.github.kuangcp.tank.constant.DirectType; import com.github.kuangcp.tank.domain.Brick; +import com.github.kuangcp.tank.domain.Bullet; import com.github.kuangcp.tank.domain.EnemyTank; import com.github.kuangcp.tank.domain.Hero; import com.github.kuangcp.tank.domain.Iron; @@ -75,6 +76,7 @@ public static void init(Hero hero, List enemyTanks, List brick instance = new PlayStageMgr(hero, enemyTanks, bricks, irons); instance.border = new StageBorder(20, 742, 20, 545); +// instance.border = new StageBorder(20, 1600, 20, 900); } private PlayStageMgr(Hero hero, List enemyTanks, List bricks, List irons) { @@ -158,6 +160,14 @@ public boolean willInBorder(Tank tank) { return false; } + public boolean willInBorder(Bullet bullet) { + if (Objects.isNull(bullet)) { + return false; + } + return bullet.sx <= border.getMinX() || bullet.sx >= border.getMaxX() + || bullet.sy <= border.getMinY() || bullet.sy >= border.getMaxY(); + } + public static int getEnemySize() { return enemySize; } From 69c8c52646f13f34f220a24f9e8e8192b22e63d0 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Sat, 25 Sep 2021 16:05:53 +0800 Subject: [PATCH 243/476] *) rename --- .../github/kuangcp/tank/domain/EnemyTank.java | 47 +++++++++---------- .../util/executor/CommonEventExecutor.java | 15 ++++-- .../tank/util/LoopEventExecutorTest.java | 12 +++++ 3 files changed, 43 insertions(+), 31 deletions(-) diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java b/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java index 6caed026..54a6623d 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java @@ -28,9 +28,7 @@ public class EnemyTank extends Tank implements Runnable, RobotRate { private static final AtomicLong counter = new AtomicLong(); - // 敌人 id - public long id; - + public final long id; public Bullet s = null; public List bulletList = Collections.synchronizedList(new ArrayList<>());//子弹集合 @@ -38,7 +36,7 @@ public class EnemyTank extends Tank implements Runnable, RobotRate { private long shotCDMs = 168; - public static int maxLiveShot = 30; // 活跃子弹 最大数 + public static int maxLiveShot = 3; // 活跃子弹 最大数 public boolean delayRemove = false; // 延迟回收内存,避免子弹线程执行中断 public int moveRate; public int shotRate; @@ -52,26 +50,31 @@ public class EnemyTank extends Tank implements Runnable, RobotRate { boolean ableMove = true; /** - * 随机生成 的 最大生命值 + * 随机生成 的 最大生命值, 每个生命值都有对应的颜色 + * + * @see EnemyTank#COLOR_HASH_MAP + * @see EnemyTank#drawSelf */ - private static final int maxLife = 5; - private static final Map colorMap = new HashMap<>(maxLife); + private static final int MAX_LIFE = 7; + private static final Map COLOR_HASH_MAP = new HashMap<>(MAX_LIFE); static { - colorMap.put(1, Color.WHITE); - colorMap.put(2, new Color(93, 217, 41)); - colorMap.put(3, new Color(34, 155, 234)); - colorMap.put(4, new Color(155, 62, 202)); - colorMap.put(5, new Color(240, 57, 23)); + COLOR_HASH_MAP.put(1, Color.WHITE); + COLOR_HASH_MAP.put(2, new Color(93, 217, 41)); + COLOR_HASH_MAP.put(3, new Color(34, 155, 234)); + COLOR_HASH_MAP.put(4, new Color(155, 62, 202)); + COLOR_HASH_MAP.put(5, new Color(240, 57, 23)); + COLOR_HASH_MAP.put(6, new Color(240, 57, 23)); + COLOR_HASH_MAP.put(7, new Color(240, 57, 23)); } public EnemyTank(int x, int y, int direct) { - super(x, y, 2); + super(x, y, 0); type = 1; this.direct = direct; this.alive = true; - this.life = ThreadLocalRandom.current().nextInt(maxLife) + 1; - this.speed = maxLife - life + 1; + this.life = ThreadLocalRandom.current().nextInt(MAX_LIFE) + 1; + this.speed = MAX_LIFE - life + 1; this.id = counter.addAndGet(1); this.setFixedDelayTime(40); @@ -81,14 +84,13 @@ public EnemyTank(int x, int y, int direct) { this.afterBuild(); } - //继承了属性,即使直接使用父类的构造器,构造器也一定要显式声明 public EnemyTank(int x, int y, int speed, int direct) { super(x, y, speed); type = 1; this.direct = direct; this.speed = speed; this.alive = true; - this.life = ThreadLocalRandom.current().nextInt(maxLife) + 1; + this.life = ThreadLocalRandom.current().nextInt(MAX_LIFE) + 1; this.id = counter.addAndGet(1); this.setFixedDelayTime(40); @@ -122,7 +124,7 @@ public int getShotRate() { @Override public void drawSelf(Graphics g) { - g.setColor(colorMap.getOrDefault(this.life, Color.cyan)); + g.setColor(COLOR_HASH_MAP.getOrDefault(this.life, Color.white)); super.drawSelf(g); } @@ -151,16 +153,9 @@ public void setBri(boolean bri) { @Override public void addLife(int delta) { super.addLife(delta); - this.speed = maxLife - this.life + 1; + this.speed = MAX_LIFE - this.life + 1; } - /** - * 视频上的思想是 在panel上写一个工具函数 形参是 子弹和坦克 - * 把函数放在 Run函数内跑 - * 遮掩更显得逻辑性强一些,代码复用率高一点 - * @param hero - */ - /** * 发射子弹 函数 */ diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/executor/CommonEventExecutor.java b/gui/src/main/java/com/github/kuangcp/tank/util/executor/CommonEventExecutor.java index 5da92570..005b1abe 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/util/executor/CommonEventExecutor.java +++ b/gui/src/main/java/com/github/kuangcp/tank/util/executor/CommonEventExecutor.java @@ -6,19 +6,21 @@ import java.util.concurrent.TimeUnit; /** - * TODO 消费任务时,延迟情况告警 - * * @author https://github.com/kuangcp on 2021-09-17 01:06 */ @Slf4j public class CommonEventExecutor { + /** + * 事件循环任务 + */ public static void loopEventSpin(BlockingQueue queue) { while (true) { try { final AbstractLoopEvent event = queue.take(); final long delay = event.getDelay(TimeUnit.MILLISECONDS); - if (delay < -200) { + // 事件延迟调度警告,且过滤掉初次运行任务 + if (delay < -200 && delay > -1000_000) { log.info("delay {}ms", delay); } final long start = System.nanoTime(); @@ -31,19 +33,22 @@ public static void loopEventSpin(BlockingQueue queue) { queue.add(event); } } catch (InterruptedException e) { - log.error("", e); + log.error("invoke loop event error", e); break; } } } + /** + * 单次延迟任务 + */ public static void delayEventSpin(BlockingQueue queue) { while (true) { try { final AbstractDelayEvent event = queue.take(); event.run(); } catch (InterruptedException e) { - log.error("", e); + log.error("invoke delay event error", e); break; } } diff --git a/gui/src/test/java/com/github/kuangcp/tank/util/LoopEventExecutorTest.java b/gui/src/test/java/com/github/kuangcp/tank/util/LoopEventExecutorTest.java index 6c02d99b..ec7ddb62 100644 --- a/gui/src/test/java/com/github/kuangcp/tank/util/LoopEventExecutorTest.java +++ b/gui/src/test/java/com/github/kuangcp/tank/util/LoopEventExecutorTest.java @@ -15,6 +15,8 @@ import java.util.concurrent.BlockingQueue; import java.util.concurrent.DelayQueue; import java.util.concurrent.Delayed; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; @@ -165,4 +167,14 @@ public void testEnemyTank() throws Exception { Thread.sleep(1000000); } + @Test + public void testSchedule() throws Exception { + final EnemyTank enemyTank = new EnemyTank(30, 30, 2, DirectType.RIGHT); + final ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5); + scheduledExecutorService.scheduleAtFixedRate(()->{ + System.out.println("run"); + }, 200, 1000, TimeUnit.MILLISECONDS); +// scheduledExecutorService.scheduleWithFixedDelay(enemyTank, 2000, 1000, TimeUnit.MILLISECONDS); + Thread.sleep(1000000); + } } \ No newline at end of file From 8c562348ca38d08cf1f05070a0e1f082757135b5 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Sat, 25 Sep 2021 22:46:08 +0800 Subject: [PATCH 244/476] -) remove repaint --- gui/src/main/java/com/github/kuangcp/tank/util/KeyListener.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/KeyListener.java b/gui/src/main/java/com/github/kuangcp/tank/util/KeyListener.java index 1b4cb802..8526fc4e 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/util/KeyListener.java +++ b/gui/src/main/java/com/github/kuangcp/tank/util/KeyListener.java @@ -73,8 +73,6 @@ public void run() { hero.shotEnemy(); } - tankGroundPanel.repaint(); - // 动作的延迟 1000 / 77 fps TankTool.yieldMsTime(33); } From fc289c1e00303959949eaa03d829896418a51e23 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Thu, 30 Sep 2021 07:16:30 +0800 Subject: [PATCH 245/476] *) comment --- .../github/kuangcp/tank/panel/HeroInfoPanel.java | 13 ++++++------- .../com/github/kuangcp/tank/v3/MainFrame.java | 15 +++++---------- .../com/github/kuangcp/tank/v3/RoundMapMgr.java | 15 +++++++++++++++ 3 files changed, 26 insertions(+), 17 deletions(-) create mode 100644 gui/src/main/java/com/github/kuangcp/tank/v3/RoundMapMgr.java diff --git a/gui/src/main/java/com/github/kuangcp/tank/panel/HeroInfoPanel.java b/gui/src/main/java/com/github/kuangcp/tank/panel/HeroInfoPanel.java index 2eeee7a9..7b6224d7 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/panel/HeroInfoPanel.java +++ b/gui/src/main/java/com/github/kuangcp/tank/panel/HeroInfoPanel.java @@ -26,7 +26,7 @@ public class HeroInfoPanel extends JPanel { List ets; HeroInfoPanelRefreshEvent refreshEvent; - static final Tank heroIcon = new Tank(20, 20, 0) { + static final Tank heroIcon = new Tank(15, 20, 0) { @Override public void drawSelf(Graphics g) { g.setColor(Color.yellow); @@ -38,7 +38,7 @@ public void run() { } }; - static final Tank enemyIcon = new Tank(100, 20, 0) { + static final Tank enemyIcon = new Tank(70, 20, 0) { @Override public void drawSelf(Graphics g) { g.setColor(Color.cyan); @@ -65,9 +65,8 @@ public HeroInfoPanel(JLabel tankCounter, List ets, JLabel prizeNo) { this.tankCounter = tankCounter; this.ets = ets; this.prizeNo = prizeNo; -// jl1 =new JLabel("生命值: "+Hero.Life/3); -// MyPanel302 mp = new MyPanel302(); -// mp.add(jl1,BorderLayout.SOUTH); + + tankCounter.setLocation(15, -20); } public void paint(Graphics g) { @@ -82,7 +81,7 @@ public void refreshData() { return; } - final String format = " :%d" + " : %d"; + final String format = " : %d : %d"; final String txt; if (PlayStageMgr.instance.hero.isAlive()) { txt = String.format(format, PlayStageMgr.instance.hero.getLife(), ets.size()); @@ -91,7 +90,7 @@ public void refreshData() { } tankCounter.setText(txt); - prizeNo.setText("战绩:" + PlayStageMgr.instance.hero.getPrize()); + prizeNo.setText("战绩: " + PlayStageMgr.instance.hero.getPrize()); } public void setRefreshEvent(HeroInfoPanelRefreshEvent refreshEvent) { diff --git a/gui/src/main/java/com/github/kuangcp/tank/v3/MainFrame.java b/gui/src/main/java/com/github/kuangcp/tank/v3/MainFrame.java index 6af3b80a..745731ca 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v3/MainFrame.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v3/MainFrame.java @@ -7,7 +7,6 @@ import com.github.kuangcp.tank.panel.TankGroundPanel; import com.github.kuangcp.tank.panel.event.HeroInfoPanelRefreshEvent; import com.github.kuangcp.tank.resource.AvatarImgMgr; -import com.github.kuangcp.tank.resource.ResourceMgr; import com.github.kuangcp.tank.util.executor.MonitorExecutor; import lombok.extern.slf4j.Slf4j; @@ -52,7 +51,7 @@ public class MainFrame extends JFrame implements Runnable { public StarterPanel starterPanel; public JButton startBtn = null, jb2 = null, jb3, jb4; //按钮 public JSplitPane centerPanel, rightAreaPanel;//拆分窗格 - public JLabel tankCounter = null, jl2 = null, jl3 = null, me = null, prizeNo = null; + public JLabel tankCounter = null, me = null, prizeNo = null; public boolean firstStart = true; //判断是否首次运行, 通过开始按钮触发 //作出我需要的菜单 @@ -90,9 +89,8 @@ public void run() { loopEvent.stop(); heroInfoPanel.setEts(groundPanel.enemyList); } else { - //提示信息 - tankCounter = new JLabel(" : : " + groundPanel.enemyList.size()); - prizeNo = new JLabel("已击杀 :");//战绩的标签 + tankCounter = new JLabel(""); + prizeNo = new JLabel(""); me = new JLabel("Myth"); heroInfoPanel = new HeroInfoPanel(tankCounter, groundPanel.enemyList, prizeNo); @@ -174,17 +172,14 @@ public void run() { actionPanel.add(jb4); //显示属性的窗体: - heroInfoPanel.setLayout(new GridLayout(6, 1, 0, 0)); + heroInfoPanel.setLayout(new GridLayout(3, 1, 0, 0)); heroInfoPanel.add(tankCounter);//网格布局 heroInfoPanel.add(prizeNo); -// mp3.add(jl2); -// mp3.add(jl3); heroInfoPanel.add(me); -// jl1 =new JLabel("898908098");//不会对上面造成任何影响 rightAreaPanel = new JSplitPane(JSplitPane.VERTICAL_SPLIT, actionPanel, heroInfoPanel);//水平 - rightAreaPanel.setDividerLocation(150); + rightAreaPanel.setDividerLocation(550); starterPanel = new StarterPanel(); if (!firstStart) { diff --git a/gui/src/main/java/com/github/kuangcp/tank/v3/RoundMapMgr.java b/gui/src/main/java/com/github/kuangcp/tank/v3/RoundMapMgr.java new file mode 100644 index 00000000..77fc3d21 --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/v3/RoundMapMgr.java @@ -0,0 +1,15 @@ +package com.github.kuangcp.tank.v3; + +import com.github.kuangcp.tank.domain.Brick; +import com.github.kuangcp.tank.domain.Iron; + +import java.util.List; + +/** + * @author https://github.com/kuangcp on 2021-09-25 15:57 + */ +public class RoundMapMgr { + + public List bricks; // 砖 + public List irons; // 铁 +} From f1ec16645e08c07c004eb445aa168d022a3d0d05 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Sun, 17 Oct 2021 23:24:42 +0800 Subject: [PATCH 246/476] -) remove info panel --- gui/build.gradle | 1 + .../github/kuangcp/tank/domain/EnemyTank.java | 18 +- .../com/github/kuangcp/tank/domain/Hero.java | 16 +- .../com/github/kuangcp/tank/mgr/BombMgr.java | 4 +- .../kuangcp/tank/panel/StageActionPanel.java | 47 +---- .../kuangcp/tank/panel/TankGroundPanel.java | 193 +++++++++--------- .../kuangcp/tank/resource/AvatarImgMgr.java | 2 +- .../kuangcp/tank/resource/ColorMgr.java | 14 ++ .../kuangcp/tank/resource/ResourceMgr.java | 1 + ...ventGroup.java => HoldingKeyEventMgr.java} | 31 ++- .../github/kuangcp/tank/util/KeyListener.java | 16 +- .../tank/util/executor/MonitorExecutor.java | 15 +- .../com/github/kuangcp/tank/v3/MainFrame.java | 164 +++++++-------- .../github/kuangcp/tank/v3/PlayStageMgr.java | 15 +- .../github/kuangcp/tank/v3/SettingFrame.java | 5 + .../resources/tank/conf/stage-map.properties | 0 .../github/kuangcp/tank/util/ListConTest.java | 7 + 17 files changed, 277 insertions(+), 272 deletions(-) create mode 100644 gui/src/main/java/com/github/kuangcp/tank/resource/ColorMgr.java rename gui/src/main/java/com/github/kuangcp/tank/util/{ListenEventGroup.java => HoldingKeyEventMgr.java} (58%) create mode 100644 gui/src/main/resources/tank/conf/stage-map.properties create mode 100644 gui/src/test/java/com/github/kuangcp/tank/util/ListConTest.java diff --git a/gui/build.gradle b/gui/build.gradle index 9937d9b8..5d558374 100644 --- a/gui/build.gradle +++ b/gui/build.gradle @@ -7,6 +7,7 @@ dependencies { implementation libs['kcp-core'] // implementation libs['quasar-core'] } + //configurations { // quasar //} diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java b/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java index 54a6623d..fca6bd71 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java @@ -74,23 +74,7 @@ public EnemyTank(int x, int y, int direct) { this.direct = direct; this.alive = true; this.life = ThreadLocalRandom.current().nextInt(MAX_LIFE) + 1; - this.speed = MAX_LIFE - life + 1; - this.id = counter.addAndGet(1); - - this.setFixedDelayTime(40); - this.moveRate = 2; - this.shotRate = 17; - - this.afterBuild(); - } - - public EnemyTank(int x, int y, int speed, int direct) { - super(x, y, speed); - type = 1; - this.direct = direct; - this.speed = speed; - this.alive = true; - this.life = ThreadLocalRandom.current().nextInt(MAX_LIFE) + 1; + this.speed = (MAX_LIFE - life + 1) / 2 + 1; this.id = counter.addAndGet(1); this.setFixedDelayTime(40); diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/Hero.java b/gui/src/main/java/com/github/kuangcp/tank/domain/Hero.java index c90bbc2f..b014747d 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/domain/Hero.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/Hero.java @@ -1,7 +1,7 @@ package com.github.kuangcp.tank.domain; - import com.github.kuangcp.tank.constant.DirectType; +import com.github.kuangcp.tank.resource.ColorMgr; import com.github.kuangcp.tank.util.executor.LoopEventExecutor; import com.github.kuangcp.tank.v3.PlayStageMgr; import lombok.extern.slf4j.Slf4j; @@ -23,7 +23,8 @@ public class Hero extends Tank { public Bullet bullet = null;//子弹 private int prize = 0;//击敌个数 public int maxLiveShot = 7;//主坦克子弹线程存活的最大数 - private long lastDieMs = 0; + private long lastDieMs = System.currentTimeMillis(); + private int normalColor = 0; public Hero(int x, int y, int speed) { super(x, y, speed); @@ -41,9 +42,14 @@ public void run() { */ @Override public void drawSelf(Graphics g) { - g.setColor(Color.yellow); + g.setColor(Color.YELLOW); if (this.isInvincible()) { - g.setColor(Color.cyan); + g.setColor(Color.CYAN); + normalColor++; + normalColor %= 28; + if (normalColor % 14 < 3) { + g.setColor(ColorMgr.instance.bgColor); + } } super.drawSelf(g); } @@ -61,7 +67,7 @@ public void resurrect() { this.lastDieMs = System.currentTimeMillis(); // 1/10 概率原地复活 - if (ThreadLocalRandom.current().nextInt(1) == 0) { + if (ThreadLocalRandom.current().nextInt(10) == 0) { return; } diff --git a/gui/src/main/java/com/github/kuangcp/tank/mgr/BombMgr.java b/gui/src/main/java/com/github/kuangcp/tank/mgr/BombMgr.java index 9ac600c4..c0a70d1c 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/mgr/BombMgr.java +++ b/gui/src/main/java/com/github/kuangcp/tank/mgr/BombMgr.java @@ -28,7 +28,7 @@ public class BombMgr extends AbstractImgListMgr { public BombMgr() { } - public String getConfigKey(){ + public String getConfigKey() { return PropertiesMgr.Key.Img.ANIMATION_BOMB; } @@ -72,7 +72,7 @@ public void checkBong(Tank tank, List bullets) { } private void checkBong(Tank tank, Bullet bullet) { - if (!bullet.alive) { + if (!bullet.alive || !tank.isAlive()) { return; } diff --git a/gui/src/main/java/com/github/kuangcp/tank/panel/StageActionPanel.java b/gui/src/main/java/com/github/kuangcp/tank/panel/StageActionPanel.java index 1da97023..5f984129 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/panel/StageActionPanel.java +++ b/gui/src/main/java/com/github/kuangcp/tank/panel/StageActionPanel.java @@ -3,23 +3,17 @@ import com.github.kuangcp.tank.constant.ButtonCommand; -import com.github.kuangcp.tank.domain.Brick; import com.github.kuangcp.tank.domain.Bullet; import com.github.kuangcp.tank.domain.EnemyTank; -import com.github.kuangcp.tank.domain.Hero; -import com.github.kuangcp.tank.domain.Iron; import com.github.kuangcp.tank.util.Audio; -import com.github.kuangcp.tank.util.Saved; import com.github.kuangcp.tank.v3.MainFrame; import com.github.kuangcp.tank.v3.PlayStageMgr; -import com.github.kuangcp.tank.v3.SettingFrame; import lombok.extern.slf4j.Slf4j; import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import java.util.List; import java.util.Objects; /** @@ -29,20 +23,13 @@ @SuppressWarnings("serial") @Slf4j public class StageActionPanel extends JPanel implements ActionListener { - static Thread actionThread = null; + + private static Thread actionThread = null; MainFrame frame; - Hero hero; - List ets; - List irons; - List bricks; - int[][] ETS; - int[] myself; Audio beginAudio; @Override public void actionPerformed(ActionEvent ae) { - //上面是不合理的,会有隐藏的bug在里面,这种做法要抛弃 - if (ae.getActionCommand().equals(ButtonCommand.START)) { this.startNewStage(); } @@ -69,18 +56,10 @@ public void actionPerformed(ActionEvent ae) { //口令与口令之间应该独立,不要有或,与这样的复杂逻辑结构 System.out.println("退出游戏"); System.exit(0); - - } - - if (ae.getActionCommand().equals(ButtonCommand.SETTING_FRAME)) { - SettingFrame.activeFocus(); } if (ae.getActionCommand().equals("saveExit")) { System.out.println("按下了 保存并退出 按钮"); - Saved s = new Saved(ets, hero, bricks, irons, ETS, myself); -// s.savedAll(); - s.saveDataBase(); //退出 System.out.println("已经退出游戏"); System.exit(0); @@ -92,26 +71,20 @@ public void actionPerformed(ActionEvent ae) { frame.firstStart = false; TankGroundPanel.newStage = false; Bullet.setSpeed(8); - frame.remove(frame.centerPanel); -// Saved s = new Saved(ets, hero, bricks, irons,ETS,myself); -// s.readAll(); - //实现一样的功能还省内存 - new Saved(ets, hero, bricks, irons, ETS, myself).readDataBase(); +// frame.remove(frame.centerPanel); EventQueue.invokeLater(frame); // actionThread = new Thread(frame); // actionThread.start(); //读取 -// Saved s = new Saved(ets, hero, bricks, irons); -// s.readAll(); beginAudio = new Audio("./src/RE/GameBegin.wav"); // beginAudio.start(); } } - private void startNewStage() { - + public void startNewStage() { + log.warn("new stage"); final int totalMaxShots = PlayStageMgr.getEnemySize() * EnemyTank.maxLiveShot; // 重新设置线程池大小 @@ -134,7 +107,7 @@ private void startNewStage() { frame.firstStart = false; TankGroundPanel.newStage = true; Bullet.setSpeed(8); - frame.remove(frame.centerPanel); + frame.remove(frame.getContentPane()); log.info("start new stage frame thread, shot:{}", totalMaxShots); actionThread = new Thread(() -> { @@ -150,13 +123,7 @@ private void startNewStage() { actionThread.start();//将画板线程开启 } - public StageActionPanel(MainFrame frame, Hero he, java.util.List ets, java.util.List bricks, List irons, int[][] ETS, int[] myself) { + public StageActionPanel(MainFrame frame) { this.frame = frame; - this.hero = he; - this.ets = ets; - this.bricks = bricks; - this.irons = irons; - this.ETS = ETS; - this.myself = myself; } } \ No newline at end of file diff --git a/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java b/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java index c3c5b771..983d4ca0 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java +++ b/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java @@ -9,14 +9,18 @@ import com.github.kuangcp.tank.domain.Iron; import com.github.kuangcp.tank.mgr.BombMgr; import com.github.kuangcp.tank.resource.AvatarImgMgr; -import com.github.kuangcp.tank.thread.ExitFlagRunnable; +import com.github.kuangcp.tank.resource.ColorMgr; +import com.github.kuangcp.tank.util.HoldingKeyEventMgr; import com.github.kuangcp.tank.util.KeyListener; -import com.github.kuangcp.tank.util.ListenEventGroup; import com.github.kuangcp.tank.util.TankTool; import com.github.kuangcp.tank.util.executor.AbstractDelayEvent; import com.github.kuangcp.tank.util.executor.DelayExecutor; import com.github.kuangcp.tank.util.executor.LoopEventExecutor; +import com.github.kuangcp.tank.util.executor.MonitorExecutor; +import com.github.kuangcp.tank.v3.MainFrame; import com.github.kuangcp.tank.v3.PlayStageMgr; +import com.github.kuangcp.tank.v3.SettingFrame; +import com.github.kuangcp.tank.v3.StageBorder; import lombok.extern.slf4j.Slf4j; import javax.swing.*; @@ -34,11 +38,12 @@ */ @SuppressWarnings("serial") @Slf4j -public class TankGroundPanel extends JPanel implements java.awt.event.KeyListener, ExitFlagRunnable { +public class TankGroundPanel extends JPanel implements java.awt.event.KeyListener, Runnable { public volatile Hero hero; public KeyListener keyListener; public static boolean newStage = true; + private volatile boolean invokeNewStage = false; //定义一个 泛型的集合ets 表示敌人坦克集合 public List enemyList = Collections.synchronizedList(new ArrayList<>()); @@ -51,16 +56,10 @@ public class TankGroundPanel extends JPanel implements java.awt.event.KeyListene public static int[][] enemyTankMap = new int[12][2]; public static int[] myself = new int[6]; - private volatile boolean exit = false; - public TankGroundPanel() { } - public void exit() { - this.exit = true; - } - /** * 画板的构造函数 用来初始化对象 * 多个敌方坦克应该用集合类,而且要考虑线程安全所以不能用ArrayList只能用Vector @@ -80,7 +79,7 @@ public void startNewRound() { PlayStageMgr.init(hero, enemyList, bricks, irons); //多键监听实现 - keyListener = new KeyListener(ListenEventGroup.instance, hero, this); + keyListener = new KeyListener(HoldingKeyEventMgr.instance, hero, this); Thread p = new Thread(keyListener); p.setName("playerKeyEventListener"); p.start(); @@ -92,19 +91,19 @@ public void startNewRound() { //在四个随机区域产生坦克 switch ((int) (Math.random() * 4)) { case 0: - ett = new EnemyTank(20 + (int) (Math.random() * 30), 20 + (int) (Math.random() * 30), 2, i % 4); + ett = new EnemyTank(20 + (int) (Math.random() * 30), 20 + (int) (Math.random() * 30), i % 4); ett.SetInfo(hero, enemyList, bricks, irons); break; case 1: - ett = new EnemyTank(700 - (int) (Math.random() * 30), 20 + (int) (Math.random() * 30), 2, i % 4); + ett = new EnemyTank(700 - (int) (Math.random() * 30), 20 + (int) (Math.random() * 30), i % 4); ett.SetInfo(hero, enemyList, bricks, irons); break; case 2: - ett = new EnemyTank(20 + (int) (Math.random() * 30), 200 + (int) (Math.random() * 30), 2, i % 4); + ett = new EnemyTank(20 + (int) (Math.random() * 30), 200 + (int) (Math.random() * 30), i % 4); ett.SetInfo(hero, enemyList, bricks, irons); break; case 3: - ett = new EnemyTank(700 - (int) (Math.random() * 30), 200 + (int) (Math.random() * 30), 2, i % 4); + ett = new EnemyTank(700 - (int) (Math.random() * 30), 200 + (int) (Math.random() * 30), i % 4); ett.SetInfo(hero, enemyList, bricks, irons); break; } @@ -124,7 +123,7 @@ public void startNewRound() { for (int i = 0; i < enemyTankMap.length; i++) { if (enemyTankMap[i][0] == 0) break; - ett = new EnemyTank(enemyTankMap[i][0], enemyTankMap[i][1], 2, i % 4); + ett = new EnemyTank(enemyTankMap[i][0], enemyTankMap[i][1], i % 4); ett.SetInfo(hero, enemyList, bricks, irons); LoopEventExecutor.addLoopEvent(ett); @@ -134,41 +133,13 @@ public void startNewRound() { // t.start(); //坦克加入集合 enemyList.add(ett); -// System.out.print("创建单个坦克地址:"+ett); -// System.out.println("_____X="+ett.getX()+"Y="+ett.getY()); - -// System.out.println("进入了读取数组这里"); } } - - //创建砖块 -// createB(bricks, 40, 40, 200, 400); -// createB(bricks, 200, 40, 400, 100); -// createB(bricks, 400, 40, 700, 400); -// createB(bricks, 200, 300, 400, 400); -// createB(bricks, 40, 40, 700, 400); - - //眼睛 - createI(irons, 180, 100, 260, 180); - createI(irons, 540, 100, 620, 180); -// createB(bricks, 200, 120, 240, 160); -// createB(bricks, 560, 120, 600, 160); - - //鼻子 - int m = 400, n = 260; - createI(irons, m, n, m + 20, n + 10); - createI(irons, m - 10, n + 10, m + 30, n + 20); - createI(irons, m - 20, n + 20, m + 40, n + 30); -// createB(bricks, 300, 240, 520, 360); - //左右中间 -// createI(irons, 580, 370, 740, 380); -// createI(irons, 20, 370, 210, 380); //左右下角 createB(bricks, 20, 310, 300, 540); createB(bricks, 520, 310, 740, 540); -// createI(irons, 250, 130, 300, 300); -// createI(irons, 300, 130, 400, 200); + //头像附近 createB(bricks, 360, 460, 460, 480); createB(bricks, 360, 480, 380, 540); @@ -179,24 +150,44 @@ public void startNewRound() { PlayStageMgr.instance.markStartLogic(); } + private void drawBg(Graphics g) { + g.setColor(ColorMgr.instance.bgColor); + final StageBorder border = PlayStageMgr.instance.border; + g.fillRect(0, 0, border.getMinX() + border.getMaxX(), border.getMinY() + border.getMaxY()); + g.setColor(Color.green); + g.drawRect(border.getMinX(), border.getMinY(), + border.getMaxX() - border.getMinX(), border.getMaxY() - border.getMinY()); + } + + private void drawHeroInfo(Graphics g) { + g.setColor(Color.GREEN); +// g.setFont(Font.getFont("IBM Plex Mono")); + final String lifeInfo = "Life:" + PlayStageMgr.instance.hero.getLife() + + " enemy: " + PlayStageMgr.instance.getLiveEnemy(); + g.drawString(lifeInfo, PlayStageMgr.instance.border.getMinX(), 15); + } + + private void drawMonitorInfo(Graphics g) { + g.setColor(Color.LIGHT_GRAY); + g.drawString(MonitorExecutor.info.toString(), PlayStageMgr.instance.border.getMinX(), 555); + } + @Override public void paint(Graphics g) { super.paint(g); + if (PlayStageMgr.stageNoneStart() || Objects.isNull(hero)) { PlayStageMgr.drawStopIgnore(g, this); return; } - /*画出坦克运动区域 */ - g.setColor(new Color(8, 8, 8, 237)); - g.fillRect(0, 0, 760, 560);//填满一个黑色矩形,说明了是一整个JPanel在JFrame上的 - g.setColor(Color.green); - g.drawRect(20, 20, 720, 520); - + this.drawBg(g); + this.drawHeroInfo(g); + this.drawMonitorInfo(g); /*画出障碍物__砖__ 铁__*/ - g.setColor(new Color(200, 200, 200)); + g.setColor(ColorMgr.instance.ironColor); for (int i = 0; i < irons.size(); i++) { Iron ir = irons.get(i); if (ir.getAlive()) { @@ -206,8 +197,7 @@ public void paint(Graphics g) { } } -// g.setColor(new Color(190, 60, 50)); - g.setColor(new Color(188, 112, 50)); + g.setColor(ColorMgr.instance.brickColor); for (int i = 0; i < bricks.size(); i++) { Brick bs = bricks.get(i); if (bs.getAlive()) { @@ -217,18 +207,22 @@ public void paint(Graphics g) { } } - //眼睛 - g.setColor(Color.YELLOW); - g.fillRect(200, 120, 40, 40); - g.fillRect(560, 120, 40, 40); - /*画出头像*/ g.drawImage(AvatarImgMgr.instance.curImg, 380, 480, AvatarImgMgr.instance.width, AvatarImgMgr.instance.height, this); + /*画出主坦克*/ if (hero.isAlive()) { - for (EnemyTank et : enemyList) { - BombMgr.instance.checkBong(hero, et.bulletList); + for (int i = 0; i < enemyList.size(); i++) { + EnemyTank demon; + try { + demon = enemyList.get(i); + } catch (IndexOutOfBoundsException e) { + log.error("", e); + continue; + } + + BombMgr.instance.checkBong(hero, demon.bulletList); } hero.drawSelf(g); @@ -261,7 +255,15 @@ public void paint(Graphics g) { /*敌人子弹*/ // FIXME ConcurrentModificationException - for (EnemyTank et : enemyList) { + for (int j = 0; j < enemyList.size(); j++) { + EnemyTank et; + try { + et = enemyList.get(j); + } catch (IndexOutOfBoundsException e) { + log.error("", e); + continue; + } + for (int i = 0; i < et.bulletList.size(); i++) { Bullet myBullet = et.bulletList.get(i); for (Brick brick : bricks) { @@ -291,7 +293,7 @@ public void paint(Graphics g) { //坦克少于5个就自动添加4个 if (enemyList.size() < 5) { for (int i = 0; i < 4; i++) { - EnemyTank d = new EnemyTank(20 + (int) (Math.random() * 400), 20 + (int) (Math.random() * 300), 2, i % 4); + EnemyTank d = new EnemyTank(20 + (int) (Math.random() * 400), 20 + (int) (Math.random() * 300), i % 4); d.SetInfo(hero, enemyList, bricks, irons); LoopEventExecutor.addLoopEvent(d); @@ -315,10 +317,16 @@ public void paint(Graphics g) { // } // } for (int i = 0; i < enemyList.size(); i++) { - EnemyTank demon = enemyList.get(i); + EnemyTank demon; + try { + demon = enemyList.get(i); + } catch (IndexOutOfBoundsException e) { + log.error("", e); + continue; + } + //存活再画出来 if (demon.isAlive()) { - BombMgr.instance.checkBong(demon, hero.bulletList); demon.drawSelf(g); @@ -353,45 +361,48 @@ public void run() { */ @Override public void keyPressed(KeyEvent e) { - if (PlayStageMgr.pause) { - return; + // 启动关闭流程 + if (e.getKeyCode() == KeyEvent.VK_Q) { + System.exit(0); + } else if (e.getKeyCode() == KeyEvent.VK_T && !invokeNewStage) { + invokeNewStage = true; + MainFrame.actionPanel.startNewStage(); + } else if (e.getKeyCode() == KeyEvent.VK_C) { + PlayStageMgr.pause = false; } - //加了if(内层的)限制后 实现了墙的限制(如果是游戏中的道具,该怎么办) - if (e.getKeyChar() == KeyEvent.VK_SPACE) { - ListenEventGroup.instance.setShot(true); - } - - if (ListenEventGroup.instance.hasPressMoveEvent()) { + if (PlayStageMgr.pause) { return; } - if (e.getKeyCode() == KeyEvent.VK_A) { - ListenEventGroup.instance.setLeft(true); + if (e.getKeyCode() == KeyEvent.VK_P) { + PlayStageMgr.pause = true; } - - if (e.getKeyCode() == KeyEvent.VK_D) { - ListenEventGroup.instance.setRight(true); + if (e.getKeyCode() == KeyEvent.VK_O) { + SettingFrame.activeFocus(); } - if (e.getKeyCode() == KeyEvent.VK_W) { - ListenEventGroup.instance.setUp(true); - } - if (e.getKeyCode() == KeyEvent.VK_S) { - ListenEventGroup.instance.setDown(true); + // 实际用户交互 + if (e.getKeyCode() == KeyEvent.VK_J) { + HoldingKeyEventMgr.instance.setShot(true); } - //必须重新绘制窗口,不然上面的方法不能视觉上动起来 + HoldingKeyEventMgr.instance.handleDirectPress(e); this.repaint(); } @Override public void keyReleased(KeyEvent re) { - ListenEventGroup.instance.handleRelease(re); + HoldingKeyEventMgr.instance.handleRelease(re); + + if (re.getKeyCode() == KeyEvent.VK_T && invokeNewStage) { + invokeNewStage = false; + } } @Override - public void keyTyped(KeyEvent arg0) { + public void keyTyped(KeyEvent e) { +// log.info("1={}", e); } /** @@ -423,12 +434,12 @@ public void createI(List irons, int startX, int startY, int endX, int endY //画板做成线程 画布进行刷新 @Override public void run() { - int maxFPS = 160; + int maxFPS = 60; long fpsTime = (long) ((1000.0 / maxFPS) * 1000000); long now; //每隔100ms重绘 - while (!exit) { + while (true) { now = System.nanoTime(); // 进行重绘 @@ -439,14 +450,6 @@ public void run() { } TankTool.yieldTime(fpsTime - waste, TimeUnit.NANOSECONDS); - if (Objects.nonNull(hero) && !hero.isAlive()) { - break; - } - } - - // clean listener - if (Objects.nonNull(keyListener)) { - keyListener.exit(); } } } \ No newline at end of file diff --git a/gui/src/main/java/com/github/kuangcp/tank/resource/AvatarImgMgr.java b/gui/src/main/java/com/github/kuangcp/tank/resource/AvatarImgMgr.java index f3460894..43955040 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/resource/AvatarImgMgr.java +++ b/gui/src/main/java/com/github/kuangcp/tank/resource/AvatarImgMgr.java @@ -8,7 +8,7 @@ @Slf4j public class AvatarImgMgr extends AbstractImgListMgr { - public static AvatarImgMgr instance = new AvatarImgMgr(); + public static final AvatarImgMgr instance = new AvatarImgMgr(); public AvatarImgMgr() { super.width = 60; diff --git a/gui/src/main/java/com/github/kuangcp/tank/resource/ColorMgr.java b/gui/src/main/java/com/github/kuangcp/tank/resource/ColorMgr.java new file mode 100644 index 00000000..b70c6445 --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/resource/ColorMgr.java @@ -0,0 +1,14 @@ +package com.github.kuangcp.tank.resource; + +import java.awt.*; + +/** + * @author https://github.com/kuangcp on 2021-10-17 23:06 + */ +public class ColorMgr { + public static final ColorMgr instance = new ColorMgr(); + + public final Color bgColor = new Color(8, 8, 8, 237); + public final Color ironColor = new Color(200, 200, 200); + public final Color brickColor = new Color(188, 112, 50); +} diff --git a/gui/src/main/java/com/github/kuangcp/tank/resource/ResourceMgr.java b/gui/src/main/java/com/github/kuangcp/tank/resource/ResourceMgr.java index a6f1c1b0..72b82c6c 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/resource/ResourceMgr.java +++ b/gui/src/main/java/com/github/kuangcp/tank/resource/ResourceMgr.java @@ -13,6 +13,7 @@ public static void loadResource() { PropertiesMgr.init(); log.info("[init] start load resource"); + // image BombMgr.instance.loadImg(); DefeatImgMgr.instance.loadImg(); diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/ListenEventGroup.java b/gui/src/main/java/com/github/kuangcp/tank/util/HoldingKeyEventMgr.java similarity index 58% rename from gui/src/main/java/com/github/kuangcp/tank/util/ListenEventGroup.java rename to gui/src/main/java/com/github/kuangcp/tank/util/HoldingKeyEventMgr.java index c7c56b76..ad9324e4 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/util/ListenEventGroup.java +++ b/gui/src/main/java/com/github/kuangcp/tank/util/HoldingKeyEventMgr.java @@ -11,9 +11,9 @@ * @author https://github.com/kuangcp on 2021-09-06 02:58 * @see TankGroundPanel#keyPressed */ -public class ListenEventGroup { +public class HoldingKeyEventMgr { - public static ListenEventGroup instance = new ListenEventGroup(); + public static HoldingKeyEventMgr instance = new HoldingKeyEventMgr(); private volatile boolean up; private volatile boolean down; @@ -21,18 +21,29 @@ public class ListenEventGroup { private volatile boolean right; private volatile boolean shot; - private final Map actionMap = new HashMap<>(); + private volatile boolean ctrl; - public ListenEventGroup() { - actionMap.put(KeyEvent.VK_A, () -> this.left = false); - actionMap.put(KeyEvent.VK_D, () -> this.right = false); - actionMap.put(KeyEvent.VK_S, () -> this.down = false); - actionMap.put(KeyEvent.VK_W, () -> this.up = false); - actionMap.put(KeyEvent.VK_SPACE, () -> this.shot = false); + private final Map releaseMap = new HashMap<>(); + private final Map pressMap = new HashMap<>(); + + public HoldingKeyEventMgr() { + releaseMap.put(KeyEvent.VK_A, () -> this.left = false); + releaseMap.put(KeyEvent.VK_D, () -> this.right = false); + releaseMap.put(KeyEvent.VK_S, () -> this.down = false); + releaseMap.put(KeyEvent.VK_W, () -> this.up = false); + releaseMap.put(KeyEvent.VK_J, () -> this.shot = false); + + pressMap.put(KeyEvent.VK_A, () -> this.left = true); + pressMap.put(KeyEvent.VK_D, () -> this.right = true); + pressMap.put(KeyEvent.VK_S, () -> this.down = true); + pressMap.put(KeyEvent.VK_W, () -> this.up = true); } + public void handleDirectPress(KeyEvent re){ + Optional.ofNullable(pressMap.get(re.getKeyCode())).ifPresent(Runnable::run); + } public void handleRelease(KeyEvent re) { - Optional.ofNullable(actionMap.get(re.getKeyCode())).ifPresent(Runnable::run); + Optional.ofNullable(releaseMap.get(re.getKeyCode())).ifPresent(Runnable::run); } public boolean isUp() { diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/KeyListener.java b/gui/src/main/java/com/github/kuangcp/tank/util/KeyListener.java index 8526fc4e..0e3c6bdd 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/util/KeyListener.java +++ b/gui/src/main/java/com/github/kuangcp/tank/util/KeyListener.java @@ -21,10 +21,10 @@ public class KeyListener implements ExitFlagRunnable { Hero hero; TankGroundPanel tankGroundPanel; - ListenEventGroup eventGroup; + HoldingKeyEventMgr eventGroup; private volatile boolean exit = false; - public KeyListener(ListenEventGroup eventGroup, Hero hero, TankGroundPanel tankGroundPanel) { + public KeyListener(HoldingKeyEventMgr eventGroup, Hero hero, TankGroundPanel tankGroundPanel) { this.eventGroup = eventGroup; this.hero = hero; this.tankGroundPanel = tankGroundPanel; @@ -46,23 +46,17 @@ public void run() { if (PlayStageMgr.instance.willInBorder(hero) && PlayStageMgr.instance.ableToMove(hero)) { hero.moveLeft(); } - } - - if (eventGroup.isRight()) { + } else if (eventGroup.isRight()) { hero.setDirect(DirectType.RIGHT); if (PlayStageMgr.instance.willInBorder(hero) && PlayStageMgr.instance.ableToMove(hero)) { hero.moveRight(); } - } - - if (eventGroup.isDown()) { + } else if (eventGroup.isDown()) { hero.setDirect(DirectType.DOWN); if (PlayStageMgr.instance.willInBorder(hero) && PlayStageMgr.instance.ableToMove(hero)) { hero.moveDown(); } - } - - if (eventGroup.isUp()) { + } else if (eventGroup.isUp()) { hero.setDirect(DirectType.UP); if (PlayStageMgr.instance.willInBorder(hero) && PlayStageMgr.instance.ableToMove(hero)) { hero.moveUp(); diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/executor/MonitorExecutor.java b/gui/src/main/java/com/github/kuangcp/tank/util/executor/MonitorExecutor.java index 8fe1ecaf..c8bf62fa 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/util/executor/MonitorExecutor.java +++ b/gui/src/main/java/com/github/kuangcp/tank/util/executor/MonitorExecutor.java @@ -13,6 +13,7 @@ @Slf4j public class MonitorExecutor { + public static final Info info = new Info(); private static final BlockingQueue queue = new DelayQueue<>(); private static final ExecutorService monitorEventPool = ExecutePool.buildFixedPool("monitorEvent", 1); @@ -27,12 +28,22 @@ public static void init() { monitorEventPool.execute(() -> CommonEventExecutor.loopEventSpin(queue)); } + public static class Info { + int loopEventCount; + int monitorEventCount; + + @Override + public String toString() { + return "loop:" + loopEventCount + " monitor:" + monitorEventCount; + } + } + private static void registerEventMonitor() { - // TODO 更新到主背景图上 final AbstractLoopEvent loopEventMonitor = new AbstractLoopEvent() { @Override public void run() { - log.info("loopEvent:{} monitor:{}", LoopEventExecutor.queue.size(), queue.size()); + info.loopEventCount = LoopEventExecutor.queue.size(); + info.monitorEventCount = queue.size(); } }; loopEventMonitor.setFixedDelayTime(5_000); diff --git a/gui/src/main/java/com/github/kuangcp/tank/v3/MainFrame.java b/gui/src/main/java/com/github/kuangcp/tank/v3/MainFrame.java index 745731ca..1fa00d6b 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v3/MainFrame.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v3/MainFrame.java @@ -5,9 +5,7 @@ import com.github.kuangcp.tank.panel.StageActionPanel; import com.github.kuangcp.tank.panel.StarterPanel; import com.github.kuangcp.tank.panel.TankGroundPanel; -import com.github.kuangcp.tank.panel.event.HeroInfoPanelRefreshEvent; import com.github.kuangcp.tank.resource.AvatarImgMgr; -import com.github.kuangcp.tank.util.executor.MonitorExecutor; import lombok.extern.slf4j.Slf4j; import javax.swing.*; @@ -42,23 +40,20 @@ public class MainFrame extends JFrame implements Runnable { public volatile TankGroundPanel groundPanel = null;//坦克的主画板 - public StageActionPanel actionPanel = null;//放按钮的画板 + public static StageActionPanel actionPanel = null;//放按钮的画板 public HeroInfoPanel heroInfoPanel = null; //显示属性的画板 - private HeroInfoPanelRefreshEvent loopEvent = null; - +// private HeroInfoPanelRefreshEvent loopEvent = null; public StarterPanel starterPanel; - public JButton startBtn = null, jb2 = null, jb3, jb4; //按钮 - public JSplitPane centerPanel, rightAreaPanel;//拆分窗格 public JLabel tankCounter = null, me = null, prizeNo = null; public boolean firstStart = true; //判断是否首次运行, 通过开始按钮触发 //作出我需要的菜单 - JMenuBar jmb = null; + JMenuBar menuBar = null; //开始游戏 - JMenu jm1 = null; - JMenu jm2 = null; + JMenu gameMenu = null; + JMenu helpMenu = null; JMenuItem jmil = null; //退出系统 JMenuItem jmi2 = null; @@ -69,24 +64,30 @@ public class MainFrame extends JFrame implements Runnable { JMenuItem setting = null; public MainFrame() { + groundPanel = new TankGroundPanel(); + actionPanel = new StageActionPanel(this); + + Thread t = new Thread(groundPanel); + t.setName("tankGroundPanel"); + t.start(); + + this.setTitle("Tank"); + this.setLocation(680, 290); + this.setSize(760, 590); +// this.setUndecorated(true); } public void run() { final long start = System.currentTimeMillis(); - if (Objects.nonNull(groundPanel)) { - groundPanel.exit(); - } // FIXME 复用对象导致性能缓慢 // else { // groundPanel = new TankGroundPanel(); // } - groundPanel = new TankGroundPanel(); - if (Objects.nonNull(heroInfoPanel)) { - loopEvent.stop(); +// loopEvent.stop(); heroInfoPanel.setEts(groundPanel.enemyList); } else { tankCounter = new JLabel(""); @@ -96,80 +97,75 @@ public void run() { heroInfoPanel = new HeroInfoPanel(tankCounter, groundPanel.enemyList, prizeNo); } - actionPanel = new StageActionPanel(this, groundPanel.hero, groundPanel.enemyList, groundPanel.bricks, - groundPanel.irons, TankGroundPanel.enemyTankMap, TankGroundPanel.myself);//放按钮的画布 +// loopEvent = new HeroInfoPanelRefreshEvent(heroInfoPanel); +// heroInfoPanel.setRefreshEvent(loopEvent); +// MonitorExecutor.addLoopEvent(loopEvent); - loopEvent = new HeroInfoPanelRefreshEvent(heroInfoPanel); - heroInfoPanel.setRefreshEvent(loopEvent); - MonitorExecutor.addLoopEvent(loopEvent); - - if (!firstStart) { - Thread t = new Thread(groundPanel); - t.setName("tankGroundPanel"); - t.start(); - } //创建菜单及菜单选项 - jmb = new JMenuBar(); - jm1 = new JMenu("Game(G)"); - jm2 = new JMenu("Help(H)"); - + menuBar = new JMenuBar(); + gameMenu = new JMenu("Game(G)"); + helpMenu = new JMenu("Help(H)"); //设置快捷方式 - jm1.setMnemonic('G'); - jm2.setMnemonic('H'); + gameMenu.setMnemonic('G'); + helpMenu.setMnemonic('H'); + jmil = new JMenuItem("New Game(N)"); jmi2 = new JMenuItem("Pause"); jmi3 = new JMenuItem("Save & Exit(C)"); jmi4 = new JMenuItem("ContinueLast(S)"); - setting = new JMenuItem("Setting"); - // - setting.addActionListener(actionPanel); + setting = new JMenuItem("Setting"); + setting.addActionListener(SettingFrame.instance); setting.setActionCommand(ButtonCommand.SETTING_FRAME); - //注册监听 + helpMenu.add(setting); + jmi4.addActionListener(actionPanel); jmi4.setActionCommand("Continue"); - //注册监听 + jmi3.addActionListener(actionPanel); jmi3.setActionCommand("saveExit"); - // + jmi2.addActionListener(actionPanel); jmi2.setActionCommand("暂停"); jmi2.setMnemonic('E'); - //对jmil相应 + jmil.addActionListener(actionPanel); jmil.setActionCommand(ButtonCommand.START); - jm1.add(jmil); -// jm1.add(jmi2); - jm1.add(jmi3); - jm1.add(jmi4); - - jm2.add(setting); - - jmb.add(jm1); - jmb.add(jm2); - - jb3 = new JButton("暂停游戏"); - jb3.addActionListener(actionPanel); //注册监听 - jb3.setActionCommand(ButtonCommand.PAUSE); - - jb4 = new JButton("继续游戏"); - jb4.addActionListener(actionPanel); //注册监听 - jb4.setActionCommand(ButtonCommand.RESUME); - - jb2 = new JButton("退出游戏"); - jb2.addActionListener(actionPanel); //注册监听 - jb2.setActionCommand(ButtonCommand.EXIT); - - startBtn = new JButton(firstStart ? "游戏开始" : "重新开始"); - startBtn.addActionListener(actionPanel); - startBtn.setActionCommand(ButtonCommand.START); - - actionPanel.add(startBtn); - actionPanel.add(jb2); - actionPanel.add(jb3); - actionPanel.add(jb4); + gameMenu.add(jmil); + + final JMenuItem exitItem = new JMenuItem("Exit"); + exitItem.addActionListener(actionPanel); + exitItem.setActionCommand(ButtonCommand.EXIT); + gameMenu.add(exitItem); + + gameMenu.add(jmi3); + gameMenu.add(jmi4); + + menuBar.add(gameMenu); + menuBar.add(helpMenu); + +// jb3 = new JButton("暂停游戏"); +// jb3.addActionListener(actionPanel); //注册监听 +// jb3.setActionCommand(ButtonCommand.PAUSE); +// +// jb4 = new JButton("继续游戏"); +// jb4.addActionListener(actionPanel); //注册监听 +// jb4.setActionCommand(ButtonCommand.RESUME); +// +// jb2 = new JButton("退出游戏"); +// jb2.addActionListener(actionPanel); //注册监听 +// jb2.setActionCommand(ButtonCommand.EXIT); +// +// startBtn = new JButton(firstStart ? "游戏开始" : "重新开始"); +// startBtn.addActionListener(actionPanel); +// startBtn.setActionCommand(ButtonCommand.START); +// +// actionPanel.add(startBtn); +// actionPanel.add(jb2); +// actionPanel.add(jb3); +// actionPanel.add(jb4); //显示属性的窗体: heroInfoPanel.setLayout(new GridLayout(3, 1, 0, 0)); @@ -178,37 +174,31 @@ public void run() { heroInfoPanel.add(prizeNo); heroInfoPanel.add(me); - rightAreaPanel = new JSplitPane(JSplitPane.VERTICAL_SPLIT, actionPanel, heroInfoPanel);//水平 - rightAreaPanel.setDividerLocation(550); +// rightAreaPanel = new JSplitPane(JSplitPane.VERTICAL_SPLIT, actionPanel, heroInfoPanel);//水平 +// rightAreaPanel.setDividerLocation(150); starterPanel = new StarterPanel(); if (!firstStart) { - centerPanel = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, groundPanel, rightAreaPanel); + this.add(groundPanel, BorderLayout.CENTER); +// centerPanel = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, groundPanel, rightAreaPanel); } else { - centerPanel = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, starterPanel, rightAreaPanel); +// centerPanel = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, starterPanel, rightAreaPanel); + this.add(starterPanel, BorderLayout.CENTER); } - centerPanel.setDividerLocation(760); - this.add(centerPanel, BorderLayout.CENTER); +// centerPanel.setDividerLocation(760); +// this.add(centerPanel, BorderLayout.CENTER); -// this.add(mp2,BorderLayout.EAST); -// this.add(mp,BorderLayout.CENTER); - - //注册键盘监听 - //下面的语句翻译为 :当前类的监听者是mp this.addKeyListener(groundPanel); - this.setJMenuBar(jmb); - //焦点跳转 tab切换 + +// this.setJMenuBar(menuBar); + // 焦点跳转 tab切换 this.setFocusable(getFocusTraversalKeysEnabled()); //JFrame 窗体的属性 - this.setTitle("Tank"); - this.setLocation(150, 60); - this.setSize(1000, 625); this.setIconImage(AvatarImgMgr.instance.curImg); final long beforeVisible = System.currentTimeMillis(); this.setVisible(true); - this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); final long now = System.currentTimeMillis(); log.info("[init] mainFrame. total:{} visible:{}", (now - start), (now - beforeVisible)); } diff --git a/gui/src/main/java/com/github/kuangcp/tank/v3/PlayStageMgr.java b/gui/src/main/java/com/github/kuangcp/tank/v3/PlayStageMgr.java index ce7fdc56..4977b1ac 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v3/PlayStageMgr.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v3/PlayStageMgr.java @@ -41,7 +41,7 @@ public class PlayStageMgr { /** * 敌人的数量 */ - static int enemySize = 8; + static int enemySize = 10; /** * 无敌状态 时间 */ @@ -75,7 +75,7 @@ public void markStartLogic() { public static void init(Hero hero, List enemyTanks, List bricks, List irons) { instance = new PlayStageMgr(hero, enemyTanks, bricks, irons); - instance.border = new StageBorder(20, 742, 20, 545); + instance.border = new StageBorder(20, 740, 20, 540); // instance.border = new StageBorder(20, 1600, 20, 900); } @@ -191,4 +191,15 @@ public static boolean ablePassByHero(Tank t) { return TankTool.ablePass(t, instance.hero); } + + public int getLiveEnemy() { + int result = 0; + for (int i = 0; i < this.enemyTanks.size(); i++) { + final EnemyTank enemyTank = enemyTanks.get(i); + if (enemyTank.isAlive()) { + result++; + } + } + return result; + } } diff --git a/gui/src/main/java/com/github/kuangcp/tank/v3/SettingFrame.java b/gui/src/main/java/com/github/kuangcp/tank/v3/SettingFrame.java index 4ca4206d..701c9010 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v3/SettingFrame.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v3/SettingFrame.java @@ -1,5 +1,6 @@ package com.github.kuangcp.tank.v3; +import com.github.kuangcp.tank.constant.ButtonCommand; import com.github.kuangcp.tank.constant.SettingCommand; import com.github.kuangcp.tank.domain.Bullet; import com.github.kuangcp.tank.domain.Hero; @@ -93,6 +94,10 @@ public void actionPerformed(ActionEvent event) { return; } + if (event.getActionCommand().equals(ButtonCommand.SETTING_FRAME)) { + SettingFrame.activeFocus(); + } + if (event.getActionCommand().equals(SettingCommand.SHOT_SPEED_INCREMENT)) { Bullet.setSpeed(Bullet.getSpeed() + 1); } diff --git a/gui/src/main/resources/tank/conf/stage-map.properties b/gui/src/main/resources/tank/conf/stage-map.properties new file mode 100644 index 00000000..e69de29b diff --git a/gui/src/test/java/com/github/kuangcp/tank/util/ListConTest.java b/gui/src/test/java/com/github/kuangcp/tank/util/ListConTest.java new file mode 100644 index 00000000..08292949 --- /dev/null +++ b/gui/src/test/java/com/github/kuangcp/tank/util/ListConTest.java @@ -0,0 +1,7 @@ +package com.github.kuangcp.tank.util; + +/** + * @author https://github.com/kuangcp on 2021-10-17 21:45 + */ +public class ListConTest { +} From d673a43d9ded7e7c6d2de1b95ecc70bc897ae9dc Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Sun, 17 Oct 2021 23:35:10 +0800 Subject: [PATCH 247/476] *) avoid init --- .../kuangcp/tank/panel/HeroInfoPanel.java | 99 ----------- .../kuangcp/tank/panel/StarterPanel.java | 9 +- .../kuangcp/tank/panel/TankGroundPanel.java | 5 +- .../event/HeroInfoPanelRefreshEvent.java | 27 --- .../com/github/kuangcp/tank/v3/MainFrame.java | 157 ++++++------------ 5 files changed, 55 insertions(+), 242 deletions(-) delete mode 100644 gui/src/main/java/com/github/kuangcp/tank/panel/HeroInfoPanel.java delete mode 100644 gui/src/main/java/com/github/kuangcp/tank/panel/event/HeroInfoPanelRefreshEvent.java diff --git a/gui/src/main/java/com/github/kuangcp/tank/panel/HeroInfoPanel.java b/gui/src/main/java/com/github/kuangcp/tank/panel/HeroInfoPanel.java deleted file mode 100644 index 7b6224d7..00000000 --- a/gui/src/main/java/com/github/kuangcp/tank/panel/HeroInfoPanel.java +++ /dev/null @@ -1,99 +0,0 @@ -package com.github.kuangcp.tank.panel; - - -import com.github.kuangcp.tank.domain.EnemyTank; -import com.github.kuangcp.tank.domain.Tank; -import com.github.kuangcp.tank.panel.event.HeroInfoPanelRefreshEvent; -import com.github.kuangcp.tank.v3.PlayStageMgr; -import lombok.extern.slf4j.Slf4j; - -import javax.swing.*; -import java.awt.*; -import java.util.List; -import java.util.Objects; - -/** - * 就是一个用来显示属性的画板 - * JLabel 被add后 就算他发生改变,对之前已经add了的组件 不会有影响 - * 但是可以用setText来进行改变 - */ -@Slf4j -@SuppressWarnings("serial") -public class HeroInfoPanel extends JPanel { - - JLabel tankCounter; - JLabel prizeNo; - List ets; - HeroInfoPanelRefreshEvent refreshEvent; - - static final Tank heroIcon = new Tank(15, 20, 0) { - @Override - public void drawSelf(Graphics g) { - g.setColor(Color.yellow); - super.drawSelf(g); - } - - @Override - public void run() { - - } - }; - static final Tank enemyIcon = new Tank(70, 20, 0) { - @Override - public void drawSelf(Graphics g) { - g.setColor(Color.cyan); - super.drawSelf(g); - } - - @Override - public void run() { - - } - }; - - public void exit() { - if (Objects.nonNull(refreshEvent)) { - this.refreshEvent.stop(); - } - } - - public void setEts(List ets) { - this.ets = ets; - } - - public HeroInfoPanel(JLabel tankCounter, List ets, JLabel prizeNo) { - this.tankCounter = tankCounter; - this.ets = ets; - this.prizeNo = prizeNo; - - tankCounter.setLocation(15, -20); - } - - public void paint(Graphics g) { - super.paint(g); - - heroIcon.drawSelf(g); - enemyIcon.drawSelf(g); - } - - public void refreshData() { - if (PlayStageMgr.stageNoneStart() || PlayStageMgr.pause) { - return; - } - - final String format = " : %d : %d"; - final String txt; - if (PlayStageMgr.instance.hero.isAlive()) { - txt = String.format(format, PlayStageMgr.instance.hero.getLife(), ets.size()); - } else { - txt = String.format(format, 0, ets.size()); - } - tankCounter.setText(txt); - - prizeNo.setText("战绩: " + PlayStageMgr.instance.hero.getPrize()); - } - - public void setRefreshEvent(HeroInfoPanelRefreshEvent refreshEvent) { - this.refreshEvent = refreshEvent; - } -} diff --git a/gui/src/main/java/com/github/kuangcp/tank/panel/StarterPanel.java b/gui/src/main/java/com/github/kuangcp/tank/panel/StarterPanel.java index 046a1e27..7bf7b4a4 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/panel/StarterPanel.java +++ b/gui/src/main/java/com/github/kuangcp/tank/panel/StarterPanel.java @@ -9,25 +9,22 @@ /** * 绘制开始游戏的界面 - *

- * 如果要实现闪烁效果 就实现接口,定义一个变量,run里面++, - * 设置变量满足什么条件再画出图片 */ @Slf4j public class StarterPanel extends JPanel { - public Image First; + public Image firstInfoImg; public StarterPanel() { try { - First = ImageIO.read(getClass().getResource("/tank/img/Tank.jpg")); + firstInfoImg = ImageIO.read(getClass().getResource("/tank/img/Tank.jpg")); } catch (IOException e) { log.error("", e); } } public void paint(Graphics g) { - g.drawImage(First, 0, 0, 760, 560, this); + g.drawImage(firstInfoImg, 0, 0, 760, 560, this); //直接设置字符串,不用图片 Font my = new Font("微软雅黑", Font.BOLD, 15); diff --git a/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java b/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java index 983d4ca0..d77ae01e 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java +++ b/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java @@ -162,8 +162,9 @@ private void drawBg(Graphics g) { private void drawHeroInfo(Graphics g) { g.setColor(Color.GREEN); // g.setFont(Font.getFont("IBM Plex Mono")); - final String lifeInfo = "Life:" + PlayStageMgr.instance.hero.getLife() - + " enemy: " + PlayStageMgr.instance.getLiveEnemy(); + final String lifeInfo = "Life:" + hero.getLife() + + " Enemy: " + PlayStageMgr.instance.getLiveEnemy() + + " Prize: " + hero.getPrize(); g.drawString(lifeInfo, PlayStageMgr.instance.border.getMinX(), 15); } diff --git a/gui/src/main/java/com/github/kuangcp/tank/panel/event/HeroInfoPanelRefreshEvent.java b/gui/src/main/java/com/github/kuangcp/tank/panel/event/HeroInfoPanelRefreshEvent.java deleted file mode 100644 index 58e05a4d..00000000 --- a/gui/src/main/java/com/github/kuangcp/tank/panel/event/HeroInfoPanelRefreshEvent.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.github.kuangcp.tank.panel.event; - -import com.github.kuangcp.tank.panel.HeroInfoPanel; -import com.github.kuangcp.tank.util.executor.AbstractLoopEvent; -import lombok.extern.slf4j.Slf4j; - -/** - * @author https://github.com/kuangcp on 2021-09-17 01:58 - */ -@Slf4j -public class HeroInfoPanelRefreshEvent extends AbstractLoopEvent { - - public static final long fixedDelayTime = 600; - - private final HeroInfoPanel heroInfoPanel; - - public HeroInfoPanelRefreshEvent(HeroInfoPanel heroInfoPanel) { - this.heroInfoPanel = heroInfoPanel; - - this.setFixedDelayTime(fixedDelayTime); - } - - @Override - public void run() { - heroInfoPanel.refreshData(); - } -} diff --git a/gui/src/main/java/com/github/kuangcp/tank/v3/MainFrame.java b/gui/src/main/java/com/github/kuangcp/tank/v3/MainFrame.java index 1fa00d6b..6a6a3f5d 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v3/MainFrame.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v3/MainFrame.java @@ -1,7 +1,5 @@ package com.github.kuangcp.tank.v3; -import com.github.kuangcp.tank.constant.ButtonCommand; -import com.github.kuangcp.tank.panel.HeroInfoPanel; import com.github.kuangcp.tank.panel.StageActionPanel; import com.github.kuangcp.tank.panel.StarterPanel; import com.github.kuangcp.tank.panel.TankGroundPanel; @@ -10,7 +8,6 @@ import javax.swing.*; import java.awt.*; -import java.util.Objects; /** * 坦克3.0 版本: @@ -39,31 +36,28 @@ @SuppressWarnings("serial") public class MainFrame extends JFrame implements Runnable { - public volatile TankGroundPanel groundPanel = null;//坦克的主画板 + public volatile TankGroundPanel groundPanel;//坦克的主画板 public static StageActionPanel actionPanel = null;//放按钮的画板 - public HeroInfoPanel heroInfoPanel = null; //显示属性的画板 -// private HeroInfoPanelRefreshEvent loopEvent = null; - public StarterPanel starterPanel; - public JLabel tankCounter = null, me = null, prizeNo = null; public boolean firstStart = true; //判断是否首次运行, 通过开始按钮触发 //作出我需要的菜单 - JMenuBar menuBar = null; +// JMenuBar menuBar = null; //开始游戏 - JMenu gameMenu = null; - JMenu helpMenu = null; - JMenuItem jmil = null; +// JMenu gameMenu = null; +// JMenu helpMenu = null; +// JMenuItem jmil = null; //退出系统 - JMenuItem jmi2 = null; +// JMenuItem jmi2 = null; //存盘退出 - JMenuItem jmi3 = null; - JMenuItem jmi4 = null; +// JMenuItem jmi3 = null; +// JMenuItem jmi4 = null; //帮助窗口 - JMenuItem setting = null; +// JMenuItem setting = null; public MainFrame() { + starterPanel = new StarterPanel(); groundPanel = new TankGroundPanel(); actionPanel = new StageActionPanel(this); @@ -75,7 +69,7 @@ public MainFrame() { this.setLocation(680, 290); this.setSize(760, 590); -// this.setUndecorated(true); + this.setUndecorated(true); } public void run() { @@ -86,111 +80,58 @@ public void run() { // groundPanel = new TankGroundPanel(); // } - if (Objects.nonNull(heroInfoPanel)) { -// loopEvent.stop(); - heroInfoPanel.setEts(groundPanel.enemyList); - } else { - tankCounter = new JLabel(""); - prizeNo = new JLabel(""); - me = new JLabel("Myth"); - - heroInfoPanel = new HeroInfoPanel(tankCounter, groundPanel.enemyList, prizeNo); - } - -// loopEvent = new HeroInfoPanelRefreshEvent(heroInfoPanel); -// heroInfoPanel.setRefreshEvent(loopEvent); -// MonitorExecutor.addLoopEvent(loopEvent); - - //创建菜单及菜单选项 - menuBar = new JMenuBar(); - gameMenu = new JMenu("Game(G)"); - helpMenu = new JMenu("Help(H)"); - //设置快捷方式 - gameMenu.setMnemonic('G'); - helpMenu.setMnemonic('H'); - - jmil = new JMenuItem("New Game(N)"); - jmi2 = new JMenuItem("Pause"); - jmi3 = new JMenuItem("Save & Exit(C)"); - jmi4 = new JMenuItem("ContinueLast(S)"); - - setting = new JMenuItem("Setting"); - setting.addActionListener(SettingFrame.instance); - setting.setActionCommand(ButtonCommand.SETTING_FRAME); - helpMenu.add(setting); - - jmi4.addActionListener(actionPanel); - jmi4.setActionCommand("Continue"); - - jmi3.addActionListener(actionPanel); - jmi3.setActionCommand("saveExit"); - - jmi2.addActionListener(actionPanel); - jmi2.setActionCommand("暂停"); - jmi2.setMnemonic('E'); - - jmil.addActionListener(actionPanel); - jmil.setActionCommand(ButtonCommand.START); - - gameMenu.add(jmil); - - final JMenuItem exitItem = new JMenuItem("Exit"); - exitItem.addActionListener(actionPanel); - exitItem.setActionCommand(ButtonCommand.EXIT); - gameMenu.add(exitItem); - - gameMenu.add(jmi3); - gameMenu.add(jmi4); - - menuBar.add(gameMenu); - menuBar.add(helpMenu); - -// jb3 = new JButton("暂停游戏"); -// jb3.addActionListener(actionPanel); //注册监听 -// jb3.setActionCommand(ButtonCommand.PAUSE); -// -// jb4 = new JButton("继续游戏"); -// jb4.addActionListener(actionPanel); //注册监听 -// jb4.setActionCommand(ButtonCommand.RESUME); +// menuBar = new JMenuBar(); +// gameMenu = new JMenu("Game(G)"); +// helpMenu = new JMenu("Help(H)"); +// //设置快捷方式 +// gameMenu.setMnemonic('G'); +// helpMenu.setMnemonic('H'); + +// jmil = new JMenuItem("New Game(N)"); +// jmi2 = new JMenuItem("Pause"); +// jmi3 = new JMenuItem("Save & Exit(C)"); +// jmi4 = new JMenuItem("ContinueLast(S)"); + +// setting = new JMenuItem("Setting"); +// setting.addActionListener(SettingFrame.instance); +// setting.setActionCommand(ButtonCommand.SETTING_FRAME); +// helpMenu.add(setting); + +// jmi4.addActionListener(actionPanel); +// jmi4.setActionCommand("Continue"); // -// jb2 = new JButton("退出游戏"); -// jb2.addActionListener(actionPanel); //注册监听 -// jb2.setActionCommand(ButtonCommand.EXIT); +// jmi3.addActionListener(actionPanel); +// jmi3.setActionCommand("saveExit"); // -// startBtn = new JButton(firstStart ? "游戏开始" : "重新开始"); -// startBtn.addActionListener(actionPanel); -// startBtn.setActionCommand(ButtonCommand.START); +// jmi2.addActionListener(actionPanel); +// jmi2.setActionCommand("暂停"); +// jmi2.setMnemonic('E'); // -// actionPanel.add(startBtn); -// actionPanel.add(jb2); -// actionPanel.add(jb3); -// actionPanel.add(jb4); +// jmil.addActionListener(actionPanel); +// jmil.setActionCommand(ButtonCommand.START); - //显示属性的窗体: - heroInfoPanel.setLayout(new GridLayout(3, 1, 0, 0)); - - heroInfoPanel.add(tankCounter);//网格布局 - heroInfoPanel.add(prizeNo); - heroInfoPanel.add(me); - -// rightAreaPanel = new JSplitPane(JSplitPane.VERTICAL_SPLIT, actionPanel, heroInfoPanel);//水平 -// rightAreaPanel.setDividerLocation(150); +// gameMenu.add(jmil); +// +// final JMenuItem exitItem = new JMenuItem("Exit"); +// exitItem.addActionListener(actionPanel); +// exitItem.setActionCommand(ButtonCommand.EXIT); +// gameMenu.add(exitItem); +// +// gameMenu.add(jmi3); +// gameMenu.add(jmi4); +// +// menuBar.add(gameMenu); +// menuBar.add(helpMenu); - starterPanel = new StarterPanel(); if (!firstStart) { this.add(groundPanel, BorderLayout.CENTER); -// centerPanel = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, groundPanel, rightAreaPanel); } else { -// centerPanel = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, starterPanel, rightAreaPanel); this.add(starterPanel, BorderLayout.CENTER); } -// centerPanel.setDividerLocation(760); -// this.add(centerPanel, BorderLayout.CENTER); this.addKeyListener(groundPanel); -// this.setJMenuBar(menuBar); // 焦点跳转 tab切换 this.setFocusable(getFocusTraversalKeysEnabled()); From 0f175dbeaa0aa23777fde9517cf3476a905f5d8a Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Sun, 17 Oct 2021 23:37:52 +0800 Subject: [PATCH 248/476] *) remove useless menu --- .../com/github/kuangcp/tank/v3/MainFrame.java | 70 +------------------ 1 file changed, 2 insertions(+), 68 deletions(-) diff --git a/gui/src/main/java/com/github/kuangcp/tank/v3/MainFrame.java b/gui/src/main/java/com/github/kuangcp/tank/v3/MainFrame.java index 6a6a3f5d..c4264bc7 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v3/MainFrame.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v3/MainFrame.java @@ -17,18 +17,10 @@ * 3 打到敌人坦克敌人消失 做出爆炸效果 * 4 让敌方坦克可以自由的随机上下左右移动 * 5 控制我方坦克和敌方坦克在规定范围内移动 - * 6 有窗口提示信息 + * 6 有相关提示信息刷新 * 7 坦克不重叠,与障碍物不重叠 - *

- *

- * 8 可以把这里的窗体做成线程 但是都是静态成员,所以就算做成线程 出来的新界面也还是原来的属性(一样的界面) - * 总结:不应该随便设置静态成员的 尤其是可能会新建的成员 - * 解决方案: - * 在需要用到无法引用到的对象时,除了做成静态,还可以在当前添加这个对象的引用 - * 然后构造器 里传入所要调用的对象 就能解决问题了,多线程启动也会是初始化的状态 - * !!最好放在别的函数里,不要改动构造器,会对以前的代码有影响() + * 8 游戏结束机制 * 9 可以暂停游戏,继续游戏 - * 原来这么简单,设置动的那些Speed为0 不就是静止么。。还有方向不能动 还有焦点的自动跳转 * 10 可以继续上局游戏开始玩 * 11 有游戏的音效(操纵声音文件) */ @@ -42,20 +34,6 @@ public class MainFrame extends JFrame implements Runnable { public StarterPanel starterPanel; public boolean firstStart = true; //判断是否首次运行, 通过开始按钮触发 - //作出我需要的菜单 -// JMenuBar menuBar = null; - //开始游戏 -// JMenu gameMenu = null; -// JMenu helpMenu = null; -// JMenuItem jmil = null; - //退出系统 -// JMenuItem jmi2 = null; - //存盘退出 -// JMenuItem jmi3 = null; -// JMenuItem jmi4 = null; - //帮助窗口 -// JMenuItem setting = null; - public MainFrame() { starterPanel = new StarterPanel(); groundPanel = new TankGroundPanel(); @@ -80,50 +58,6 @@ public void run() { // groundPanel = new TankGroundPanel(); // } - //创建菜单及菜单选项 -// menuBar = new JMenuBar(); -// gameMenu = new JMenu("Game(G)"); -// helpMenu = new JMenu("Help(H)"); -// //设置快捷方式 -// gameMenu.setMnemonic('G'); -// helpMenu.setMnemonic('H'); - -// jmil = new JMenuItem("New Game(N)"); -// jmi2 = new JMenuItem("Pause"); -// jmi3 = new JMenuItem("Save & Exit(C)"); -// jmi4 = new JMenuItem("ContinueLast(S)"); - -// setting = new JMenuItem("Setting"); -// setting.addActionListener(SettingFrame.instance); -// setting.setActionCommand(ButtonCommand.SETTING_FRAME); -// helpMenu.add(setting); - -// jmi4.addActionListener(actionPanel); -// jmi4.setActionCommand("Continue"); -// -// jmi3.addActionListener(actionPanel); -// jmi3.setActionCommand("saveExit"); -// -// jmi2.addActionListener(actionPanel); -// jmi2.setActionCommand("暂停"); -// jmi2.setMnemonic('E'); -// -// jmil.addActionListener(actionPanel); -// jmil.setActionCommand(ButtonCommand.START); - -// gameMenu.add(jmil); -// -// final JMenuItem exitItem = new JMenuItem("Exit"); -// exitItem.addActionListener(actionPanel); -// exitItem.setActionCommand(ButtonCommand.EXIT); -// gameMenu.add(exitItem); -// -// gameMenu.add(jmi3); -// gameMenu.add(jmi4); -// -// menuBar.add(gameMenu); -// menuBar.add(helpMenu); - if (!firstStart) { this.add(groundPanel, BorderLayout.CENTER); } else { From 883033e599a490899d73dd2af14c864a6df07c11 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Mon, 18 Oct 2021 00:08:51 +0800 Subject: [PATCH 249/476] *) border init --- .../kuangcp/tank/panel/TankGroundPanel.java | 7 ++-- .../kuangcp/tank/resource/ResourceMgr.java | 3 ++ .../github/kuangcp/tank/v2/MainPanelV2.java | 5 +-- .../github/kuangcp/tank/v2/TankGameV2.java | 3 +- .../com/github/kuangcp/tank/v3/MainFrame.java | 34 ++++++++++++++++++- .../github/kuangcp/tank/v3/PlayStageMgr.java | 16 ++++----- .../github/kuangcp/tank/v3/RoundMapMgr.java | 8 +++++ .../github/kuangcp/tank/v3/StageBorder.java | 8 +++++ 8 files changed, 67 insertions(+), 17 deletions(-) diff --git a/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java b/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java index d77ae01e..0d0f4a99 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java +++ b/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java @@ -19,6 +19,7 @@ import com.github.kuangcp.tank.util.executor.MonitorExecutor; import com.github.kuangcp.tank.v3.MainFrame; import com.github.kuangcp.tank.v3.PlayStageMgr; +import com.github.kuangcp.tank.v3.RoundMapMgr; import com.github.kuangcp.tank.v3.SettingFrame; import com.github.kuangcp.tank.v3.StageBorder; import lombok.extern.slf4j.Slf4j; @@ -152,7 +153,7 @@ public void startNewRound() { private void drawBg(Graphics g) { g.setColor(ColorMgr.instance.bgColor); - final StageBorder border = PlayStageMgr.instance.border; + final StageBorder border = RoundMapMgr.instance.border; g.fillRect(0, 0, border.getMinX() + border.getMaxX(), border.getMinY() + border.getMaxY()); g.setColor(Color.green); g.drawRect(border.getMinX(), border.getMinY(), @@ -165,12 +166,12 @@ private void drawHeroInfo(Graphics g) { final String lifeInfo = "Life:" + hero.getLife() + " Enemy: " + PlayStageMgr.instance.getLiveEnemy() + " Prize: " + hero.getPrize(); - g.drawString(lifeInfo, PlayStageMgr.instance.border.getMinX(), 15); + g.drawString(lifeInfo, RoundMapMgr.instance.border.getMinX(), 15); } private void drawMonitorInfo(Graphics g) { g.setColor(Color.LIGHT_GRAY); - g.drawString(MonitorExecutor.info.toString(), PlayStageMgr.instance.border.getMinX(), 555); + g.drawString(MonitorExecutor.info.toString(), RoundMapMgr.instance.border.getMinX(), 555); } @Override diff --git a/gui/src/main/java/com/github/kuangcp/tank/resource/ResourceMgr.java b/gui/src/main/java/com/github/kuangcp/tank/resource/ResourceMgr.java index 72b82c6c..dbb48982 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/resource/ResourceMgr.java +++ b/gui/src/main/java/com/github/kuangcp/tank/resource/ResourceMgr.java @@ -1,6 +1,7 @@ package com.github.kuangcp.tank.resource; import com.github.kuangcp.tank.mgr.BombMgr; +import com.github.kuangcp.tank.v3.RoundMapMgr; import lombok.extern.slf4j.Slf4j; /** @@ -19,5 +20,7 @@ public static void loadResource() { DefeatImgMgr.instance.loadImg(); AvatarImgMgr.instance.loadImg(); VictoryImgMgr.instance.loadImg(); + + RoundMapMgr.init(); } } diff --git a/gui/src/main/java/com/github/kuangcp/tank/v2/MainPanelV2.java b/gui/src/main/java/com/github/kuangcp/tank/v2/MainPanelV2.java index c67d1623..89e27ed4 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v2/MainPanelV2.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v2/MainPanelV2.java @@ -7,6 +7,7 @@ import com.github.kuangcp.tank.util.TankTool; import com.github.kuangcp.tank.util.executor.LoopEventExecutor; import com.github.kuangcp.tank.v3.PlayStageMgr; +import com.github.kuangcp.tank.v3.RoundMapMgr; import com.github.kuangcp.tank.v3.StageBorder; import lombok.extern.slf4j.Slf4j; @@ -46,7 +47,7 @@ public MainPanelV2() { ets.add(new EnemyTank(250, 100, DirectType.UP)); for (int i = 0; i < 700; i++) { - ets.add(new EnemyTank(20 + i * 40 % PlayStageMgr.instance.border.getMaxX(), 100 + i * 5 % PlayStageMgr.instance.border.getMaxY(), DirectType.UP)); + ets.add(new EnemyTank(20 + i * 40 % RoundMapMgr.instance.border.getMaxX(), 100 + i * 5 % RoundMapMgr.instance.border.getMaxY(), DirectType.UP)); } for (EnemyTank et : ets) { @@ -59,7 +60,7 @@ public MainPanelV2() { public void paint(Graphics g) { super.paint(g); - final StageBorder border = PlayStageMgr.instance.border; + final StageBorder border = RoundMapMgr.instance.border; g.fillRect(0, 0, border.getMaxX() + border.getMinX(), border.getMaxY() + border.getMinY()); //调用函数绘画出主坦克 diff --git a/gui/src/main/java/com/github/kuangcp/tank/v2/TankGameV2.java b/gui/src/main/java/com/github/kuangcp/tank/v2/TankGameV2.java index e5b29552..f88328d1 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v2/TankGameV2.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v2/TankGameV2.java @@ -3,6 +3,7 @@ import com.github.kuangcp.tank.domain.Hero; import com.github.kuangcp.tank.util.executor.MonitorExecutor; import com.github.kuangcp.tank.v3.PlayStageMgr; +import com.github.kuangcp.tank.v3.RoundMapMgr; import com.github.kuangcp.tank.v3.StageBorder; import javax.swing.*; @@ -32,7 +33,7 @@ public TankGameV2() { final Thread panelThread = new Thread(panel); panelThread.start(); - final StageBorder border = PlayStageMgr.instance.border; + final StageBorder border = RoundMapMgr.instance.border; this.setLocation(200, 50); this.setSize(border.getMaxX() + border.getMinX(), border.getMaxY() + border.getMinY() * 2); diff --git a/gui/src/main/java/com/github/kuangcp/tank/v3/MainFrame.java b/gui/src/main/java/com/github/kuangcp/tank/v3/MainFrame.java index c4264bc7..5ecd45c4 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v3/MainFrame.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v3/MainFrame.java @@ -8,6 +8,9 @@ import javax.swing.*; import java.awt.*; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseMotionAdapter; /** * 坦克3.0 版本: @@ -45,7 +48,7 @@ public MainFrame() { this.setTitle("Tank"); this.setLocation(680, 290); - this.setSize(760, 590); + this.setSize(RoundMapMgr.instance.border.getTotalX(), RoundMapMgr.instance.border.getTotalY()); this.setUndecorated(true); } @@ -64,6 +67,7 @@ public void run() { this.add(starterPanel, BorderLayout.CENTER); } + this.supportMouseMoveWindow(); this.addKeyListener(groundPanel); // 焦点跳转 tab切换 @@ -77,4 +81,32 @@ public void run() { final long now = System.currentTimeMillis(); log.info("[init] mainFrame. total:{} visible:{}", (now - start), (now - beforeVisible)); } + + private void supportMouseMoveWindow() { + class DeltaLocation { + int xOld = 0; + int yOld = 0; + } + + final DeltaLocation deltaLocation = new DeltaLocation(); + //处理拖动事件 + this.addMouseListener(new MouseAdapter() { + @Override + public void mousePressed(MouseEvent e) { + deltaLocation.xOld = e.getX(); + deltaLocation.yOld = e.getY(); + } + }); + final MainFrame mainFrame = this; + this.addMouseMotionListener(new MouseMotionAdapter() { + @Override + public void mouseDragged(MouseEvent e) { + int xOnScreen = e.getXOnScreen(); + int yOnScreen = e.getYOnScreen(); + int xx = xOnScreen - deltaLocation.xOld; + int yy = yOnScreen - deltaLocation.yOld; + mainFrame.setLocation(xx, yy); + } + }); + } } \ No newline at end of file diff --git a/gui/src/main/java/com/github/kuangcp/tank/v3/PlayStageMgr.java b/gui/src/main/java/com/github/kuangcp/tank/v3/PlayStageMgr.java index 4977b1ac..8f84ac1f 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v3/PlayStageMgr.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v3/PlayStageMgr.java @@ -26,7 +26,6 @@ public class PlayStageMgr { public static PlayStageMgr instance = null; public Hero hero; - public StageBorder border = null; public boolean startLogic = false; public boolean winCurRound = false; public int roundPrize = 0; @@ -74,9 +73,6 @@ public void markStartLogic() { */ public static void init(Hero hero, List enemyTanks, List bricks, List irons) { instance = new PlayStageMgr(hero, enemyTanks, bricks, irons); - - instance.border = new StageBorder(20, 740, 20, 540); -// instance.border = new StageBorder(20, 1600, 20, 900); } private PlayStageMgr(Hero hero, List enemyTanks, List bricks, List irons) { @@ -149,13 +145,13 @@ public boolean willInBorder(Tank tank) { } switch (tank.getDirect()) { case DirectType.UP: - return tank.getY() - tank.getHalfHeight() - tank.getSpeed() > border.getMinY(); + return tank.getY() - tank.getHalfHeight() - tank.getSpeed() > RoundMapMgr.instance.border.getMinY(); case DirectType.DOWN: - return tank.getY() + tank.getHalfHeight() + tank.getSpeed() < border.getMaxY(); + return tank.getY() + tank.getHalfHeight() + tank.getSpeed() < RoundMapMgr.instance.border.getMaxY(); case DirectType.LEFT: - return tank.getX() - tank.getHalfHeight() - tank.getSpeed() > border.getMinX(); + return tank.getX() - tank.getHalfHeight() - tank.getSpeed() > RoundMapMgr.instance.border.getMinX(); case DirectType.RIGHT: - return tank.getX() + tank.getHalfHeight() + tank.getSpeed() < border.getMaxX(); + return tank.getX() + tank.getHalfHeight() + tank.getSpeed() < RoundMapMgr.instance.border.getMaxX(); } return false; } @@ -164,8 +160,8 @@ public boolean willInBorder(Bullet bullet) { if (Objects.isNull(bullet)) { return false; } - return bullet.sx <= border.getMinX() || bullet.sx >= border.getMaxX() - || bullet.sy <= border.getMinY() || bullet.sy >= border.getMaxY(); + return bullet.sx <= RoundMapMgr.instance.border.getMinX() || bullet.sx >= RoundMapMgr.instance.border.getMaxX() + || bullet.sy <= RoundMapMgr.instance.border.getMinY() || bullet.sy >= RoundMapMgr.instance.border.getMaxY(); } public static int getEnemySize() { diff --git a/gui/src/main/java/com/github/kuangcp/tank/v3/RoundMapMgr.java b/gui/src/main/java/com/github/kuangcp/tank/v3/RoundMapMgr.java index 77fc3d21..2cee31a3 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v3/RoundMapMgr.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v3/RoundMapMgr.java @@ -10,6 +10,14 @@ */ public class RoundMapMgr { + public static final RoundMapMgr instance = new RoundMapMgr(); public List bricks; // 砖 public List irons; // 铁 + + public StageBorder border = null; + + public static void init() { + instance.border = new StageBorder(20, 740, 20, 540); +// instance.border = new StageBorder(20, 1600, 20, 900); + } } diff --git a/gui/src/main/java/com/github/kuangcp/tank/v3/StageBorder.java b/gui/src/main/java/com/github/kuangcp/tank/v3/StageBorder.java index c3b5c33a..a2815fbd 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v3/StageBorder.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v3/StageBorder.java @@ -32,4 +32,12 @@ public int getMinY() { public int getMaxY() { return maxY; } + + public int getTotalX() { + return minX + maxX; + } + + public int getTotalY() { + return minY + maxY; + } } From e95cd0a86ff7d3610a3218dc05aa4fa42e3fee8a Mon Sep 17 00:00:00 2001 From: kcp Date: Thu, 21 Oct 2021 23:12:16 +0800 Subject: [PATCH 250/476] *) config --- gui/Readme.md | 10 ++++++---- .../caculator/CalculateBtnIncreaseActionAdapter.java | 2 +- .../com/github/kuangcp/tank/panel/StarterPanel.java | 4 +++- .../github/kuangcp/tank/resource/PropertiesMgr.java | 1 + gui/src/main/resources/tank/conf/img.properties | 3 ++- 5 files changed, 13 insertions(+), 7 deletions(-) diff --git a/gui/Readme.md b/gui/Readme.md index 66fa8d5d..42142739 100644 --- a/gui/Readme.md +++ b/gui/Readme.md @@ -1,9 +1,11 @@ # GUI -gradle tank 构建坦克游戏 -gradle note 构建记事本 -gradle virus 构建病毒模拟 -gradle calc 构建计算器 +gradle tank run -x test 直接运行 + +gradle tank -x test 打包坦克游戏 +gradle note -x test 打包记事本 +gradle virus -x test 打包病毒模拟 +gradle calc -x test 打包计算器 ## GTK > [java-gnome](http://java-gnome.sourceforge.net/README.html) diff --git a/gui/src/main/java/com/github/kuangcp/caculator/CalculateBtnIncreaseActionAdapter.java b/gui/src/main/java/com/github/kuangcp/caculator/CalculateBtnIncreaseActionAdapter.java index c33c3081..f09bbcfd 100644 --- a/gui/src/main/java/com/github/kuangcp/caculator/CalculateBtnIncreaseActionAdapter.java +++ b/gui/src/main/java/com/github/kuangcp/caculator/CalculateBtnIncreaseActionAdapter.java @@ -10,7 +10,7 @@ */ class CalculateBtnIncreaseActionAdapter implements ActionListener { - private Calculator adapter; + private final Calculator adapter; CalculateBtnIncreaseActionAdapter(Calculator adapter) { this.adapter = adapter; diff --git a/gui/src/main/java/com/github/kuangcp/tank/panel/StarterPanel.java b/gui/src/main/java/com/github/kuangcp/tank/panel/StarterPanel.java index 79d96b41..190c0ca8 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/panel/StarterPanel.java +++ b/gui/src/main/java/com/github/kuangcp/tank/panel/StarterPanel.java @@ -1,6 +1,7 @@ package com.github.kuangcp.tank.panel; import lombok.extern.slf4j.Slf4j; +import com.github.kuangcp.tank.resource.PropertiesMgr; import javax.imageio.ImageIO; import javax.swing.*; @@ -20,7 +21,8 @@ public class StarterPanel extends JPanel { public StarterPanel() { try { - First = ImageIO.read(getClass().getResource("/images/Tank.jpg")); + String imgPath = PropertiesMgr.imgProperties.getProperty(PropertiesMgr.Key.Img.START_BG); + First = ImageIO.read(getClass().getResource(imgPath)); } catch (IOException e) { log.error("", e); } diff --git a/gui/src/main/java/com/github/kuangcp/tank/resource/PropertiesMgr.java b/gui/src/main/java/com/github/kuangcp/tank/resource/PropertiesMgr.java index dece8d67..2c6661b0 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/resource/PropertiesMgr.java +++ b/gui/src/main/java/com/github/kuangcp/tank/resource/PropertiesMgr.java @@ -19,6 +19,7 @@ interface Img { String ROUND_VICTORY = "round.victory"; String ANIMATION_BOMB = "animation.bomb"; + String START_BG = "round.start"; } } diff --git a/gui/src/main/resources/tank/conf/img.properties b/gui/src/main/resources/tank/conf/img.properties index eb128930..b1d3878b 100644 --- a/gui/src/main/resources/tank/conf/img.properties +++ b/gui/src/main/resources/tank/conf/img.properties @@ -1,4 +1,5 @@ round.avatar=/tank/img/Me4.jpg round.defeat=/tank/img/Over.jpg round.victory=/tank/img/Win2.jpg -animation.bomb=/tank/img/bomb_1.gif,/tank/img/bomb_2.gif,/tank/img/bomb_3.gif \ No newline at end of file +animation.bomb=/tank/img/bomb_1.gif,/tank/img/bomb_2.gif,/tank/img/bomb_3.gif +round.start=/tank/img/Tank.jpg \ No newline at end of file From ddecf79cacc35a394ce685925e357e4c6df1d81e Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Sat, 23 Oct 2021 23:51:36 +0800 Subject: [PATCH 251/476] x) memory leak --- .../com/github/kuangcp/tank/domain/Brick.java | 4 - .../github/kuangcp/tank/domain/EnemyTank.java | 1145 ++++++++--------- .../com/github/kuangcp/tank/mgr/BombMgr.java | 4 +- .../kuangcp/tank/panel/StageActionPanel.java | 7 +- .../kuangcp/tank/panel/TankGroundPanel.java | 63 +- .../github/kuangcp/tank/util/ExecutePool.java | 2 + .../github/kuangcp/tank/v3/PlayStageMgr.java | 2 +- 7 files changed, 595 insertions(+), 632 deletions(-) diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/Brick.java b/gui/src/main/java/com/github/kuangcp/tank/domain/Brick.java index 0a9de77e..b32f55ab 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/domain/Brick.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/Brick.java @@ -1,14 +1,10 @@ package com.github.kuangcp.tank.domain; -import java.awt.*; - /** * 砖块 */ public class Brick extends Hinder { - Graphics g; - public Brick(int hx, int hy) { super(hx, hy); } diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java b/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java index fca6bd71..148b0b87 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java @@ -1,22 +1,18 @@ - package com.github.kuangcp.tank.domain; - import com.github.kuangcp.tank.constant.DirectType; import com.github.kuangcp.tank.domain.robot.EnemyActionContext; import com.github.kuangcp.tank.domain.robot.RobotRate; import com.github.kuangcp.tank.domain.robot.RoundActionEnum; -import com.github.kuangcp.tank.util.TankTool; import com.github.kuangcp.tank.util.executor.LoopEventExecutor; import com.github.kuangcp.tank.v3.PlayStageMgr; import lombok.extern.slf4j.Slf4j; import java.awt.*; -import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.atomic.AtomicLong; @@ -28,27 +24,29 @@ public class EnemyTank extends Tank implements Runnable, RobotRate { private static final AtomicLong counter = new AtomicLong(); - public final long id; - public Bullet s = null; - public List bulletList = Collections.synchronizedList(new ArrayList<>());//子弹集合 + public final long id; + public List bulletList = new CopyOnWriteArrayList<>();//子弹集合 private long lastShotMs = 0; private long shotCDMs = 168; + public int INIT_MAX_LIVE_BULLET = 4; + public int maxLiveBullet = INIT_MAX_LIVE_BULLET + 1; // 活跃子弹 最大数 - public static int maxLiveShot = 3; // 活跃子弹 最大数 - public boolean delayRemove = false; // 延迟回收内存,避免子弹线程执行中断 + /** + * 延迟回收内存(标记使用一次) + * 避免子弹线程执行中断,突然消失 + */ + public boolean delayRemove = false; + /** + * 移动频率 + */ public int moveRate; + /** + * 发射子弹频率 + */ public int shotRate; - public List ets; - public List bricks; - public List irons; - - boolean overlap = true; //同类之间允许重叠 - boolean bri = true; - boolean ableMove = true; - /** * 随机生成 的 最大生命值, 每个生命值都有对应的颜色 * @@ -73,18 +71,23 @@ public EnemyTank(int x, int y, int direct) { type = 1; this.direct = direct; this.alive = true; - this.life = ThreadLocalRandom.current().nextInt(MAX_LIFE) + 1; - this.speed = (MAX_LIFE - life + 1) / 2 + 1; + + this.randomLife(); + this.resetPropByLife(); + +// this.life = ThreadLocalRandom.current().nextInt(MAX_LIFE) + 1; +// this.speed = Math.max((MAX_LIFE - life + 1) / 2, 1); +// log.info(": life={} speed={}", life, speed); this.id = counter.addAndGet(1); this.setFixedDelayTime(40); this.moveRate = 2; - this.shotRate = 17; + this.shotRate = 7; - this.afterBuild(); + this.registerDestroy(); } - private void afterBuild() { + private void registerDestroy() { this.registerHook(() -> { if (this.isAbort()) { for (Bullet d : this.bulletList) { @@ -96,6 +99,24 @@ private void afterBuild() { }); } + private void randomLife() { + double val = ThreadLocalRandom.current().nextGaussian(); + val = val * Math.sqrt(2) + MAX_LIFE / 3.0; + val = Math.min(Math.max(val, 1), MAX_LIFE); + this.life = (int) val; + } + + /** + * 依据生命值重置属性 + */ + private void resetPropByLife() { + this.speed = Math.max((MAX_LIFE - life + 1) / 2, 1); + if (this.life == 1) { + this.speed++; + } + this.maxLiveBullet = INIT_MAX_LIVE_BULLET + this.life; + } + @Override public int getMoveRate() { return this.moveRate; @@ -112,32 +133,10 @@ public void drawSelf(Graphics g) { super.drawSelf(g); } - public void SetInfo(Hero hero, List ets, List bricks, List irons) { - this.ets = ets; - this.bricks = bricks; - this.irons = irons; - } - - public boolean isOverlap() { - return overlap; - } - - public void setOverlap(boolean overlap) { - this.overlap = overlap; - } - - public boolean isBri() { - return bri; - } - - public void setBri(boolean bri) { - this.bri = bri; - } - @Override public void addLife(int delta) { super.addLife(delta); - this.speed = MAX_LIFE - this.life + 1; + this.resetPropByLife(); } /** @@ -149,33 +148,35 @@ public void finalShotAction() { if (lastShotMs != 0 && nowMs - lastShotMs < shotCDMs) { return; } - if (this.bulletList.size() >= maxLiveShot || !this.isAlive()) { + if (this.bulletList.size() >= maxLiveBullet || !this.isAlive()) { return; } - switch (this.getDirect()) { - case 0: {//0123 代表 上下左右 - s = new Bullet(this.getX() - 1, this.getY() - 15, 0); + case DirectType.UP: { + Bullet s = new Bullet(this.getX() - 1, this.getY() - 15, 0); bulletList.add(s); + LoopEventExecutor.addLoopEvent(s); break; } - case 1: { - s = new Bullet(this.getX() - 2, this.getY() + 15, 1); + case DirectType.DOWN: { + Bullet s = new Bullet(this.getX() - 2, this.getY() + 15, 1); bulletList.add(s); + LoopEventExecutor.addLoopEvent(s); break; } - case 2: { - s = new Bullet(this.getX() - 15 - 2, this.getY(), 2); + case DirectType.LEFT: { + Bullet s = new Bullet(this.getX() - 15 - 2, this.getY(), 2); bulletList.add(s); + LoopEventExecutor.addLoopEvent(s); break; } - case 3: { - s = new Bullet(this.getX() + 15 - 2, this.getY() - 1, 3); + case DirectType.RIGHT: { + Bullet s = new Bullet(this.getX() + 15 - 2, this.getY() - 1, 3); bulletList.add(s); + LoopEventExecutor.addLoopEvent(s); break; } } - LoopEventExecutor.addLoopEvent(s); // 常规线程池 // ExecutePool.shotPool.execute(s); @@ -188,278 +189,275 @@ public void finalShotAction() { } //还是没有用,因为坦克太多,会卡住。。。 - public boolean TouchOther() { - boolean flag = false;//没有碰到 - - switch (this.direct) { - case 0://上 - for (int i = 0; i < ets.size(); i++) { - EnemyTank et = ets.get(i); - if (et != this) { - if (et.direct == 0 || et.direct == 1) {//对方是上下 - //自己的上左 - if ((et.x - 10 <= this.x - 10 && et.x + 10 >= this.x - 10 && - et.y - 15 <= this.y - 15 && et.y + 15 >= this.y - 15)) { - flag = true; - } - //上右 - if (et.x - 10 <= this.x + 10 && et.x + 10 >= this.x + 10 && - et.y - 15 <= this.y - 15 && et.y + 15 >= this.y - 15) {//判断最前方两个点是否在对方坦克区域内 - flag = true; - } - } - if (et.direct == 2 || et.direct == 3) {//对方是左右 - //自己的上左 - if ((et.x - 15 <= this.x - 10 && et.x + 15 >= this.x - 10 && - et.y - 10 <= this.y - 15 && et.y + 10 >= this.y - 15)) { - flag = true; - } - //上右 - if (et.x - 15 <= this.x + 10 && et.x + 15 >= this.x + 10 && - et.y - 10 <= this.y - 15 && et.y + 10 >= this.y - 15) {//判断最前方两个点是否在对方坦克区域内 - flag = true; - } - } - } - } - break; - case 1: - for (int i = 0; i < ets.size(); i++) { - EnemyTank et = ets.get(i); - if (et != this) { - //对方是上下 - if (et.direct == 0 || et.direct == 1) { - //自己的下左 - if ((et.x - 10 <= this.x - 10 && et.x + 10 >= this.x - 10 && - et.y - 15 <= this.y + 15 && et.y + 15 >= this.y + 15)) { - flag = true; - } - //下右 - if (et.x - 10 <= this.x + 10 && et.x + 10 >= this.x + 10 && - et.y - 15 <= this.y + 15 && et.y + 15 >= this.y + 15) {//判断最前方两个点是否在对方坦克区域内 - flag = true; - } - } - //对方是左右 - if (et.direct == 2 || et.direct == 3) { - //自己的下左 - if ((et.x - 15 <= this.x - 10 && et.x + 15 >= this.x - 10 && - et.y - 10 <= this.y + 15 && et.y + 10 >= this.y + 15)) { - flag = true; - } - //下右 - if (et.x - 15 <= this.x + 10 && et.x + 15 >= this.x + 10 && - et.y - 10 <= this.y + 15 && et.y + 10 >= this.y + 15) {//判断最前方两个点是否在对方坦克区域内 - flag = true; - } - } - } - } - break; - case 2: - for (int i = 0; i < ets.size(); i++) { - EnemyTank et = ets.get(i); - if (et != this) { - if (et.direct == 0 || et.direct == 1) { - //对方是上下 - //自己的左上 - if ((et.x - 10 <= this.x - 15 && et.x + 10 >= this.x - 15 && - et.y - 15 <= this.y - 10 && et.y + 15 >= this.y - 10)) { - flag = true; - } - //右下 - if (et.x - 10 <= this.x - 15 && et.x + 10 >= this.x - 15 && - et.y - 15 <= this.y + 10 && et.y + 15 >= this.y + 10) {//判断最前方两个点是否在对方坦克区域内 - flag = true; - } - } - if (et.direct == 2 || et.direct == 3) { - //对方是左右 - //自己的左上 - if ((et.x - 15 <= this.x - 15 && et.x + 15 >= this.x - 15 && - et.y - 10 <= this.y - 10 && et.y + 10 >= this.y - 10)) { - flag = true; - } - //右下 - if (et.x - 15 <= this.x - 15 && et.x + 15 >= this.x - 15 && - et.y - 10 <= this.y + 10 && et.y + 10 >= this.y + 10) {//判断最前方两个点是否在对方坦克区域内 - flag = true; - } - } - } - } - break; - case 3: - for (int i = 0; i < ets.size(); i++) { - EnemyTank et = ets.get(i); - if (et != this) { - if (et.direct == 0 || et.direct == 1) { - //对方是上下 - //自己的左上 - if ((et.x - 10 <= this.x + 15 && et.x + 10 >= this.x + 15 && - et.y - 15 <= this.y - 10 && et.y + 15 >= this.y - 10)) { - flag = true; - } - //右下 - if (et.x - 10 <= this.x + 15 && et.x + 10 >= this.x + 15 && - et.y - 15 <= this.y + 10 && et.y + 15 >= this.y + 10) {//判断最前方两个点是否在对方坦克区域内 - flag = true; - } - } - if (et.direct == 2 || et.direct == 3) { - //对方是左右 - //自己的左上 - if ((et.x - 15 <= this.x + 15 && et.x + 15 >= this.x + 15 && - et.y - 10 <= this.y - 10 && et.y + 10 >= this.y - 10)) { - flag = true; - } - //右下 - if (et.x - 15 <= this.x + 15 && et.x + 15 >= this.x + 15 && - et.y - 10 <= this.y + 10 && et.y + 10 >= this.y + 10) {//判断最前方两个点是否在对方坦克区域内 - flag = true; - } - } - } - } - break; - } - - return flag; - } +// public boolean TouchOther() { +// boolean flag = false;//没有碰到 +// +// switch (this.direct) { +// case 0://上 +// for (int i = 0; i < ets.size(); i++) { +// EnemyTank et = ets.get(i); +// if (et != this) { +// if (et.direct == 0 || et.direct == 1) {//对方是上下 +// //自己的上左 +// if ((et.x - 10 <= this.x - 10 && et.x + 10 >= this.x - 10 && +// et.y - 15 <= this.y - 15 && et.y + 15 >= this.y - 15)) { +// flag = true; +// } +// //上右 +// if (et.x - 10 <= this.x + 10 && et.x + 10 >= this.x + 10 && +// et.y - 15 <= this.y - 15 && et.y + 15 >= this.y - 15) {//判断最前方两个点是否在对方坦克区域内 +// flag = true; +// } +// } +// if (et.direct == 2 || et.direct == 3) {//对方是左右 +// //自己的上左 +// if ((et.x - 15 <= this.x - 10 && et.x + 15 >= this.x - 10 && +// et.y - 10 <= this.y - 15 && et.y + 10 >= this.y - 15)) { +// flag = true; +// } +// //上右 +// if (et.x - 15 <= this.x + 10 && et.x + 15 >= this.x + 10 && +// et.y - 10 <= this.y - 15 && et.y + 10 >= this.y - 15) {//判断最前方两个点是否在对方坦克区域内 +// flag = true; +// } +// } +// } +// } +// break; +// case 1: +// for (int i = 0; i < ets.size(); i++) { +// EnemyTank et = ets.get(i); +// if (et != this) { +// //对方是上下 +// if (et.direct == 0 || et.direct == 1) { +// //自己的下左 +// if ((et.x - 10 <= this.x - 10 && et.x + 10 >= this.x - 10 && +// et.y - 15 <= this.y + 15 && et.y + 15 >= this.y + 15)) { +// flag = true; +// } +// //下右 +// if (et.x - 10 <= this.x + 10 && et.x + 10 >= this.x + 10 && +// et.y - 15 <= this.y + 15 && et.y + 15 >= this.y + 15) {//判断最前方两个点是否在对方坦克区域内 +// flag = true; +// } +// } +// //对方是左右 +// if (et.direct == 2 || et.direct == 3) { +// //自己的下左 +// if ((et.x - 15 <= this.x - 10 && et.x + 15 >= this.x - 10 && +// et.y - 10 <= this.y + 15 && et.y + 10 >= this.y + 15)) { +// flag = true; +// } +// //下右 +// if (et.x - 15 <= this.x + 10 && et.x + 15 >= this.x + 10 && +// et.y - 10 <= this.y + 15 && et.y + 10 >= this.y + 15) {//判断最前方两个点是否在对方坦克区域内 +// flag = true; +// } +// } +// } +// } +// break; +// case 2: +// for (int i = 0; i < ets.size(); i++) { +// EnemyTank et = ets.get(i); +// if (et != this) { +// if (et.direct == 0 || et.direct == 1) { +// //对方是上下 +// //自己的左上 +// if ((et.x - 10 <= this.x - 15 && et.x + 10 >= this.x - 15 && +// et.y - 15 <= this.y - 10 && et.y + 15 >= this.y - 10)) { +// flag = true; +// } +// //右下 +// if (et.x - 10 <= this.x - 15 && et.x + 10 >= this.x - 15 && +// et.y - 15 <= this.y + 10 && et.y + 15 >= this.y + 10) {//判断最前方两个点是否在对方坦克区域内 +// flag = true; +// } +// } +// if (et.direct == 2 || et.direct == 3) { +// //对方是左右 +// //自己的左上 +// if ((et.x - 15 <= this.x - 15 && et.x + 15 >= this.x - 15 && +// et.y - 10 <= this.y - 10 && et.y + 10 >= this.y - 10)) { +// flag = true; +// } +// //右下 +// if (et.x - 15 <= this.x - 15 && et.x + 15 >= this.x - 15 && +// et.y - 10 <= this.y + 10 && et.y + 10 >= this.y + 10) {//判断最前方两个点是否在对方坦克区域内 +// flag = true; +// } +// } +// } +// } +// break; +// case 3: +// for (int i = 0; i < ets.size(); i++) { +// EnemyTank et = ets.get(i); +// if (et != this) { +// if (et.direct == 0 || et.direct == 1) { +// //对方是上下 +// //自己的左上 +// if ((et.x - 10 <= this.x + 15 && et.x + 10 >= this.x + 15 && +// et.y - 15 <= this.y - 10 && et.y + 15 >= this.y - 10)) { +// flag = true; +// } +// //右下 +// if (et.x - 10 <= this.x + 15 && et.x + 10 >= this.x + 15 && +// et.y - 15 <= this.y + 10 && et.y + 15 >= this.y + 10) {//判断最前方两个点是否在对方坦克区域内 +// flag = true; +// } +// } +// if (et.direct == 2 || et.direct == 3) { +// //对方是左右 +// //自己的左上 +// if ((et.x - 15 <= this.x + 15 && et.x + 15 >= this.x + 15 && +// et.y - 10 <= this.y - 10 && et.y + 10 >= this.y - 10)) { +// flag = true; +// } +// //右下 +// if (et.x - 15 <= this.x + 15 && et.x + 15 >= this.x + 15 && +// et.y - 10 <= this.y + 10 && et.y + 10 >= this.y + 10) {//判断最前方两个点是否在对方坦克区域内 +// flag = true; +// } +// } +// } +// } +// break; +// } +// +// return flag; +// } //只是移动一步的函数 - public boolean toUp() { - if (y > 30) { - ableMove = true; - for (EnemyTank et : ets) { - if (!TankTool.ablePass(this, et)) { - ableMove = (false); - break; - } - } - for (Brick brick : bricks) { - if (TankTool.ablePass(this, brick)) - ableMove = false; - } - for (Iron iron : irons) { - if (TankTool.ablePass(this, iron)) - ableMove = false; - } - if (ableMove && TankTool.ablePass(this, PlayStageMgr.instance.hero)) { - y -= speed; - try { - Thread.sleep(100); - } catch (Exception e) { - log.error("", e); - } - } else { - return false; - } - - } else return false; - return true; - } - - public boolean toDown() { - if (y < 530) { - ableMove = true; - for (EnemyTank et : ets) { - if (!TankTool.ablePass(this, et)) { - ableMove = (false); - break; - } - } - for (Brick brick : bricks) { - if (TankTool.ablePass(this, brick)) - ableMove = false; - } - for (Iron iron : irons) { - if (TankTool.ablePass(this, iron)) - ableMove = false; - } - if (ableMove && TankTool.ablePass(this, PlayStageMgr.instance.hero)) { - y += speed; - try { - Thread.sleep(100); - } catch (Exception e) { - log.error("", e); - } - } else { - return false; -// this.direct = ((int)(Math.random()*100))%4; - } - - } else return false; - return true; - } - - public boolean toLeft() { - if (x > 30) { - ableMove = true; - for (EnemyTank et : ets) { - if (!TankTool.ablePass(this, et)) { - ableMove = false; - break; - } - } - for (Brick brick : bricks) { - if (TankTool.ablePass(this, brick)) - ableMove = false; - } - for (Iron iron : irons) { - if (TankTool.ablePass(this, iron)) - ableMove = false; - } - if (ableMove && TankTool.ablePass(this, PlayStageMgr.instance.hero)) { - x -= speed; - try { - Thread.sleep(100); - } catch (Exception e) { - log.error("", e); - } - } else { - return true; - } - - } else return false; - return true; - } +// public boolean toUp() { +// if (y > 30) { +// ableMove = true; +// for (EnemyTank et : ets) { +// if (!TankTool.ablePass(this, et)) { +// ableMove = (false); +// break; +// } +// } +// for (Brick brick : bricks) { +// if (TankTool.ablePass(this, brick)) +// ableMove = false; +// } +// for (Iron iron : irons) { +// if (TankTool.ablePass(this, iron)) +// ableMove = false; +// } +// if (ableMove && TankTool.ablePass(this, PlayStageMgr.instance.hero)) { +// y -= speed; +// try { +// Thread.sleep(100); +// } catch (Exception e) { +// log.error("", e); +// } +// } else { +// return false; +// } +// +// } else return false; +// return true; +// } - public boolean toRight() { - if (x < 710) { - ableMove = true; - for (EnemyTank et : ets) { - if (!TankTool.ablePass(this, et)) { - ableMove = (false); - break; - } - } - for (Brick brick : bricks) { - if (TankTool.ablePass(this, brick)) - ableMove = false; - } - for (Iron iron : irons) { - if (TankTool.ablePass(this, iron)) - ableMove = false; - } - if (ableMove && TankTool.ablePass(this, PlayStageMgr.instance.hero)) { - x += speed; - try { - Thread.sleep(100); - } catch (Exception e) { - log.error("", e); - } - } else { - return false; -// this.direct = ((int)(Math.random()*100))%4; - } +// public boolean toDown() { +// if (y < 530) { +// ableMove = true; +// for (EnemyTank et : ets) { +// if (!TankTool.ablePass(this, et)) { +// ableMove = (false); +// break; +// } +// } +// for (Brick brick : bricks) { +// if (TankTool.ablePass(this, brick)) +// ableMove = false; +// } +// for (Iron iron : irons) { +// if (TankTool.ablePass(this, iron)) +// ableMove = false; +// } +// if (ableMove && TankTool.ablePass(this, PlayStageMgr.instance.hero)) { +// y += speed; +// try { +// Thread.sleep(100); +// } catch (Exception e) { +// log.error("", e); +// } +// } else { +// return false; +//// this.direct = ((int)(Math.random()*100))%4; +// } +// +// } else return false; +// return true; +// } - } else return false; - return true; - } +// public boolean toLeft() { +// if (x > 30) { +// ableMove = true; +// for (EnemyTank et : ets) { +// if (!TankTool.ablePass(this, et)) { +// ableMove = false; +// break; +// } +// } +// for (Brick brick : bricks) { +// if (TankTool.ablePass(this, brick)) +// ableMove = false; +// } +// for (Iron iron : irons) { +// if (TankTool.ablePass(this, iron)) +// ableMove = false; +// } +// if (ableMove && TankTool.ablePass(this, PlayStageMgr.instance.hero)) { +// x -= speed; +// try { +// Thread.sleep(100); +// } catch (Exception e) { +// log.error("", e); +// } +// } else { +// return true; +// } +// +// } else return false; +// return true; +// } - //重写 - int min = 0; +// public boolean toRight() { +// if (x < 710) { +// ableMove = true; +// for (EnemyTank et : ets) { +// if (!TankTool.ablePass(this, et)) { +// ableMove = (false); +// break; +// } +// } +// for (Brick brick : bricks) { +// if (TankTool.ablePass(this, brick)) +// ableMove = false; +// } +// for (Iron iron : irons) { +// if (TankTool.ablePass(this, iron)) +// ableMove = false; +// } +// if (ableMove && TankTool.ablePass(this, PlayStageMgr.instance.hero)) { +// x += speed; +// try { +// Thread.sleep(100); +// } catch (Exception e) { +// log.error("", e); +// } +// } else { +// return false; +//// this.direct = ((int)(Math.random()*100))%4; +// } +// +// } else return false; +// return true; +// } // @Override // public void run() { @@ -483,45 +481,45 @@ public void run() { } // 运动 - public void actionModeRun() { - while (true) { - try { - if (this.speed == 0) { - TankTool.yieldMsTime(1000); - continue; - } - this.direct = (int) (Math.random() * 4); - switch (this.direct) { - case DirectType.UP: - toUp(); - break; - case DirectType.DOWN: - toDown(); - break; - case DirectType.LEFT: - toLeft(); - break; - case DirectType.RIGHT: - toRight(); - break; - default: - break; - } - - //判断坦克是否死亡 - if (!this.isAlive()) { - //让坦克退出while即退出线程 - - if (!abort) { - PlayStageMgr.instance.hero.addPrize(1); - } - break; - } - } catch (Exception e) { - log.error("", e); - } - } - } +// public void actionModeRun() { +// while (true) { +// try { +// if (this.speed == 0) { +// TankTool.yieldMsTime(1000); +// continue; +// } +// this.direct = (int) (Math.random() * 4); +// switch (this.direct) { +// case DirectType.UP: +// toUp(); +// break; +// case DirectType.DOWN: +// toDown(); +// break; +// case DirectType.LEFT: +// toLeft(); +// break; +// case DirectType.RIGHT: +// toRight(); +// break; +// default: +// break; +// } +// +// //判断坦克是否死亡 +// if (!this.isAlive()) { +// //让坦克退出while即退出线程 +// +// if (!abort) { +// PlayStageMgr.instance.hero.addPrize(1); +// } +// break; +// } +// } catch (Exception e) { +// log.error("", e); +// } +// } +// } private final EnemyActionContext actionContext = new EnemyActionContext(); @@ -582,216 +580,215 @@ private void finalMoveAction() { } } - public void run2() { - //子弹发射的(时间)坐标间隔 - - while (true) { - min++; - if (min > 10000) min -= 10000; -// System.out.println("x = "+this.x+" y = "+this.y); -// System.out.println(speed); 终于找出错误,speed没有初始化 - - //让坦克随机产生一个随机方向 - if (this.speed != 0) this.direct = (int) (Math.random() * 4); - - switch (this.direct) {//上下左右 - case 0://说明坦克正在向上移动 - for (int i = 0; i < 30; i++) { - min++; -// if(!bri)this.direct = (int)(Math.random()*4); - if (y > 30) { - if (TankTool.ablePass(this, PlayStageMgr.instance.hero) && overlap) y -= speed; -// if(bri)y-=speed; -// else {y+=speed;this.direct = 1;} -// else continue; - else break;//这样才不会有敌人坦克凑在你附近不动 -// else this.direct = (int)(Math.random()*4); - if (min % 27 == 0) - this.finalShotAction(); - TankTool.yieldMsTime(50); - } - - } - - break; - case 1: - for (int i = 0; i < 30; i++) { - min++; -// if(!bri)this.direct = (int)(Math.random()*4); - - if (y < 530) { - if (TankTool.ablePass(this, PlayStageMgr.instance.hero) && overlap && bri) y += speed; -// if(bri) y+=speed; -// else {y-=speed;this.direct = 0;} -// else continue; - else break; - if (min % 27 == 0) { - this.finalShotAction(); - } - TankTool.yieldMsTime(50); - } - - } - - break; - case 2: - for (int i = 0; i < 30; i++) { - min++; -// if(!bri)this.direct = (int)(Math.random()*4); - - if (x > 30) { - if (TankTool.ablePass(this, PlayStageMgr.instance.hero) && overlap && bri) x -= speed; -// if(bri)x-=speed; -// else{x+=speed;this.direct = 3;} -// else continue; - else break; - if (min % 27 == 0) - this.finalShotAction(); - TankTool.yieldMsTime(50); - } - - } - - break; - case 3: - for (int i = 0; i < 30; i++) { - min++; -// if(!bri)this.direct = (int)(Math.random()*4); - - if (x < 710) { - if (TankTool.ablePass(this, PlayStageMgr.instance.hero) && overlap && bri) { - x += speed; - } -// if(bri)x+=speed; -// else{x-=speed;this.direct = 2;} - else break; -// else continue; - if (min % 27 == 0) - this.finalShotAction(); - - TankTool.yieldMsTime(50); - } - - - } - break; - default: - break; - } - - //判断坦克是否死亡 - if (!this.isAlive()) { - //让坦克退出while即退出线程 - if (!abort) { - PlayStageMgr.instance.hero.addPrize(1); - } - - break; - } - } - } - - - public void newEventRun() { - if (PlayStageMgr.pause) { - return; - } - //判断坦克是否死亡 - if (!this.isAlive()) { - this.stop(); - return; - } - - //子弹发射的(时间)坐标间隔 - min++; - if (min > 10000) min -= 10000; -// System.out.println("x = "+this.x+" y = "+this.y); -// System.out.println(speed); 终于找出错误,speed没有初始化 - - //让坦克随机产生一个随机方向 - if (this.speed != 0) this.direct = (int) (Math.random() * 4); - - switch (this.direct) {//上下左右 - case 0://说明坦克正在向上移动 - for (int i = 0; i < 5; i++) { - min++; -// if(!bri)this.direct = (int)(Math.random()*4); - if (y > 30) { - if (PlayStageMgr.ablePassByHero(this) && overlap) y -= speed; -// if(bri)y-=speed; -// else {y+=speed;this.direct = 1;} -// else continue; - else break;//这样才不会有敌人坦克凑在你附近不动 -// else this.direct = (int)(Math.random()*4); - if (min % 27 == 0) - this.finalShotAction(); -// TankTool.yieldMsTime(50); - } - - } - - break; - case 1: - for (int i = 0; i < 5; i++) { - min++; -// if(!bri)this.direct = (int)(Math.random()*4); - - if (y < 530) { - if (PlayStageMgr.ablePassByHero(this) && overlap && bri) y += speed; -// if(bri) y+=speed; -// else {y-=speed;this.direct = 0;} -// else continue; - else break; - if (min % 27 == 0) { - this.finalShotAction(); - } -// TankTool.yieldMsTime(50); - } - - } - - break; - case 2: - for (int i = 0; i < 5; i++) { - min++; -// if(!bri)this.direct = (int)(Math.random()*4); - - if (x > 30) { - if (PlayStageMgr.ablePassByHero(this) && overlap && bri) x -= speed; -// if(bri)x-=speed; -// else{x+=speed;this.direct = 3;} -// else continue; - else break; - if (min % 27 == 0) - this.finalShotAction(); -// TankTool.yieldMsTime(50); - } - - } +// public void run2() { +// //子弹发射的(时间)坐标间隔 +// +// while (true) { +// min++; +// if (min > 10000) min -= 10000; +//// System.out.println("x = "+this.x+" y = "+this.y); +//// System.out.println(speed); 终于找出错误,speed没有初始化 +// +// //让坦克随机产生一个随机方向 +// if (this.speed != 0) this.direct = (int) (Math.random() * 4); +// +// switch (this.direct) {//上下左右 +// case 0://说明坦克正在向上移动 +// for (int i = 0; i < 30; i++) { +// min++; +//// if(!bri)this.direct = (int)(Math.random()*4); +// if (y > 30) { +// if (TankTool.ablePass(this, PlayStageMgr.instance.hero) && overlap) y -= speed; +//// if(bri)y-=speed; +//// else {y+=speed;this.direct = 1;} +//// else continue; +// else break;//这样才不会有敌人坦克凑在你附近不动 +//// else this.direct = (int)(Math.random()*4); +// if (min % 27 == 0) +// this.finalShotAction(); +// TankTool.yieldMsTime(50); +// } +// +// } +// +// break; +// case 1: +// for (int i = 0; i < 30; i++) { +// min++; +//// if(!bri)this.direct = (int)(Math.random()*4); +// +// if (y < 530) { +// if (TankTool.ablePass(this, PlayStageMgr.instance.hero) && overlap && bri) y += speed; +//// if(bri) y+=speed; +//// else {y-=speed;this.direct = 0;} +//// else continue; +// else break; +// if (min % 27 == 0) { +// this.finalShotAction(); +// } +// TankTool.yieldMsTime(50); +// } +// +// } +// +// break; +// case 2: +// for (int i = 0; i < 30; i++) { +// min++; +//// if(!bri)this.direct = (int)(Math.random()*4); +// +// if (x > 30) { +// if (TankTool.ablePass(this, PlayStageMgr.instance.hero) && overlap && bri) x -= speed; +//// if(bri)x-=speed; +//// else{x+=speed;this.direct = 3;} +//// else continue; +// else break; +// if (min % 27 == 0) +// this.finalShotAction(); +// TankTool.yieldMsTime(50); +// } +// +// } +// +// break; +// case 3: +// for (int i = 0; i < 30; i++) { +// min++; +//// if(!bri)this.direct = (int)(Math.random()*4); +// +// if (x < 710) { +// if (TankTool.ablePass(this, PlayStageMgr.instance.hero) && overlap && bri) { +// x += speed; +// } +//// if(bri)x+=speed; +//// else{x-=speed;this.direct = 2;} +// else break; +//// else continue; +// if (min % 27 == 0) +// this.finalShotAction(); +// +// TankTool.yieldMsTime(50); +// } +// +// +// } +// break; +// default: +// break; +// } +// +// //判断坦克是否死亡 +// if (!this.isAlive()) { +// //让坦克退出while即退出线程 +// if (!abort) { +// PlayStageMgr.instance.hero.addPrize(1); +// } +// +// break; +// } +// } +// } - break; - case 3: - for (int i = 0; i < 5; i++) { - min++; -// if(!bri)this.direct = (int)(Math.random()*4); - - if (x < 710) { - if (PlayStageMgr.ablePassByHero(this) && overlap && bri) { - x += speed; - } -// if(bri)x+=speed; -// else{x-=speed;this.direct = 2;} - else break; -// else continue; - if (min % 27 == 0) - this.finalShotAction(); - -// TankTool.yieldMsTime(50); - } - } - break; - default: - break; - } - } +// public void newEventRun() { +// if (PlayStageMgr.pause) { +// return; +// } +// //判断坦克是否死亡 +// if (!this.isAlive()) { +// this.stop(); +// return; +// } +// +// //子弹发射的(时间)坐标间隔 +// min++; +// if (min > 10000) min -= 10000; +//// System.out.println("x = "+this.x+" y = "+this.y); +//// System.out.println(speed); 终于找出错误,speed没有初始化 +// +// //让坦克随机产生一个随机方向 +// if (this.speed != 0) this.direct = (int) (Math.random() * 4); +// +// switch (this.direct) {//上下左右 +// case 0://说明坦克正在向上移动 +// for (int i = 0; i < 5; i++) { +// min++; +//// if(!bri)this.direct = (int)(Math.random()*4); +// if (y > 30) { +// if (PlayStageMgr.ablePassByHero(this) && overlap) y -= speed; +//// if(bri)y-=speed; +//// else {y+=speed;this.direct = 1;} +//// else continue; +// else break;//这样才不会有敌人坦克凑在你附近不动 +//// else this.direct = (int)(Math.random()*4); +// if (min % 27 == 0) +// this.finalShotAction(); +//// TankTool.yieldMsTime(50); +// } +// +// } +// +// break; +// case 1: +// for (int i = 0; i < 5; i++) { +// min++; +//// if(!bri)this.direct = (int)(Math.random()*4); +// +// if (y < 530) { +// if (PlayStageMgr.ablePassByHero(this) && overlap && bri) y += speed; +//// if(bri) y+=speed; +//// else {y-=speed;this.direct = 0;} +//// else continue; +// else break; +// if (min % 27 == 0) { +// this.finalShotAction(); +// } +//// TankTool.yieldMsTime(50); +// } +// +// } +// +// break; +// case 2: +// for (int i = 0; i < 5; i++) { +// min++; +//// if(!bri)this.direct = (int)(Math.random()*4); +// +// if (x > 30) { +// if (PlayStageMgr.ablePassByHero(this) && overlap && bri) x -= speed; +//// if(bri)x-=speed; +//// else{x+=speed;this.direct = 3;} +//// else continue; +// else break; +// if (min % 27 == 0) +// this.finalShotAction(); +//// TankTool.yieldMsTime(50); +// } +// +// } +// +// break; +// case 3: +// for (int i = 0; i < 5; i++) { +// min++; +//// if(!bri)this.direct = (int)(Math.random()*4); +// +// if (x < 710) { +// if (PlayStageMgr.ablePassByHero(this) && overlap && bri) { +// x += speed; +// } +//// if(bri)x+=speed; +//// else{x-=speed;this.direct = 2;} +// else break; +//// else continue; +// if (min % 27 == 0) +// this.finalShotAction(); +// +//// TankTool.yieldMsTime(50); +// } +// } +// break; +// default: +// break; +// } +// } } diff --git a/gui/src/main/java/com/github/kuangcp/tank/mgr/BombMgr.java b/gui/src/main/java/com/github/kuangcp/tank/mgr/BombMgr.java index c0a70d1c..64f6b2b2 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/mgr/BombMgr.java +++ b/gui/src/main/java/com/github/kuangcp/tank/mgr/BombMgr.java @@ -68,7 +68,9 @@ public void drawBomb(Graphics g, JPanel panel) { * 工具类-检测爆炸的函数 */ public void checkBong(Tank tank, List bullets) { - bullets.forEach(v -> this.checkBong(tank, v)); + for (Bullet bullet : bullets) { + this.checkBong(tank, bullet); + } } private void checkBong(Tank tank, Bullet bullet) { diff --git a/gui/src/main/java/com/github/kuangcp/tank/panel/StageActionPanel.java b/gui/src/main/java/com/github/kuangcp/tank/panel/StageActionPanel.java index 5f984129..0dbf3540 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/panel/StageActionPanel.java +++ b/gui/src/main/java/com/github/kuangcp/tank/panel/StageActionPanel.java @@ -4,7 +4,6 @@ import com.github.kuangcp.tank.constant.ButtonCommand; import com.github.kuangcp.tank.domain.Bullet; -import com.github.kuangcp.tank.domain.EnemyTank; import com.github.kuangcp.tank.util.Audio; import com.github.kuangcp.tank.v3.MainFrame; import com.github.kuangcp.tank.v3.PlayStageMgr; @@ -84,9 +83,6 @@ public void actionPerformed(ActionEvent ae) { } public void startNewStage() { - log.warn("new stage"); - final int totalMaxShots = PlayStageMgr.getEnemySize() * EnemyTank.maxLiveShot; - // 重新设置线程池大小 // final ThreadPoolExecutor pool = (ThreadPoolExecutor) ExecutePool.shotPool; // final int poolSize = (int) Math.max(PlayStageMgr.getEnemySize() * EnemyTank.maxLiveShot * 0.6, 10); @@ -99,7 +95,6 @@ public void startNewStage() { // ExecutePool.shotScheduler = new FiberForkJoinScheduler("enemyShot", poolSize, null, false); if (Objects.nonNull(actionThread) && !actionThread.isInterrupted()) { - log.info("clean last stage"); actionThread.interrupt(); PlayStageMgr.instance.abortStage(); } @@ -109,7 +104,7 @@ public void startNewStage() { Bullet.setSpeed(8); frame.remove(frame.getContentPane()); - log.info("start new stage frame thread, shot:{}", totalMaxShots); +// log.info("start new stage frame thread"); actionThread = new Thread(() -> { frame.run(); if (beginAudio != null) { diff --git a/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java b/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java index 0d0f4a99..8e1bc099 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java +++ b/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java @@ -1,7 +1,5 @@ - package com.github.kuangcp.tank.panel; - import com.github.kuangcp.tank.domain.Brick; import com.github.kuangcp.tank.domain.Bullet; import com.github.kuangcp.tank.domain.EnemyTank; @@ -10,6 +8,7 @@ import com.github.kuangcp.tank.mgr.BombMgr; import com.github.kuangcp.tank.resource.AvatarImgMgr; import com.github.kuangcp.tank.resource.ColorMgr; +import com.github.kuangcp.tank.util.ExecutePool; import com.github.kuangcp.tank.util.HoldingKeyEventMgr; import com.github.kuangcp.tank.util.KeyListener; import com.github.kuangcp.tank.util.TankTool; @@ -27,16 +26,11 @@ import javax.swing.*; import java.awt.*; import java.awt.event.KeyEvent; -import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.Objects; +import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.TimeUnit; - -/** - * (20, 20, 720, 520) 该画板坦克运动区域 - */ @SuppressWarnings("serial") @Slf4j public class TankGroundPanel extends JPanel implements java.awt.event.KeyListener, Runnable { @@ -46,12 +40,9 @@ public class TankGroundPanel extends JPanel implements java.awt.event.KeyListene public static boolean newStage = true; private volatile boolean invokeNewStage = false; - //定义一个 泛型的集合ets 表示敌人坦克集合 - public List enemyList = Collections.synchronizedList(new ArrayList<>()); - - //定义砖块集合 - public List bricks = Collections.synchronizedList(new ArrayList<>()); - public List irons = Collections.synchronizedList(new ArrayList<>()); + public List enemyList = new CopyOnWriteArrayList<>(); + public List bricks = new CopyOnWriteArrayList<>(); + public List irons = new CopyOnWriteArrayList<>(); //所有按下键的code集合 public static int[][] enemyTankMap = new int[12][2]; @@ -61,29 +52,26 @@ public TankGroundPanel() { } - /** - * 画板的构造函数 用来初始化对象 - * 多个敌方坦克应该用集合类,而且要考虑线程安全所以不能用ArrayList只能用Vector - */ public void startNewRound() { + enemyList = new CopyOnWriteArrayList<>(); + bricks = new CopyOnWriteArrayList<>(); + irons = new CopyOnWriteArrayList<>(); + //创建英雄坦克 - if (newStage) {//正常启动并创建坦克线程 - hero = new Hero(480, 500, 3);//坐标和步长和敌人坦克集合 - hero.setLife(10);//设置生命值 + if (newStage) { + hero = new Hero(480, 500, 3); + hero.setLife(10); } else { hero = new Hero(myself[0], myself[1], 3); hero.setLife(myself[2]); hero.setPrize(myself[3]); } -// log.info("hero={}", hero); PlayStageMgr.init(hero, enemyList, bricks, irons); //多键监听实现 keyListener = new KeyListener(HoldingKeyEventMgr.instance, hero, this); - Thread p = new Thread(keyListener); - p.setName("playerKeyEventListener"); - p.start(); + ExecutePool.exclusiveLoopPool.execute(keyListener); // 创建 敌人的坦克 EnemyTank ett = null; @@ -93,19 +81,15 @@ public void startNewRound() { switch ((int) (Math.random() * 4)) { case 0: ett = new EnemyTank(20 + (int) (Math.random() * 30), 20 + (int) (Math.random() * 30), i % 4); - ett.SetInfo(hero, enemyList, bricks, irons); break; case 1: ett = new EnemyTank(700 - (int) (Math.random() * 30), 20 + (int) (Math.random() * 30), i % 4); - ett.SetInfo(hero, enemyList, bricks, irons); break; case 2: ett = new EnemyTank(20 + (int) (Math.random() * 30), 200 + (int) (Math.random() * 30), i % 4); - ett.SetInfo(hero, enemyList, bricks, irons); break; case 3: ett = new EnemyTank(700 - (int) (Math.random() * 30), 200 + (int) (Math.random() * 30), i % 4); - ett.SetInfo(hero, enemyList, bricks, irons); break; } @@ -113,26 +97,16 @@ public void startNewRound() { continue; } LoopEventExecutor.addLoopEvent(ett); -// Thread t = new Thread(ett); -// t.setName("enemyThread-" + ett.id); -// t.start(); - //坦克加入集合 enemyList.add(ett); } } else { /*进入读取文件步骤*/ for (int i = 0; i < enemyTankMap.length; i++) { - if (enemyTankMap[i][0] == 0) break; - + if (enemyTankMap[i][0] == 0) { + break; + } ett = new EnemyTank(enemyTankMap[i][0], enemyTankMap[i][1], i % 4); - ett.SetInfo(hero, enemyList, bricks, irons); - LoopEventExecutor.addLoopEvent(ett); - -// Thread t = new Thread(ett); -// t.setName("enemyThreadF-" + ett.id); -// t.start(); - //坦克加入集合 enemyList.add(ett); } } @@ -162,7 +136,6 @@ private void drawBg(Graphics g) { private void drawHeroInfo(Graphics g) { g.setColor(Color.GREEN); -// g.setFont(Font.getFont("IBM Plex Mono")); final String lifeInfo = "Life:" + hero.getLife() + " Enemy: " + PlayStageMgr.instance.getLiveEnemy() + " Prize: " + hero.getPrize(); @@ -296,8 +269,6 @@ public void paint(Graphics g) { if (enemyList.size() < 5) { for (int i = 0; i < 4; i++) { EnemyTank d = new EnemyTank(20 + (int) (Math.random() * 400), 20 + (int) (Math.random() * 300), i % 4); - d.SetInfo(hero, enemyList, bricks, irons); - LoopEventExecutor.addLoopEvent(d); // Thread fillThread = new Thread(d); // fillThread.setName("fillEnemy" + d.id); @@ -381,6 +352,7 @@ public void keyPressed(KeyEvent e) { PlayStageMgr.pause = true; } if (e.getKeyCode() == KeyEvent.VK_O) { + PlayStageMgr.pause = true; SettingFrame.activeFocus(); } @@ -415,7 +387,6 @@ public void createB(List bricks, int startX, int startY, int endX, int en for (int j = startY; j < endY; j += 10) { Brick bs = new Brick(i, j); bricks.add(bs); - } } } diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/ExecutePool.java b/gui/src/main/java/com/github/kuangcp/tank/util/ExecutePool.java index 5f1b27d7..7e69e413 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/util/ExecutePool.java +++ b/gui/src/main/java/com/github/kuangcp/tank/util/ExecutePool.java @@ -24,6 +24,8 @@ public class ExecutePool { public static final AtomicLong totalCounter = new AtomicLong(); + public static final ExecutorService exclusiveLoopPool = ExecutePool.buildFixedPool("exclusiveLoopPool", 2); + /** * @param prefix eg: shot * @param coreSize 核心线程数 diff --git a/gui/src/main/java/com/github/kuangcp/tank/v3/PlayStageMgr.java b/gui/src/main/java/com/github/kuangcp/tank/v3/PlayStageMgr.java index 8f84ac1f..8b6a36d9 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v3/PlayStageMgr.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v3/PlayStageMgr.java @@ -65,7 +65,7 @@ public void markStopLogic() { public void markStartLogic() { this.startLogic = true; round++; - log.info("start round:{}", round); + log.warn("start round:{}", round); } /** From ca8cf828124d2e1bcdd7c75e598b0eab5318ebd5 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Sat, 23 Oct 2021 23:54:04 +0800 Subject: [PATCH 252/476] x) relative position --- .../java/com/github/kuangcp/tank/panel/TankGroundPanel.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java b/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java index 8e1bc099..ff4baaac 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java +++ b/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java @@ -144,7 +144,8 @@ private void drawHeroInfo(Graphics g) { private void drawMonitorInfo(Graphics g) { g.setColor(Color.LIGHT_GRAY); - g.drawString(MonitorExecutor.info.toString(), RoundMapMgr.instance.border.getMinX(), 555); + g.drawString(MonitorExecutor.info.toString(), + RoundMapMgr.instance.border.getMinX(), RoundMapMgr.instance.border.getTotalY() - 3); } @Override From f5f04665b736a953f489828995bd56ff211968f2 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Sun, 31 Oct 2021 19:30:17 +0800 Subject: [PATCH 253/476] x) fix click --- .../kuangcp/tank/constant/DirectType.java | 22 +++++ .../github/kuangcp/tank/domain/EnemyTank.java | 24 +++-- .../github/kuangcp/tank/domain/Hinder.java | 8 ++ .../tank/domain/robot/EnemyActionContext.java | 2 +- .../com/github/kuangcp/tank/mgr/BombMgr.java | 3 +- .../kuangcp/tank/panel/TankGroundPanel.java | 8 +- .../kuangcp/tank/resource/ColorMgr.java | 2 +- .../kuangcp/tank/util/HeroKeyListener.java | 89 +++++++++++++++++++ .../kuangcp/tank/util/HoldingKeyEventMgr.java | 14 ++- .../github/kuangcp/tank/util/KeyListener.java | 74 --------------- .../github/kuangcp/tank/util/TankTool.java | 51 +++++++---- .../util/executor/CommonEventExecutor.java | 6 +- .../tank/util/executor/LoopEventExecutor.java | 4 +- .../tank/util/executor/MonitorExecutor.java | 2 +- .../github/kuangcp/tank/v3/PlayStageMgr.java | 24 ++++- 15 files changed, 213 insertions(+), 120 deletions(-) create mode 100644 gui/src/main/java/com/github/kuangcp/tank/util/HeroKeyListener.java delete mode 100644 gui/src/main/java/com/github/kuangcp/tank/util/KeyListener.java diff --git a/gui/src/main/java/com/github/kuangcp/tank/constant/DirectType.java b/gui/src/main/java/com/github/kuangcp/tank/constant/DirectType.java index ab1418a9..c487e44a 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/constant/DirectType.java +++ b/gui/src/main/java/com/github/kuangcp/tank/constant/DirectType.java @@ -1,14 +1,19 @@ package com.github.kuangcp.tank.constant; +import java.util.concurrent.ThreadLocalRandom; + /** * @author https://github.com/kuangcp on 2021-09-06 03:08 */ public interface DirectType { + int UP = 0; int DOWN = 1; int LEFT = 2; int RIGHT = 3; + int MAX = RIGHT; + int[] UP_SELECT = new int[]{UP, LEFT, RIGHT}; int[] DOWN_SELECT = new int[]{DOWN, LEFT, RIGHT}; int[] LEFT_SELECT = new int[]{UP, DOWN, LEFT}; @@ -28,4 +33,21 @@ static int[] turnSelection(int direct) { return UP_SELECT; } } + + static boolean isUp(int direct){ + return direct == UP; + } + static boolean isDown(int direct){ + return direct == DOWN; + } + static boolean isLeft(int direct){ + return direct == LEFT; + } + static boolean isRight(int direct){ + return direct == RIGHT; + } + + static int rollDirect(int curDirect) { + return DirectType.turnSelection(curDirect)[ThreadLocalRandom.current().nextInt(DirectType.MAX)]; + } } diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java b/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java index 148b0b87..4fde768e 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java @@ -33,6 +33,8 @@ public class EnemyTank extends Tank implements Runnable, RobotRate { public int INIT_MAX_LIVE_BULLET = 4; public int maxLiveBullet = INIT_MAX_LIVE_BULLET + 1; // 活跃子弹 最大数 + private final EnemyActionContext actionContext = new EnemyActionContext(); + /** * 延迟回收内存(标记使用一次) * 避免子弹线程执行中断,突然消失 @@ -75,9 +77,6 @@ public EnemyTank(int x, int y, int direct) { this.randomLife(); this.resetPropByLife(); -// this.life = ThreadLocalRandom.current().nextInt(MAX_LIFE) + 1; -// this.speed = Math.max((MAX_LIFE - life + 1) / 2, 1); -// log.info(": life={} speed={}", life, speed); this.id = counter.addAndGet(1); this.setFixedDelayTime(40); @@ -107,7 +106,7 @@ private void randomLife() { } /** - * 依据生命值重置属性 + * 依据生命值重置属性(速度,子弹数) */ private void resetPropByLife() { this.speed = Math.max((MAX_LIFE - life + 1) / 2, 1); @@ -521,8 +520,6 @@ public void run() { // } // } - private final EnemyActionContext actionContext = new EnemyActionContext(); - public void moveOrShot() { if (!this.isAlive()) { this.stop(); @@ -550,10 +547,14 @@ public void moveOrShot() { } private void finalMoveAction() { - if (actionContext.getSameDirectCounter() > actionContext.getCurRoundStep() - || !PlayStageMgr.instance.willInBorder(this)) { - this.direct = DirectType.turnSelection(this.direct)[ThreadLocalRandom.current().nextInt(3)]; - actionContext.reset(); + final boolean sameDirect = actionContext.getSameDirectCounter() > actionContext.getCurRoundStep(); + final boolean ablePassHero = PlayStageMgr.ablePassByHero(this); + final boolean ablePassHinder = PlayStageMgr.ablePassByHinder(this); + + // 重定向 + if (sameDirect || !ablePassHero || !ablePassHinder || !PlayStageMgr.instance.willInBorder(this)) { + this.direct = DirectType.rollDirect(this.direct); + actionContext.resetDirectCount(); return; } @@ -574,9 +575,6 @@ private void finalMoveAction() { x += this.speed; actionContext.addCount(); break; - default: - log.warn("not exist direct"); - break; } } diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/Hinder.java b/gui/src/main/java/com/github/kuangcp/tank/domain/Hinder.java index d8622ccd..ba543195 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/domain/Hinder.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/Hinder.java @@ -7,6 +7,8 @@ public abstract class Hinder { int hx, hy;//障碍物绘图坐标 左上角顶点 的坐标 boolean alive;//存活状态 + int width = 20; + int height = 10; public Hinder(int hx, int hy) { alive = true; @@ -41,5 +43,11 @@ public void setHy(int hy) { this.hy = hy; } + public int getWidth() { + return width; + } + public int getHeight() { + return height; + } } diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/robot/EnemyActionContext.java b/gui/src/main/java/com/github/kuangcp/tank/domain/robot/EnemyActionContext.java index b6e3ef33..65257095 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/domain/robot/EnemyActionContext.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/robot/EnemyActionContext.java @@ -55,7 +55,7 @@ public void addCount() { this.sameDirectCounter += 1; } - public void reset() { + public void resetDirectCount() { this.sameDirectCounter = 0; final ThreadLocalRandom random = ThreadLocalRandom.current(); final int curRoundStep = random.nextInt(EnemyActionContext.MIN_DIRECT_STEP, EnemyActionContext.MAX_DIRECT_STEP); diff --git a/gui/src/main/java/com/github/kuangcp/tank/mgr/BombMgr.java b/gui/src/main/java/com/github/kuangcp/tank/mgr/BombMgr.java index 64f6b2b2..d60dc778 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/mgr/BombMgr.java +++ b/gui/src/main/java/com/github/kuangcp/tank/mgr/BombMgr.java @@ -110,10 +110,10 @@ private void checkBong(Tank tank, Bullet bullet) { } private void handleBombAndTank(Tank tank, int bx, int by) { - // 复活 或 无敌 Hero hero = null; if (tank instanceof Hero) { hero = (Hero) tank; + // 无敌 if (hero.isInvincible()) { return; } @@ -126,6 +126,7 @@ private void handleBombAndTank(Tank tank, int bx, int by) { bombs.add(new Bomb(bx, by)); + // 复活 if (Objects.nonNull(hero)) { hero.resurrect(); } diff --git a/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java b/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java index ff4baaac..a02d5a30 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java +++ b/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java @@ -36,7 +36,7 @@ public class TankGroundPanel extends JPanel implements java.awt.event.KeyListener, Runnable { public volatile Hero hero; - public KeyListener keyListener; + public HeroKeyListener heroKeyListener; public static boolean newStage = true; private volatile boolean invokeNewStage = false; @@ -70,8 +70,8 @@ public void startNewRound() { PlayStageMgr.init(hero, enemyList, bricks, irons); //多键监听实现 - keyListener = new KeyListener(HoldingKeyEventMgr.instance, hero, this); - ExecutePool.exclusiveLoopPool.execute(keyListener); + heroKeyListener = new HeroKeyListener(HoldingKeyEventMgr.instance, hero, this); + ExecutePool.exclusiveLoopPool.execute(heroKeyListener); // 创建 敌人的坦克 EnemyTank ett = null; @@ -336,7 +336,7 @@ public void run() { @Override public void keyPressed(KeyEvent e) { // 启动关闭流程 - if (e.getKeyCode() == KeyEvent.VK_Q) { + if (e.getKeyCode() == KeyEvent.VK_Q && HoldingKeyEventMgr.instance.isCtrl()) { System.exit(0); } else if (e.getKeyCode() == KeyEvent.VK_T && !invokeNewStage) { invokeNewStage = true; diff --git a/gui/src/main/java/com/github/kuangcp/tank/resource/ColorMgr.java b/gui/src/main/java/com/github/kuangcp/tank/resource/ColorMgr.java index b70c6445..2e509910 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/resource/ColorMgr.java +++ b/gui/src/main/java/com/github/kuangcp/tank/resource/ColorMgr.java @@ -8,7 +8,7 @@ public class ColorMgr { public static final ColorMgr instance = new ColorMgr(); - public final Color bgColor = new Color(8, 8, 8, 237); + public final Color bgColor = new Color(25, 25, 25, 255); public final Color ironColor = new Color(200, 200, 200); public final Color brickColor = new Color(188, 112, 50); } diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/HeroKeyListener.java b/gui/src/main/java/com/github/kuangcp/tank/util/HeroKeyListener.java new file mode 100644 index 00000000..c1e07f02 --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/util/HeroKeyListener.java @@ -0,0 +1,89 @@ +package com.github.kuangcp.tank.util; + + +import com.github.kuangcp.tank.constant.DirectType; +import com.github.kuangcp.tank.domain.Hero; +import com.github.kuangcp.tank.panel.TankGroundPanel; +import com.github.kuangcp.tank.thread.ExitFlagRunnable; +import com.github.kuangcp.tank.v3.PlayStageMgr; +import lombok.extern.slf4j.Slf4j; + +/** + * 思路如下: + * 在按下键的pressed函数中 激活键 + * 在离开键Release函数中把离开的键从 取消激活 + * 另开一个线程来一直遍历 字段,达到同时监控两个键的的动作的效果 + *

+ * 实现结果: 一边跑,一边放子弹完全不是事儿,方向的切换也十分流畅 + */ +@Slf4j +public class HeroKeyListener implements ExitFlagRunnable { + + Hero hero; + TankGroundPanel tankGroundPanel; + HoldingKeyEventMgr eventGroup; + private volatile boolean exit = false; + + public HeroKeyListener(HoldingKeyEventMgr eventGroup, Hero hero, TankGroundPanel tankGroundPanel) { + this.eventGroup = eventGroup; + this.hero = hero; + this.tankGroundPanel = tankGroundPanel; + } + + public void exit() { + this.exit = true; + } + + @Override + public void run() { + while (hero.isAlive() && !exit) { + if (eventGroup.hasPressMoveEvent()) { +// log.info("eventGroup={}", eventGroup); + } + + final int lastDirect = hero.getDirect(); + + if (eventGroup.isLeft()) { + final boolean ablePass = PlayStageMgr.ablePassByHinder(hero); + if ((ablePass || !DirectType.isLeft(lastDirect))) { + hero.setDirect(DirectType.LEFT); + if (PlayStageMgr.instance.willInBorder(hero) && PlayStageMgr.instance.ableToMove(hero)) { + hero.moveLeft(); + } + } + } else if (eventGroup.isRight()) { + final boolean ablePass = PlayStageMgr.ablePassByHinder(hero); + if (ablePass || !DirectType.isRight(lastDirect)) { + + hero.setDirect(DirectType.RIGHT); + if (PlayStageMgr.instance.willInBorder(hero) && PlayStageMgr.instance.ableToMove(hero)) { + hero.moveRight(); + } + } + } else if (eventGroup.isDown()) { + final boolean ablePass = PlayStageMgr.ablePassByHinder(hero); + if (ablePass || !DirectType.isDown(lastDirect)) { + hero.setDirect(DirectType.DOWN); + if (PlayStageMgr.instance.willInBorder(hero) && PlayStageMgr.instance.ableToMove(hero)) { + hero.moveDown(); + } + } + } else if (eventGroup.isUp()) { + final boolean ablePass = PlayStageMgr.ablePassByHinder(hero); + if (ablePass || !DirectType.isUp(lastDirect)) { + hero.setDirect(DirectType.UP); + if (PlayStageMgr.instance.willInBorder(hero) && PlayStageMgr.instance.ableToMove(hero)) { + hero.moveUp(); + } + } + } + + if (eventGroup.isShot()) { + hero.shotEnemy(); + } + + // 动作的延迟 1000 / 77 fps + TankTool.yieldMsTime(33); + } + } +} diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/HoldingKeyEventMgr.java b/gui/src/main/java/com/github/kuangcp/tank/util/HoldingKeyEventMgr.java index ad9324e4..3c04f288 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/util/HoldingKeyEventMgr.java +++ b/gui/src/main/java/com/github/kuangcp/tank/util/HoldingKeyEventMgr.java @@ -15,6 +15,7 @@ public class HoldingKeyEventMgr { public static HoldingKeyEventMgr instance = new HoldingKeyEventMgr(); + // 允许多键同时被触发 private volatile boolean up; private volatile boolean down; private volatile boolean left; @@ -32,16 +33,19 @@ public HoldingKeyEventMgr() { releaseMap.put(KeyEvent.VK_S, () -> this.down = false); releaseMap.put(KeyEvent.VK_W, () -> this.up = false); releaseMap.put(KeyEvent.VK_J, () -> this.shot = false); + releaseMap.put(KeyEvent.VK_CONTROL, () -> this.ctrl = false); pressMap.put(KeyEvent.VK_A, () -> this.left = true); pressMap.put(KeyEvent.VK_D, () -> this.right = true); pressMap.put(KeyEvent.VK_S, () -> this.down = true); pressMap.put(KeyEvent.VK_W, () -> this.up = true); + pressMap.put(KeyEvent.VK_CONTROL, () -> this.ctrl = true); } - public void handleDirectPress(KeyEvent re){ + public void handleDirectPress(KeyEvent re) { Optional.ofNullable(pressMap.get(re.getKeyCode())).ifPresent(Runnable::run); } + public void handleRelease(KeyEvent re) { Optional.ofNullable(releaseMap.get(re.getKeyCode())).ifPresent(Runnable::run); } @@ -86,6 +90,14 @@ public void setShot(boolean shot) { this.shot = shot; } + public boolean isCtrl() { + return ctrl; + } + + public void setCtrl(boolean ctrl) { + this.ctrl = ctrl; + } + public boolean hasPressMoveEvent() { return up || down || left || right; } diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/KeyListener.java b/gui/src/main/java/com/github/kuangcp/tank/util/KeyListener.java deleted file mode 100644 index 0e3c6bdd..00000000 --- a/gui/src/main/java/com/github/kuangcp/tank/util/KeyListener.java +++ /dev/null @@ -1,74 +0,0 @@ -package com.github.kuangcp.tank.util; - - -import com.github.kuangcp.tank.constant.DirectType; -import com.github.kuangcp.tank.domain.Hero; -import com.github.kuangcp.tank.panel.TankGroundPanel; -import com.github.kuangcp.tank.thread.ExitFlagRunnable; -import com.github.kuangcp.tank.v3.PlayStageMgr; -import lombok.extern.slf4j.Slf4j; - -/** - * 思路如下: - * 在按下键的pressed函数中 激活键 - * 在离开键Release函数中把离开的键从 取消激活 - * 另开一个线程来一直遍历 字段,达到同时监控两个键的的动作的效果 - *

- * 实现结果: 一边跑,一边放子弹完全不是事儿,方向的切换也十分流畅 - */ -@Slf4j -public class KeyListener implements ExitFlagRunnable { - - Hero hero; - TankGroundPanel tankGroundPanel; - HoldingKeyEventMgr eventGroup; - private volatile boolean exit = false; - - public KeyListener(HoldingKeyEventMgr eventGroup, Hero hero, TankGroundPanel tankGroundPanel) { - this.eventGroup = eventGroup; - this.hero = hero; - this.tankGroundPanel = tankGroundPanel; - } - - public void exit() { - this.exit = true; - } - - @Override - public void run() { - while (hero.isAlive() && !exit) { - if (eventGroup.hasPressMoveEvent()) { -// log.info("eventGroup={}", eventGroup); - } - - if (eventGroup.isLeft()) { - hero.setDirect(DirectType.LEFT); - if (PlayStageMgr.instance.willInBorder(hero) && PlayStageMgr.instance.ableToMove(hero)) { - hero.moveLeft(); - } - } else if (eventGroup.isRight()) { - hero.setDirect(DirectType.RIGHT); - if (PlayStageMgr.instance.willInBorder(hero) && PlayStageMgr.instance.ableToMove(hero)) { - hero.moveRight(); - } - } else if (eventGroup.isDown()) { - hero.setDirect(DirectType.DOWN); - if (PlayStageMgr.instance.willInBorder(hero) && PlayStageMgr.instance.ableToMove(hero)) { - hero.moveDown(); - } - } else if (eventGroup.isUp()) { - hero.setDirect(DirectType.UP); - if (PlayStageMgr.instance.willInBorder(hero) && PlayStageMgr.instance.ableToMove(hero)) { - hero.moveUp(); - } - } - - if (eventGroup.isShot()) { - hero.shotEnemy(); - } - - // 动作的延迟 1000 / 77 fps - TankTool.yieldMsTime(33); - } - } -} diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/TankTool.java b/gui/src/main/java/com/github/kuangcp/tank/util/TankTool.java index 8467c0d5..5491cfb0 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/util/TankTool.java +++ b/gui/src/main/java/com/github/kuangcp/tank/util/TankTool.java @@ -15,6 +15,7 @@ public class TankTool { /** * 碰撞检测函数 坦克之间 + * TODO 重构 */ public static boolean ablePass(Tank me, Tank you) { if (Objects.isNull(me) || Objects.isNull(you)) { @@ -165,48 +166,62 @@ public static boolean ablePass(Tank me, Tank you) { } /** + * FIXME 使用坐标区间是否重合来判断 还需要考虑速度, 目前可以两个方向频繁切换发生重叠 + *

* 碰撞检测函数 坦克 和 障碍物 */ public static boolean ablePass(Tank t, Hinder h) { - int hx = 20, hy = 10; + int hx = h.getWidth(), hy = h.getHeight(); final int halfHeight = t.getHalfHeight(); final int halfWidth = t.getHalfWidth(); + final int leftX = t.getX() - halfWidth; + final int rightX = t.getX() + halfWidth; switch (t.getDirect()) { case DirectType.UP: - if (t.getX() - halfWidth >= h.getHx() && t.getX() - halfWidth <= h.getHx() + hx - && t.getY() - halfHeight >= h.getHy() && t.getY() - halfHeight <= h.getHy() + hy + final int upY = t.getY() - halfHeight; + if (leftX >= h.getHx() && leftX <= h.getHx() + hx + && upY >= h.getHy() && upY <= h.getHy() + hy || t.getX() >= h.getHx() && t.getX() <= h.getHx() + hx - && t.getY() - halfHeight >= h.getHy() && t.getY() - halfHeight <= h.getHy() + hy - || t.getX() + halfWidth >= h.getHx() && t.getX() + halfWidth <= h.getHx() + hx - && t.getY() - halfHeight >= h.getHy() && t.getY() - halfHeight <= h.getHy() + hy) - return true; + && upY >= h.getHy() && upY <= h.getHy() + hy + || rightX >= h.getHx() && rightX <= h.getHx() + hx + && upY >= h.getHy() && upY <= h.getHy() + hy) { + return false; + } + break; case DirectType.DOWN: - if (t.getX() - halfWidth >= h.getHx() && t.getX() - halfWidth <= h.getHx() + hx - && t.getY() + halfHeight >= h.getHy() && t.getY() + halfHeight <= h.getHy() + hy + final int downY = t.getY() + halfHeight; + if (leftX >= h.getHx() && leftX <= h.getHx() + hx + && downY >= h.getHy() && downY <= h.getHy() + hy || t.getX() >= h.getHx() && t.getX() <= h.getHx() + hx - && t.getY() + halfHeight >= h.getHy() && t.getY() + halfHeight <= h.getHy() + hy - || t.getX() + halfWidth >= h.getHx() && t.getX() + halfWidth <= h.getHx() + hx - && t.getY() + halfHeight >= h.getHy() && t.getY() + halfHeight <= h.getHy() + hy) - return true; + && downY >= h.getHy() && downY <= h.getHy() + hy + || rightX >= h.getHx() && rightX <= h.getHx() + hx + && downY >= h.getHy() && downY <= h.getHy() + hy) { + return false; + } + break; case DirectType.LEFT: if (t.getX() - halfHeight >= h.getHx() && t.getX() - halfHeight <= h.getHx() + hx && t.getY() - halfWidth >= h.getHy() && t.getY() - halfWidth <= h.getHy() + hy || t.getX() - halfHeight >= h.getHx() && t.getX() - halfHeight <= h.getHx() + hx && t.getY() >= h.getHy() && t.getY() <= h.getHy() + hy || t.getX() - halfHeight >= h.getHx() && t.getX() - halfHeight <= h.getHx() + hx - && t.getY() + halfWidth >= h.getHy() && t.getY() + halfWidth <= h.getHy() + hy) - return true; + && t.getY() + halfWidth >= h.getHy() && t.getY() + halfWidth <= h.getHy() + hy) { + return false; + } + break; case DirectType.RIGHT: if (t.getX() + halfHeight >= h.getHx() - 2 && t.getX() + halfHeight <= h.getHx() + hx && t.getY() + halfWidth >= h.getHy() && t.getY() + halfWidth <= h.getHy() + hy || t.getX() + halfHeight >= h.getHx() - 2 && t.getX() + halfHeight <= h.getHx() + hx && t.getY() >= h.getHy() && t.getY() <= h.getHy() + hy || t.getX() + halfHeight >= h.getHx() - 2 && t.getX() + halfHeight <= h.getHx() + hx - && t.getY() - halfWidth >= h.getHy() && t.getY() - halfWidth <= h.getHy() + hy) - return true; + && t.getY() - halfWidth >= h.getHy() && t.getY() - halfWidth <= h.getHy() + hy) { + return false; + } + break; } - return false; + return true; } /** diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/executor/CommonEventExecutor.java b/gui/src/main/java/com/github/kuangcp/tank/util/executor/CommonEventExecutor.java index 005b1abe..2fd3b971 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/util/executor/CommonEventExecutor.java +++ b/gui/src/main/java/com/github/kuangcp/tank/util/executor/CommonEventExecutor.java @@ -14,14 +14,14 @@ public class CommonEventExecutor { /** * 事件循环任务 */ - public static void loopEventSpin(BlockingQueue queue) { + public static void loopEventSpin(String type, BlockingQueue queue) { while (true) { try { final AbstractLoopEvent event = queue.take(); final long delay = event.getDelay(TimeUnit.MILLISECONDS); // 事件延迟调度警告,且过滤掉初次运行任务 if (delay < -200 && delay > -1000_000) { - log.info("delay {}ms", delay); + log.info("[{}] delay {}ms", type, delay); } final long start = System.nanoTime(); event.run(); @@ -33,7 +33,7 @@ public static void loopEventSpin(BlockingQueue queue) { queue.add(event); } } catch (InterruptedException e) { - log.error("invoke loop event error", e); + log.error("[{}]invoke loop event error", type, e); break; } } diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/executor/LoopEventExecutor.java b/gui/src/main/java/com/github/kuangcp/tank/util/executor/LoopEventExecutor.java index 9c22d0ca..0bcbf38a 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/util/executor/LoopEventExecutor.java +++ b/gui/src/main/java/com/github/kuangcp/tank/util/executor/LoopEventExecutor.java @@ -15,7 +15,7 @@ public class LoopEventExecutor { static final BlockingQueue queue = new DelayQueue<>(); - private static final int EVENT_POOL_SIZE = 6; + private static final int EVENT_POOL_SIZE = 8; /** * 循环事件线程池 @@ -24,7 +24,7 @@ public class LoopEventExecutor { public static void init() { for (int i = 0; i < EVENT_POOL_SIZE; i++) { - loopEventPool.execute(() -> CommonEventExecutor.loopEventSpin(queue)); + loopEventPool.execute(() -> CommonEventExecutor.loopEventSpin(LoopEventExecutor.class.getSimpleName(), queue)); } } diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/executor/MonitorExecutor.java b/gui/src/main/java/com/github/kuangcp/tank/util/executor/MonitorExecutor.java index c8bf62fa..8d019a21 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/util/executor/MonitorExecutor.java +++ b/gui/src/main/java/com/github/kuangcp/tank/util/executor/MonitorExecutor.java @@ -25,7 +25,7 @@ public static void init() { // 2. 注册管理任务 registerEventMonitor(); - monitorEventPool.execute(() -> CommonEventExecutor.loopEventSpin(queue)); + monitorEventPool.execute(() -> CommonEventExecutor.loopEventSpin(MonitorExecutor.class.getSimpleName(), queue)); } public static class Info { diff --git a/gui/src/main/java/com/github/kuangcp/tank/v3/PlayStageMgr.java b/gui/src/main/java/com/github/kuangcp/tank/v3/PlayStageMgr.java index 8b6a36d9..835fa7d6 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/v3/PlayStageMgr.java +++ b/gui/src/main/java/com/github/kuangcp/tank/v3/PlayStageMgr.java @@ -44,7 +44,7 @@ public class PlayStageMgr { /** * 无敌状态 时间 */ - static long invincibleMs = 3000L; + static long invincibleMs = 5000L; // 场景 上下文 public List enemyTanks; @@ -188,6 +188,28 @@ public static boolean ablePassByHero(Tank t) { return TankTool.ablePass(t, instance.hero); } + public static boolean ablePassByHinder(Tank t) { + if (Objects.isNull(instance) || Objects.isNull(instance.hero) || !instance.hero.isAlive()) { + return true; + } + + for (int i = 0; i < instance.bricks.size(); i++) { + final boolean pass = TankTool.ablePass(t, instance.bricks.get(i)); + if (!pass) { + return false; + } + } + + for (int i = 0; i < instance.irons.size(); i++) { + final boolean pass = TankTool.ablePass(t, instance.irons.get(i)); + if (!pass) { + return false; + } + } + + return true; + } + public int getLiveEnemy() { int result = 0; for (int i = 0; i < this.enemyTanks.size(); i++) { From 2bef112a754c895aae04707959d3e3bf3d2d5a8d Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Sun, 31 Oct 2021 20:09:19 +0800 Subject: [PATCH 254/476] *) hinder size --- .../com/github/kuangcp/tank/domain/Brick.java | 10 +++++++++ .../kuangcp/tank/panel/TankGroundPanel.java | 21 ++++++++++--------- .../github/kuangcp/tank/util/TankTool.java | 4 ++-- 3 files changed, 23 insertions(+), 12 deletions(-) diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/Brick.java b/gui/src/main/java/com/github/kuangcp/tank/domain/Brick.java index b32f55ab..bab7b712 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/domain/Brick.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/Brick.java @@ -9,6 +9,16 @@ public Brick(int hx, int hy) { super(hx, hy); } + @Override + public int getWidth() { + return 5; + } + + @Override + public int getHeight() { + return 5; + } + /** * 构造器全是沿用父类的,父类要是没有无参构造器,子类有了就属于非法 * 并且 父类有了含参构造器,子类必须也要有,显式的super一下 diff --git a/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java b/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java index a02d5a30..fd3f8f9f 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java +++ b/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java @@ -10,7 +10,7 @@ import com.github.kuangcp.tank.resource.ColorMgr; import com.github.kuangcp.tank.util.ExecutePool; import com.github.kuangcp.tank.util.HoldingKeyEventMgr; -import com.github.kuangcp.tank.util.KeyListener; +import com.github.kuangcp.tank.util.HeroKeyListener; import com.github.kuangcp.tank.util.TankTool; import com.github.kuangcp.tank.util.executor.AbstractDelayEvent; import com.github.kuangcp.tank.util.executor.DelayExecutor; @@ -167,7 +167,7 @@ public void paint(Graphics g) { for (int i = 0; i < irons.size(); i++) { Iron ir = irons.get(i); if (ir.getAlive()) { - g.fill3DRect(ir.getHx(), ir.getHy(), 20, 10, false); + g.fill3DRect(ir.getHx(), ir.getHy(), ir.getWidth(), ir.getHeight(), false); } else { irons.remove(ir); } @@ -177,7 +177,7 @@ public void paint(Graphics g) { for (int i = 0; i < bricks.size(); i++) { Brick bs = bricks.get(i); if (bs.getAlive()) { - g.fill3DRect(bs.getHx(), bs.getHy(), 20, 10, false); + g.fill3DRect(bs.getHx(), bs.getHy(), bs.getWidth(), bs.getHeight(), false); } else { bricks.remove(bs); } @@ -381,11 +381,12 @@ public void keyTyped(KeyEvent e) { } /** - * 创建 一块矩形的砖(20*10) 的函数 + * 创建砖 */ public void createB(List bricks, int startX, int startY, int endX, int endY) { - for (int i = startX; i < endX; i += 20) { - for (int j = startY; j < endY; j += 10) { + Brick template = new Brick(0, 0); + for (int i = startX; i < endX; i += template.getWidth()) { + for (int j = startY; j < endY; j += template.getHeight()) { Brick bs = new Brick(i, j); bricks.add(bs); } @@ -393,14 +394,14 @@ public void createB(List bricks, int startX, int startY, int endX, int en } /** - * 创建铁块(20*10) + * 创建铁块 */ public void createI(List irons, int startX, int startY, int endX, int endY) { - for (int i = startX; i < endX; i += 20) { - for (int j = startY; j < endY; j += 10) { + Iron template = new Iron(0, 0); + for (int i = startX; i < endX; i += template.getWidth()) { + for (int j = startY; j < endY; j += template.getHeight()) { Iron bs = new Iron(i, j); irons.add(bs); - } } } diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/TankTool.java b/gui/src/main/java/com/github/kuangcp/tank/util/TankTool.java index 5491cfb0..07aabd3f 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/util/TankTool.java +++ b/gui/src/main/java/com/github/kuangcp/tank/util/TankTool.java @@ -228,8 +228,8 @@ public static boolean ablePass(Tank t, Hinder h) { * 子弹和障碍物 碰撞监测 */ public static void judgeHint(Bullet s, Hinder h) { - if (s.sx >= h.getHx() - 1 && s.sx <= h.getHx() + 20 - && s.sy >= h.getHy() - 1 && s.sy <= h.getHy() + 10) { + if (s.sx >= h.getHx() - 1 && s.sx <= h.getHx() + h.getWidth() + && s.sy >= h.getHy() - 1 && s.sy <= h.getHy() + h.getHeight()) { s.alive = false; if (h instanceof Brick) { From 388a1a3640f42e5d319bc4c9a52dd94f360de1be Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Tue, 7 Dec 2021 08:23:00 +0800 Subject: [PATCH 255/476] +) async eventbus --- .../test/java/guava/eventbus/BlockTest.java | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 guava/src/test/java/guava/eventbus/BlockTest.java diff --git a/guava/src/test/java/guava/eventbus/BlockTest.java b/guava/src/test/java/guava/eventbus/BlockTest.java new file mode 100644 index 00000000..6a684880 --- /dev/null +++ b/guava/src/test/java/guava/eventbus/BlockTest.java @@ -0,0 +1,74 @@ +package guava.eventbus; + +import com.google.common.eventbus.AsyncEventBus; +import com.google.common.eventbus.Subscribe; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +/** + * @author https://github.com/kuangcp on 2021-12-07 07:29 + */ +@Slf4j +public class BlockTest { + @Data + static class LoginEvent { + private Integer userId; + } + + @Slf4j + static class BlockEventListener { + + @Subscribe +// @AllowConcurrentEvents + public void blockMethod(LoginEvent event) throws InterruptedException { + log.warn("block: {}", event.getUserId()); + if (event.getUserId() == 0) { + final CountDownLatch latch = new CountDownLatch(5); + latch.await(); + } + log.warn("block finish {}", event.getUserId()); + } + } + + @Slf4j + static class LoginEventListener { + + @Subscribe +// @AllowConcurrentEvents + public void handleLog(LoginEvent event) throws InterruptedException { + log.info("event={}", event.getUserId()); + Thread.sleep(30); + log.info("finish {}", event.getUserId()); + } + } + + /** + * TODO 为什么会阻塞后,还能有部分event被消费 + * + * @see com.google.common.eventbus.Subscriber#invokeSubscriberMethod(java.lang.Object) 使用注解 @AllowConcurrentEvents 后 + * @see com.google.common.eventbus.Subscriber.SynchronizedSubscriber#invokeSubscriberMethod(java.lang.Object) + */ + @Test + public void testBlockAsync() throws Exception { + final ExecutorService pool = Executors.newFixedThreadPool(3); + final AsyncEventBus asyncEventBus = new AsyncEventBus(pool); + // TODO 注册顺序也会影响结果 + asyncEventBus.register(new LoginEventListener()); + asyncEventBus.register(new BlockEventListener()); + + for (int i = 0; i < 7; i++) { + final LoginEvent event = new LoginEvent(); + event.setUserId(i); + asyncEventBus.post(event); + log.info("post {}", i); + } + + TimeUnit.SECONDS.sleep(100); + } +} From 398b05ce0269b20d8ed3854e737ee8e54d74ead2 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Tue, 7 Dec 2021 08:27:35 +0800 Subject: [PATCH 256/476] *) comment --- guava/src/test/java/guava/eventbus/BlockTest.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/guava/src/test/java/guava/eventbus/BlockTest.java b/guava/src/test/java/guava/eventbus/BlockTest.java index 6a684880..032e6b8d 100644 --- a/guava/src/test/java/guava/eventbus/BlockTest.java +++ b/guava/src/test/java/guava/eventbus/BlockTest.java @@ -49,13 +49,14 @@ public void handleLog(LoginEvent event) throws InterruptedException { } /** - * TODO 为什么会阻塞后,还能有部分event被消费 + * TODO 从日志上看为什么会阻塞后,还能有部分event被消费 * - * @see com.google.common.eventbus.Subscriber#invokeSubscriberMethod(java.lang.Object) 使用注解 @AllowConcurrentEvents 后 + * @see com.google.common.eventbus.Subscriber#invokeSubscriberMethod(java.lang.Object) 使用注解 @AllowConcurrentEvents 后 消费事件的方式 * @see com.google.common.eventbus.Subscriber.SynchronizedSubscriber#invokeSubscriberMethod(java.lang.Object) */ @Test public void testBlockAsync() throws Exception { + // TODO 陷入 一个线程 Park,其他线程 Monitor,表现为:整个线程池无法消费任务,队列阻塞满 final ExecutorService pool = Executors.newFixedThreadPool(3); final AsyncEventBus asyncEventBus = new AsyncEventBus(pool); // TODO 注册顺序也会影响结果 From 2ac928bbf0fdd5b15da43a068d601886038931ca Mon Sep 17 00:00:00 2001 From: kcp Date: Tue, 8 Mar 2022 21:35:29 +0800 Subject: [PATCH 257/476] +) timeout pool --- common-config/src/main/resources/logback.xml | 79 ------------------- .../situation/timoutpool/CreateNewPool.java | 13 +-- .../situation/timoutpool/TimeoutExecPool.java | 69 ++++++++++++++++ .../situation/timoutpool/TimeoutFuture.java | 1 + .../timoutpool/base/TaskExecutor.java | 1 + .../timoutpool/base/TimeoutExecutor.java | 12 +++ .../timoutpool/TimeoutExecPoolTest.java | 43 ++++++++++ .../situation/timoutpool/TimeoutPoolTest.java | 25 ++++-- .../github/kuangcp/caculator/Calculator.java | 24 +++--- io/src/test/java/buffer/BufferTest.java | 33 ++++---- .../netty/websocket/NioWebSocketHandler.java | 3 +- netty/src/main/resources/logback.xml | 17 ---- 12 files changed, 183 insertions(+), 137 deletions(-) delete mode 100644 common-config/src/main/resources/logback.xml create mode 100644 concurrency/src/main/java/situation/timoutpool/TimeoutExecPool.java create mode 100644 concurrency/src/main/java/situation/timoutpool/base/TimeoutExecutor.java create mode 100644 concurrency/src/test/java/situation/timoutpool/TimeoutExecPoolTest.java delete mode 100644 netty/src/main/resources/logback.xml diff --git a/common-config/src/main/resources/logback.xml b/common-config/src/main/resources/logback.xml deleted file mode 100644 index 9df33a48..00000000 --- a/common-config/src/main/resources/logback.xml +++ /dev/null @@ -1,79 +0,0 @@ - - - - - - - - - %d{HH:mm:ss.SSS} %highlight(%-5level) [%thread] %logger{30} %highlight(%M):%yellow(%-3L) %msg%n - - - DEBUG - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/concurrency/src/main/java/situation/timoutpool/CreateNewPool.java b/concurrency/src/main/java/situation/timoutpool/CreateNewPool.java index 3f5b0c59..b9e0ab0a 100644 --- a/concurrency/src/main/java/situation/timoutpool/CreateNewPool.java +++ b/concurrency/src/main/java/situation/timoutpool/CreateNewPool.java @@ -24,14 +24,15 @@ public class CreateNewPool implements TaskExecutor { @Override public Result execute(Param param, long timeout, TimeUnit timeUnit) { final LinkedBlockingQueue workQueue = new LinkedBlockingQueue<>(1000); - final Result result = Result.builder().dataList(Collections.synchronizedList(new ArrayList<>())).build(); + final Result result = Result.builder().dataList(Collections.synchronizedList(new ArrayList<>())) + .build(); final ThreadPoolExecutor pool = new ThreadPoolExecutor(3, 3, 1, TimeUnit.SECONDS, workQueue, new ThreadPoolExecutor.DiscardPolicy()); for (int i = 0; i < param.getTotal(); i++) { final Param tmpParam = Param.builder().start(i).build(); pool.execute(() -> { try { - TimeUnit.MILLISECONDS.sleep(ThreadLocalRandom.current().nextInt(600) + 200); + TimeUnit.MILLISECONDS.sleep(ThreadLocalRandom.current().nextInt(600) + 60); } catch (InterruptedException e) { e.printStackTrace(); } @@ -43,15 +44,15 @@ public Result execute(Param param, long timeout, TimeUnit timeUnit) { try { pool.shutdown(); final boolean complete = pool.awaitTermination(timeout, timeUnit); - log.info("complete={}", complete); - if (!complete) { - log.info("workQueue={}", workQueue.size()); + if (complete) { + log.info("complete"); + } else { + log.info("not complete. workQueue={}", workQueue.size()); } } catch (Exception e) { log.error("", e); } - return result; } diff --git a/concurrency/src/main/java/situation/timoutpool/TimeoutExecPool.java b/concurrency/src/main/java/situation/timoutpool/TimeoutExecPool.java new file mode 100644 index 00000000..39863f9b --- /dev/null +++ b/concurrency/src/main/java/situation/timoutpool/TimeoutExecPool.java @@ -0,0 +1,69 @@ +package situation.timoutpool; + +import lombok.extern.slf4j.Slf4j; +import situation.timoutpool.base.TimeoutExecutor; + +import java.time.Duration; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.function.Function; + +/** + * @author kuangcp + */ +@Slf4j +public class TimeoutExecPool implements TimeoutExecutor { + + private final ThreadPoolExecutor pool; + private Duration duration; + + public TimeoutExecPool(int coreSize, Duration timeout) { + this.pool = new ThreadPoolExecutor(coreSize, coreSize, 1, TimeUnit.MINUTES, + new LinkedBlockingQueue<>(1000), new ThreadPoolExecutor.DiscardPolicy()); + this.duration = timeout; + } + + @Override + public List execute(List

params, Function handler) { + List results = Collections.synchronizedList(new ArrayList<>()); + + for (P p : params) { + pool.execute(() -> { + R result = handler.apply(p); + results.add(result); + }); + } + + new Thread(() -> { + while (true) { + try { + Thread.sleep(200); + } catch (InterruptedException e) { + e.printStackTrace(); + } +// log.info("{} ", pool.getQueue().size()); + if (pool.getQueue().size() == 0) { + pool.shutdown(); + break; + } + } + }).start(); + + try { + final boolean complete = pool.awaitTermination(duration.toNanos(), TimeUnit.NANOSECONDS); + if (complete) { +// log.info("complete"); + } else { + log.warn("not complete. workQueue={}", pool.getQueue().size()); + } + } catch (Exception e) { + log.error("", e); + } + + return results; + } +} diff --git a/concurrency/src/main/java/situation/timoutpool/TimeoutFuture.java b/concurrency/src/main/java/situation/timoutpool/TimeoutFuture.java index eaa07528..f2706f0c 100644 --- a/concurrency/src/main/java/situation/timoutpool/TimeoutFuture.java +++ b/concurrency/src/main/java/situation/timoutpool/TimeoutFuture.java @@ -4,6 +4,7 @@ import situation.timoutpool.base.Result; import situation.timoutpool.base.TaskExecutor; +import java.time.Duration; import java.util.concurrent.TimeUnit; /** diff --git a/concurrency/src/main/java/situation/timoutpool/base/TaskExecutor.java b/concurrency/src/main/java/situation/timoutpool/base/TaskExecutor.java index 43ed8c51..328c6bb3 100644 --- a/concurrency/src/main/java/situation/timoutpool/base/TaskExecutor.java +++ b/concurrency/src/main/java/situation/timoutpool/base/TaskExecutor.java @@ -1,5 +1,6 @@ package situation.timoutpool.base; +import java.time.Duration; import java.util.concurrent.TimeUnit; /** diff --git a/concurrency/src/main/java/situation/timoutpool/base/TimeoutExecutor.java b/concurrency/src/main/java/situation/timoutpool/base/TimeoutExecutor.java new file mode 100644 index 00000000..4e2df2f2 --- /dev/null +++ b/concurrency/src/main/java/situation/timoutpool/base/TimeoutExecutor.java @@ -0,0 +1,12 @@ +package situation.timoutpool.base; + +import java.util.List; +import java.util.function.Function; + +/** + * @author kuangcp + */ +public interface TimeoutExecutor { + + List execute(List

param, Function handler); +} diff --git a/concurrency/src/test/java/situation/timoutpool/TimeoutExecPoolTest.java b/concurrency/src/test/java/situation/timoutpool/TimeoutExecPoolTest.java new file mode 100644 index 00000000..6d07c808 --- /dev/null +++ b/concurrency/src/test/java/situation/timoutpool/TimeoutExecPoolTest.java @@ -0,0 +1,43 @@ +package situation.timoutpool; + + +import lombok.extern.slf4j.Slf4j; + +import java.time.Duration; +import java.util.List; +import java.util.Objects; +import java.util.UUID; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +/** + * @author kuangcp + */ +@Slf4j +class TimeoutExecPoolTest { + + @Test + public void testRequest() throws InterruptedException { +// TimeoutExecPool timeoutPool = new TimeoutExecPool<>(10, Duration.ofSeconds(10)); + TimeoutExecPool timeoutPool = new TimeoutExecPool<>(10, Duration.ofSeconds(5)); + List params = IntStream.range(0, 300).mapToObj(v -> UUID.randomUUID().toString().substring(0, 5) + v).collect(Collectors.toList()); +// log.info("start param={}", params); + log.info("start"); + List result = timeoutPool.execute(params, this::logic); +// log.info("end: result={}", result); + log.info("end"); +// Thread.currentThread().join(); + } + + private Long logic(String param) { + if (Objects.isNull(param)) { + return 0L; + } + try { + Thread.sleep(150); + } catch (InterruptedException e) { + e.printStackTrace(); + } + return (long) param.length(); + } +} \ No newline at end of file diff --git a/concurrency/src/test/java/situation/timoutpool/TimeoutPoolTest.java b/concurrency/src/test/java/situation/timoutpool/TimeoutPoolTest.java index ba562368..4bc3d349 100644 --- a/concurrency/src/test/java/situation/timoutpool/TimeoutPoolTest.java +++ b/concurrency/src/test/java/situation/timoutpool/TimeoutPoolTest.java @@ -10,7 +10,6 @@ import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; -import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; /** @@ -21,19 +20,33 @@ public class TimeoutPoolTest { @Test public void testCreateNew() throws Exception { - int loop = 6000; + int loop = 10; final CountDownLatch latch = new CountDownLatch(loop); - final ExecutorService exe = Executors.newFixedThreadPool(loop / 15); + final ExecutorService exe = Executors.newFixedThreadPool(loop); for (int i = 0; i < loop; i++) { exe.execute(() -> { final CreateNewPool pool = new CreateNewPool(); final long start = System.nanoTime(); - final Result result = pool.execute(Param.builder().start(1).total(40).build(), 5, TimeUnit.SECONDS); - log.info("result={} {}ms", result, (System.nanoTime() - start) / 1000_000); + final Result result = pool.execute(Param.builder().start(1).total(40).build(), 5, + TimeUnit.SECONDS); + log.info("{}ms {}", (System.nanoTime() - start) / 1000_000, result); latch.countDown(); - assertThat(result.getDataList(), equalTo(40)); +// assertThat(result.getDataList(), equalTo(40)); + log.info("size={}", result.getDataList().size()); }); } latch.await(); + log.info("end"); + } + + @Test + public void testOnce() { + final CreateNewPool pool = new CreateNewPool(); + final long start = System.nanoTime(); + final Result result = pool.execute(Param.builder().start(1).total(40).build(), 5, + TimeUnit.SECONDS); + log.info("{}ms {}", (System.nanoTime() - start) / 1000_000, result); +// assertThat(result.getDataList(), equalTo(40)); + log.info("size={}", result.getDataList().size()); } } \ No newline at end of file diff --git a/gui/src/main/java/com/github/kuangcp/caculator/Calculator.java b/gui/src/main/java/com/github/kuangcp/caculator/Calculator.java index f75ee22b..c2d42bc5 100644 --- a/gui/src/main/java/com/github/kuangcp/caculator/Calculator.java +++ b/gui/src/main/java/com/github/kuangcp/caculator/Calculator.java @@ -33,21 +33,21 @@ public class Calculator extends JFrame { private boolean numFlag = false;//用于判断是否输入了数字 private boolean calculateFlag = false;//用于判断是否按下了等号运算符 - private JTextField txtResult = new JTextField("0"); - private JTextField inputCache = new JTextField(""); - private JButton btnNull = new JButton(" "); + private final JTextField txtResult = new JTextField("0"); + private final JTextField inputCache = new JTextField(""); + private final JButton btnNull = new JButton(" "); - private JButton btnDecrease = new JButton("-"); - private JButton btnBegin = new JButton("C"); + private final JButton btnDecrease = new JButton("-"); + private final JButton btnBegin = new JButton("C"); - private JButton btnMultiply = new JButton("*"); - private JButton btnCancel = new JButton("←"); + private final JButton btnMultiply = new JButton("*"); + private final JButton btnCancel = new JButton("←"); - private JButton btnMinus = new JButton("+/-"); - private JButton btnPoint = new JButton("."); - private JButton btnDivide = new JButton("/"); - private JButton btnEqual = new JButton("="); - private JButton btnIncrease = new JButton("+"); + private final JButton btnMinus = new JButton("+/-"); + private final JButton btnPoint = new JButton("."); + private final JButton btnDivide = new JButton("/"); + private final JButton btnEqual = new JButton("="); + private final JButton btnIncrease = new JButton("+"); private Calculator() { try { diff --git a/io/src/test/java/buffer/BufferTest.java b/io/src/test/java/buffer/BufferTest.java index 02468992..67c4226e 100644 --- a/io/src/test/java/buffer/BufferTest.java +++ b/io/src/test/java/buffer/BufferTest.java @@ -1,5 +1,7 @@ package buffer; +import java.io.File; + import lombok.extern.slf4j.Slf4j; import org.junit.Test; @@ -8,25 +10,26 @@ /** * Created by https://github.com/kuangcp * TODO StringBuffer + * * @author kuangcp */ @Slf4j public class BufferTest { - /** - * flip 方法直译就是翻转的意思, 也就是说在读写之间切换, 做好准备工作 - */ - @Test - public void testFlip() { - IntBuffer buffer = IntBuffer.allocate(1024); - buffer.put(1); - // 需要使用flip方法来进行模式的切换, 如果没有做读写操作就直接调用就会抛出 BufferOverflowException 异常 - // 也就是说, 在写和读操作后, 需要调用该方法才能进行下一步的写和读 - buffer.flip(); - buffer.put(2); - buffer.flip(); - int result = buffer.get(); + /** + * flip 方法直译就是翻转的意思, 也就是说在读写之间切换, 做好准备工作 + */ + @Test + public void testFlip() { + IntBuffer buffer = IntBuffer.allocate(1024); + buffer.put(1); + // 需要使用flip方法来进行模式的切换, 如果没有做读写操作就直接调用就会抛出 BufferOverflowException 异常 + // 也就是说, 在写和读操作后, 需要调用该方法才能进行下一步的写和读 + buffer.flip(); + buffer.put(2); + buffer.flip(); + int result = buffer.get(); - log.info("result={}", result); - } + log.info("result={}", result); + } } diff --git a/netty/src/main/java/netty/websocket/NioWebSocketHandler.java b/netty/src/main/java/netty/websocket/NioWebSocketHandler.java index a54e1f83..508a7cfc 100644 --- a/netty/src/main/java/netty/websocket/NioWebSocketHandler.java +++ b/netty/src/main/java/netty/websocket/NioWebSocketHandler.java @@ -69,8 +69,7 @@ private void handlerWebSocketFrame(ChannelHandlerContext ctx, WebSocketFrame fra // 判断是否ping消息 if (frame instanceof PingWebSocketFrame) { - ctx.channel().write( - new PongWebSocketFrame(frame.content().retain())); + ctx.channel().write(new PongWebSocketFrame(frame.content().retain())); return; } diff --git a/netty/src/main/resources/logback.xml b/netty/src/main/resources/logback.xml deleted file mode 100644 index d5538e4b..00000000 --- a/netty/src/main/resources/logback.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - %d{HH:mm:ss.SSS} %highlight(%-5level) [%thread] %logger{30} %highlight(%M):%yellow(%-3L) %msg%n - - - INFO - - - - - - - \ No newline at end of file From 76ebd77ba9addd0b3720d13561bf458023e82dc0 Mon Sep 17 00:00:00 2001 From: kcp Date: Fri, 29 Apr 2022 22:18:22 +0800 Subject: [PATCH 258/476] *) rename --- class/src/main/java/jvm/oom/JvmStackOOM.java | 28 --------------- .../main/java/jvm/oom/JvmStackOverFlow.java | 34 +++++++++---------- class/src/main/java/jvm/oom/ThreadOOM.java | 31 +++++++---------- .../main/java/situation/timoutpool/Readme.md | 1 + 4 files changed, 31 insertions(+), 63 deletions(-) delete mode 100644 class/src/main/java/jvm/oom/JvmStackOOM.java diff --git a/class/src/main/java/jvm/oom/JvmStackOOM.java b/class/src/main/java/jvm/oom/JvmStackOOM.java deleted file mode 100644 index fa5459ce..00000000 --- a/class/src/main/java/jvm/oom/JvmStackOOM.java +++ /dev/null @@ -1,28 +0,0 @@ -package jvm.oom; - -/** - * -Xss2M - * - * 在容器中运行 结果竟然是 内存满载一段时间后 终端输出了 Killed .. 系统并没有卡死 - * TODO 可能 Windows 会卡死吧 - * - * @author kuangcp on 4/3/19-11:10 PM - */ -public class JvmStackOOM { - - private void doLoop() { - while (true) { - } - } - - private void stackLeakByThread() { - while (true) { - new Thread(this::doLoop); - } - } - - public static void main(String[] args) { - JvmStackOOM oom = new JvmStackOOM(); - oom.stackLeakByThread(); - } -} diff --git a/class/src/main/java/jvm/oom/JvmStackOverFlow.java b/class/src/main/java/jvm/oom/JvmStackOverFlow.java index a1eeda24..87b29ff9 100644 --- a/class/src/main/java/jvm/oom/JvmStackOverFlow.java +++ b/class/src/main/java/jvm/oom/JvmStackOverFlow.java @@ -12,27 +12,27 @@ @Slf4j public class JvmStackOverFlow { - private int count = 1; + private int count = 1; - private void stackLeak() { - int a = 0; - int b = 0; - int c = 0; - int d = 0; + private void stackLeak() { + int a = 0; + int b = 0; + int c = 0; + int d = 0; - count++; - stackLeak(); - } + count++; + stackLeak(); + } - public static void main(String[] args) { - JvmStackOverFlow overFlow = new JvmStackOverFlow(); - try { - overFlow.stackLeak(); + public static void main(String[] args) { + JvmStackOverFlow overFlow = new JvmStackOverFlow(); + try { + overFlow.stackLeak(); - // 不能用 Exception 因为 Error 和 Exception 是两个体系... - } catch (Throwable e) { - log.info("count={}", overFlow.count); + // 不能用 Exception 因为 Error 和 Exception 是两个体系... + } catch (Throwable e) { + log.info("count={}", overFlow.count); // log.error(e.getMessage(), e); + } } - } } diff --git a/class/src/main/java/jvm/oom/ThreadOOM.java b/class/src/main/java/jvm/oom/ThreadOOM.java index f02315fd..aad0ee8f 100644 --- a/class/src/main/java/jvm/oom/ThreadOOM.java +++ b/class/src/main/java/jvm/oom/ThreadOOM.java @@ -1,33 +1,28 @@ package jvm.oom; -import java.util.concurrent.LinkedBlockingDeque; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; - /** * set max thread: echo 1000 > /proc/sys/kernel/threads-max - * + *

* OutOfMemoryError: unable to create new native thread * * @author https://github.com/kuangcp on 2019-12-25 20:05 */ public class ThreadOOM { - public static void main(String[] args) { - while (true) { - ThreadPoolExecutor pool = new ThreadPoolExecutor(16, 20, 2, TimeUnit.MINUTES, - new LinkedBlockingDeque<>()); + private void doLoop() { + while (true) { + } + } - pool.submit(() -> { - int a = 1; - }); + private void stackLeakByThread() { + while (true) { + new Thread(this::doLoop); + } + } - try { - Thread.sleep(10); - } catch (InterruptedException e) { - e.printStackTrace(); - } + public static void main(String[] args) { + ThreadOOM oom = new ThreadOOM(); + oom.stackLeakByThread(); } - } } diff --git a/concurrency/src/main/java/situation/timoutpool/Readme.md b/concurrency/src/main/java/situation/timoutpool/Readme.md index e69de29b..3358d242 100644 --- a/concurrency/src/main/java/situation/timoutpool/Readme.md +++ b/concurrency/src/main/java/situation/timoutpool/Readme.md @@ -0,0 +1 @@ +对比临时线程池和 CompleteFuture 实现方案 From 643822a17934a5a72ba5fcf0bfb59ef1e0f71bf0 Mon Sep 17 00:00:00 2001 From: kcp Date: Mon, 27 Jun 2022 22:31:56 +0800 Subject: [PATCH 259/476] *) readme --- .gitignore | 3 +++ .../test/java/com/github/kuangcp/reference/Apple.java | 2 +- generic/src/main/java/com/github/kuangcp/README.md | 2 +- .../main/java/netty/websocket/NioWebSocketServer.java | 10 ++++------ 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index 98afe137..d3c8eb40 100644 --- a/.gitignore +++ b/.gitignore @@ -26,3 +26,6 @@ Servers/ .gradle build/ .gradletasknamecache +gradle/ +gradlew* + diff --git a/class/src/test/java/com/github/kuangcp/reference/Apple.java b/class/src/test/java/com/github/kuangcp/reference/Apple.java index 02a980ce..2c9c3c2b 100644 --- a/class/src/test/java/com/github/kuangcp/reference/Apple.java +++ b/class/src/test/java/com/github/kuangcp/reference/Apple.java @@ -20,6 +20,6 @@ class Apple { @Override protected void finalize() throws Throwable { super.finalize(); - log.info("Apply: finalize={}", name); +// log.info("Apple: finalize={}", name); } } diff --git a/generic/src/main/java/com/github/kuangcp/README.md b/generic/src/main/java/com/github/kuangcp/README.md index c2b02ed6..8c4730f5 100644 --- a/generic/src/main/java/com/github/kuangcp/README.md +++ b/generic/src/main/java/com/github/kuangcp/README.md @@ -1,3 +1,3 @@ # Java泛型 -> [总结](https://github.com/Kuangcp/Note/blob/master/Java/AdvancedLearning/Generics.md) +> [总结](https://github.com/Kuangcp/Note/blob/master/Java/AdvancedLearning/JavaGenerics.md) diff --git a/netty/src/main/java/netty/websocket/NioWebSocketServer.java b/netty/src/main/java/netty/websocket/NioWebSocketServer.java index 779b05fa..1163810f 100644 --- a/netty/src/main/java/netty/websocket/NioWebSocketServer.java +++ b/netty/src/main/java/netty/websocket/NioWebSocketServer.java @@ -4,16 +4,14 @@ import io.netty.channel.Channel; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioServerSocketChannel; -import lombok.extern.slf4j.Slf4j; /** * @author https://github.com/kuangcp on 2021-05-18 08:32 */ -@Slf4j public class NioWebSocketServer { private void init() { - log.info("正在启动WebSocket服务器"); +// log.info("正在启动WebSocket服务器"); NioEventLoopGroup boss = new NioEventLoopGroup(); NioEventLoopGroup work = new NioEventLoopGroup(); try { @@ -22,14 +20,14 @@ private void init() { bootstrap.channel(NioServerSocketChannel.class); bootstrap.childHandler(new NioWebSocketChannelInitializer()); Channel channel = bootstrap.bind(7094).sync().channel(); - log.info("WebSocket服务器启动成功:" + channel); +// log.info("WebSocket服务器启动成功:" + channel); channel.closeFuture().sync(); } catch (InterruptedException e) { - log.info("", e); +// log.info("", e); } finally { boss.shutdownGracefully(); work.shutdownGracefully(); - log.info("WebSocket服务器已关闭"); +// log.info("WebSocket服务器已关闭"); } } From a499ba0e32506e277a66e006a71d696873f80263 Mon Sep 17 00:00:00 2001 From: kcp Date: Thu, 30 Jun 2022 16:11:10 +0800 Subject: [PATCH 260/476] +) common code map --- dependency.gradle | 1 + question/build.gradle | 4 + .../commoncode/CommonRelationTest.java | 412 ++++++++++++++++++ 3 files changed, 417 insertions(+) create mode 100644 question/src/test/java/com/github/kuangcp/situation/commoncode/CommonRelationTest.java diff --git a/dependency.gradle b/dependency.gradle index df80d7ca..7b31cffb 100644 --- a/dependency.gradle +++ b/dependency.gradle @@ -29,6 +29,7 @@ ext { // tool , "lombok" : "org.projectlombok:lombok:1.18.2" , "common-lang" : "org.apache.commons:commons-lang3:3.7" + , "commons-collections4" : "org.apache.commons:commons-collections4:4.4" , "groovy" : "org.codehaus.groovy:groovy-all:$ver.groovy" , "cglib" : "cglib:cglib:3.2.9" , "common-math" : "org.apache.commons:commons-math3:3.6.1" diff --git a/question/build.gradle b/question/build.gradle index 838c6995..2131f752 100644 --- a/question/build.gradle +++ b/question/build.gradle @@ -5,4 +5,8 @@ dependencies { implementation libs["cglib-3.2.4"] implementation libs["asm-3.1"] implementation libs["asm-5.1"] + implementation libs["spring-core"] + implementation libs["common-lang"] + implementation libs["commons-collections4"] + implementation libs["gson"] } \ No newline at end of file diff --git a/question/src/test/java/com/github/kuangcp/situation/commoncode/CommonRelationTest.java b/question/src/test/java/com/github/kuangcp/situation/commoncode/CommonRelationTest.java new file mode 100644 index 00000000..f21d693e --- /dev/null +++ b/question/src/test/java/com/github/kuangcp/situation/commoncode/CommonRelationTest.java @@ -0,0 +1,412 @@ +package com.github.kuangcp.situation.commoncode; + +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.junit.Assert; +import org.junit.Test; +import org.springframework.util.StopWatch; + +import java.io.IOException; +import java.lang.reflect.Type; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.*; +import java.util.concurrent.*; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +/** + * @author KuangChengPing@51peiq.com on 2022-06-30 10:07 + */ +@Slf4j +public class CommonRelationTest { + + @Builder + @Data + @NoArgsConstructor + @AllArgsConstructor + static class Parts { + // 用于equal函数 + private String id; + private String parts; + private String first; + private String second; + + public Parts(String parts, String first, String second) { + this.parts = parts; + this.first = first; + this.second = second; + } + } + + @Data + @AllArgsConstructor + static class Common { + private String id; + private String parts; + } + + int partsSize = 10000; + int poolSize = 100000; + + private String randStr(List pool) { + return pool.get(new Random().nextInt(poolSize)); + } + + private List initData() { + List pool = IntStream.range(0, poolSize) + .mapToObj(v -> UUID.randomUUID().toString().substring(0, 8)) + .collect(Collectors.toList()); + + List parts = new ArrayList<>(); + for (int i = 0; i < partsSize; i++) { + Parts p = new Parts(System.nanoTime() + "" + i, + randStr(pool), + randStr(pool), + randStr(pool)); + parts.add(p); + } + return parts; + } + + @Test + public void testGenData() throws IOException { + List parts = initData(); + Gson gson = new Gson(); + String s = gson.toJson(parts); + Files.write(Paths.get("b.json"), s.getBytes(StandardCharsets.UTF_8)); + } + + private List readData() throws IOException { + Path path = Paths.get("b.json"); + byte[] bytes = Files.readAllBytes(path); + Gson gson = new Gson(); + Type type = new TypeToken>() { + }.getType(); + return gson.fromJson(new String(bytes), type); + } + + private void appendParts(Map> cache, String parts, Set tmp) { + Set exist = cache.get(parts); + if (Objects.nonNull(exist)) { + exist.addAll(tmp); + } else { + cache.put(parts, tmp); + } + } + + + ExecutorService pool = Executors.newFixedThreadPool(5); + + private Map> loopCodeMap(List parts) throws InterruptedException { + final StopWatch stopWatch = new StopWatch(); + stopWatch.start("cache"); + Map> cache = new HashMap<>(partsSize * 3); + for (Parts part : parts) { + Set tmp = Stream.of(part.getParts(), part.getFirst(), part.getSecond()) + .filter(StringUtils::isNoneBlank).collect(Collectors.toSet()); + if (tmp.size() == 1) { + continue; + } + appendParts(cache, part.getParts(), tmp); + appendParts(cache, part.getFirst(), tmp); + appendParts(cache, part.getSecond(), tmp); + } + stopWatch.stop(); + + Map> result = mergeWithMain(stopWatch, cache); + + int i = 0; + int pc = 0; + for (Map.Entry> entry : result.entrySet()) { + int size = entry.getValue().size(); + pc += size; + if (size > 3) { + i++; +// System.out.print(size + ","); + } + } + + log.info("avg={}", pc / result.size()); + + log.info("配件数{} 去重总编码数 {} 通用码块={} 块内编码数 {} 合并次数={} \n{}", + parts.size(), cache.size(), result.size(), pc, i, stopWatch.prettyPrint()); +// log.info(": result={}", result); + + return result; + } + + private Map> mergeWithMain(StopWatch stopWatch, Map> cache) throws InterruptedException { + stopWatch.start("merge"); + Map> result = new HashMap<>(); + Map handled = new HashMap<>(); + for (Map.Entry> entry : cache.entrySet()) { + String code = entry.getKey(); + if (handled.containsKey(code)) { + continue; + } + + Set total = new HashSet<>(); + loopFind(cache, total, handled, code); + result.put(UUID.randomUUID().toString(), total); + } + stopWatch.stop(); + return result; + } + + /** + * 引入线程池,但是数据集太分散 锁的调度开销 远大于多层循环耗时 最终执行时间翻了三倍 + * + * @see CommonRelationTest#mergeWithMain(StopWatch, Map) + */ + @Deprecated + private Map> mergeWithPool(StopWatch stopWatch, Map> cache) throws InterruptedException { + stopWatch.start("merge"); + CountDownLatch latch = new CountDownLatch(cache.entrySet().size()); + Map> result = new ConcurrentHashMap<>(); + Map handled = new ConcurrentHashMap<>(); + for (Map.Entry> entry : cache.entrySet()) { + String code = entry.getKey(); + if (handled.containsKey(code)) { + latch.countDown(); + continue; + } + + pool.execute(() -> { + Set total = new HashSet<>(); + loopFind(cache, total, handled, code); + result.put(UUID.randomUUID().toString(), total); + latch.countDown(); + }); + } + latch.await(); + stopWatch.stop(); + return result; + } + + // -ea -Xmx500m -XX:+UseG1GC -XX:+UseStringDeduplication -XX:+PrintStringDeduplicationStatistics -XX:+PrintStringTableStatistics + + /** + * 500000 500000 申请了 100M 内存 + *

running time (millis) = 3629 + * ----------------------------------------- + * ms % Task name + * ----------------------------------------- + * 00620 017% init + * 00692 019% cache + * 02317 064% merge + */ + @Test + public void testMergeCodeMap() throws IOException, InterruptedException { + TimeUnit.SECONDS.sleep(6); + List parts = readData(); + + for (int i = 0; i < 10; i++) { + Map> result = loopCodeMap(parts); + log.info("finish {}", i); + TimeUnit.SECONDS.sleep(5); + } + + TimeUnit.SECONDS.sleep(100000); + } + + @Test + public void testMergeCodeMapValid() throws InterruptedException { + List parts = new ArrayList<>(); + parts.add(new Parts("A", "B", "C")); + parts.add(new Parts("B", "ii", "X")); + parts.add(new Parts("X", "Y", "P")); + parts.add(new Parts("P", "Xx", "C1")); + parts.add(new Parts("Xx", "uu", "C2")); + parts.add(new Parts("uu", "uuw", "C3")); + + parts.add(new Parts("F", "I", "O")); + + parts.add(new Parts("1", "11", "")); + + parts.add(new Parts("x", " ", null)); + parts.add(new Parts(" ", "uu", null)); + + parts.add(new Parts("22", "2", "222")); + + parts.add(new Parts("y", "c", null)); + parts.add(new Parts("y", "a", null)); + parts.add(new Parts("a", null, "b")); + + + log.info("start"); + Map> result = loopCodeMap(parts); + result.forEach((k, v) -> log.info("{} {}", k, v)); + + Set s = new HashSet<>(); + s.add("F,I,O"); + s.add("2,22,222"); + s.add("1,11"); + s.add("a,b,c,y"); + s.add("A,B,C,C1,C2,C3,P,X,Xx,Y,ii,uu,uuw"); + assertResultMap(s, result); + } + + private void assertResultMap(Set exceptMap, Map> result) { + Map sortMap = new HashMap<>(); + for (Map.Entry> entry : result.entrySet()) { + String key = entry.getKey(); + sortMap.put(key, entry.getValue().stream().filter(StringUtils::isNoneBlank) + .sorted().collect(Collectors.joining(","))); + } + HashSet sameSet = new HashSet<>(); + sameSet.addAll(exceptMap); + sameSet.retainAll(sortMap.values()); + + HashSet re = new HashSet<>(); + + re.addAll(exceptMap); + re.addAll(sortMap.values()); + re.removeAll(sameSet); + + if (re.size() != 0) { + log.info("re={}", re); + Assert.fail(); + } + } + + private void loopFind(Map> cache, Set total, Map handled, String code) { + total.add(code); + handled.put(code, ""); + Set next = cache.get(code); + + Set appendSet = new HashSet<>(); + Set loopSet = new HashSet<>(); + Set swapTmp; + while (CollectionUtils.isNotEmpty(next)) { + swapTmp = appendSet; + appendSet = loopSet; + loopSet = swapTmp; + + appendSet.clear(); + for (String s : next) { + Set sNext = cache.get(s); + if (CollectionUtils.isNotEmpty(sNext)) { +// log.info("{} code={}", total.size(), code); + sNext.stream().filter(v -> !total.contains(v)).forEach(appendSet::add); + } + total.add(s); + handled.put(s, ""); + } + + next = appendSet; + } + } + + private void generateKeyCache(Map> cache, List parts) { + for (Parts part : parts) { + + HashSet tmp = new HashSet<>(Arrays.asList(part.getParts(), part.getFirst(), part.getSecond())); + if (cache.containsKey(part.getParts())) { + Set oldSet = cache.get(part.getParts()); + tmp.addAll(oldSet); + cache.put(part.getParts(), tmp); + } else { + cache.put(part.getParts(), tmp); + + } + if (cache.containsKey(part.getFirst())) { + Set oldSet = cache.get(part.getFirst()); + tmp.addAll(oldSet); + cache.put(part.getFirst(), tmp); + } else { + cache.put(part.getFirst(), tmp); + + } + if (cache.containsKey(part.getSecond())) { + Set oldSet = cache.get(part.getSecond()); + tmp.addAll(oldSet); + cache.put(part.getSecond(), tmp); + } else { + cache.put(part.getSecond(), tmp); + + } + } + } + + /** + * 当数据集中出现关联的数据较多时,栈的深度会爆炸 + * + * @see CommonRelationTest#loopFind + */ + @Deprecated + private void recursiveFind(Map> cache, Set total, String code) { +// log.info(": code={}", code); + //code为编码一/二/三 + total.add(code); + Set smallBlock = cache.get(code); + if (CollectionUtils.isEmpty(smallBlock)) { + return; + } + + log.info("{} code={} {}", total.size(), code, smallBlock); + + smallBlock.stream() + .filter(v -> !total.contains(v)) + .forEach(v -> recursiveFind(cache, total, v)); + } + + @Test + public void testMergeSet() throws IOException { + List parts = readData(); + + Set> cache = new HashSet<>(); + for (Parts part : parts) { + cache.add(new HashSet<>(Arrays.asList(part.getParts(), part.getFirst(), part.getSecond()))); + } + + log.info("before: {}", cache.size()); +// log.info("{}", cache); + int i = 0; + while (mergeWithForEach(cache)) { + i++; + log.info("merge loop:{} size:{}", loopCount, cache.size()); + } + log.info("result: merge:{} size:{}", i, cache.size()); + log.info("{}", cache); + } + + int loopCount = 0; + + private boolean mergeWithForEach(Set> cache) { + boolean hasSame = false; + + for (Set sets : cache) { + for (Set tmp : cache) { + loopCount++; + if (Objects.equals(sets, tmp)) { + continue; + } + hasSame = tmp.stream().anyMatch(sets::contains); + if (hasSame) { +// log.info("{} & {}", sets, tmp); + sets.addAll(tmp); + cache.remove(tmp); + break; + } + } + if (hasSame) { + break; + } + } + + return hasSame; + } + + +} From 087f6f24c4d74ec1227cb1338bd76c5e9f81ebcd Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Thu, 30 Jun 2022 23:33:57 +0800 Subject: [PATCH 261/476] *) fmt --- .../commoncode/CommonRelationTest.java | 239 ++++++++---------- .../dao/jdkproxy/PermissionInterceptor.java | 9 +- .../dao/jdkproxy/TransactionInterceptor.java | 9 +- 3 files changed, 111 insertions(+), 146 deletions(-) diff --git a/question/src/test/java/com/github/kuangcp/situation/commoncode/CommonRelationTest.java b/question/src/test/java/com/github/kuangcp/situation/commoncode/CommonRelationTest.java index f21d693e..d40d11aa 100644 --- a/question/src/test/java/com/github/kuangcp/situation/commoncode/CommonRelationTest.java +++ b/question/src/test/java/com/github/kuangcp/situation/commoncode/CommonRelationTest.java @@ -49,15 +49,101 @@ public Parts(String parts, String first, String second) { } } - @Data - @AllArgsConstructor - static class Common { - private String id; - private String parts; + int partsSize = 30; + int poolSize = 50; + + int loopCount = 0; + + @Test + public void testGenData() throws IOException { + List parts = initData(); + Gson gson = new Gson(); + String s = gson.toJson(parts); + Files.write(Paths.get("b.json"), s.getBytes(StandardCharsets.UTF_8)); + } + // -ea -Xmx500m -XX:+UseG1GC -XX:+UseStringDeduplication -XX:+PrintStringDeduplicationStatistics -XX:+PrintStringTableStatistics + + /** + * 500000 500000 申请了 100M 内存 + *

running time (millis) = 3629 + * ----------------------------------------- + * ms % Task name + * ----------------------------------------- + * 00620 017% init + * 00692 019% cache + * 02317 064% merge + */ + @Test + public void testMergeCodeMap() throws IOException, InterruptedException { + TimeUnit.SECONDS.sleep(6); + List parts = readData(); + + for (int i = 0; i < 100; i++) { + Map> result = loopCodeMap(parts); + log.info("finish {}", i); +// TimeUnit.SECONDS.sleep(5); + } + + TimeUnit.SECONDS.sleep(100000); + } + + @Test + public void testMergeCodeMapValid() throws InterruptedException { + List parts = new ArrayList<>(); + parts.add(new Parts("A", "B", "C")); + parts.add(new Parts("B", "ii", "X")); + parts.add(new Parts("X", "Y", "P")); + parts.add(new Parts("P", "Xx", "C1")); + parts.add(new Parts("Xx", "uu", "C2")); + parts.add(new Parts("uu", "uuw", "C3")); + + parts.add(new Parts("F", "I", "O")); + + parts.add(new Parts("1", "11", "")); + + parts.add(new Parts("x", " ", null)); + parts.add(new Parts(" ", "uu", null)); + + parts.add(new Parts("22", "2", "222")); + + parts.add(new Parts("y", "c", null)); + parts.add(new Parts("y", "a", null)); + parts.add(new Parts("a", null, "b")); + + + log.info("start"); + Map> result = loopCodeMap(parts); + result.forEach((k, v) -> log.info("{} {}", k, v)); + + Set s = new HashSet<>(); + s.add("F,I,O"); + s.add("2,22,222"); + s.add("1,11"); + s.add("a,b,c,y"); + s.add("A,B,C,C1,C2,C3,P,X,Xx,Y,ii,uu,uuw"); + assertResultMap(s, result); + } + + @Test + public void testMergeSet() throws IOException { + List parts = readData(); + + Set> cache = new HashSet<>(); + for (Parts part : parts) { + cache.add(new HashSet<>(Arrays.asList(part.getParts(), part.getFirst(), part.getSecond()))); + } + + log.info("before: {}", cache.size()); +// log.info("{}", cache); + int i = 0; + while (mergeWithForEach(cache)) { + i++; + log.info("merge loop:{} size:{}", loopCount, cache.size()); + } + log.info("result: merge:{} size:{}", i, cache.size()); + log.info("{}", cache); } - int partsSize = 10000; - int poolSize = 100000; private String randStr(List pool) { return pool.get(new Random().nextInt(poolSize)); @@ -79,14 +165,6 @@ private List initData() { return parts; } - @Test - public void testGenData() throws IOException { - List parts = initData(); - Gson gson = new Gson(); - String s = gson.toJson(parts); - Files.write(Paths.get("b.json"), s.getBytes(StandardCharsets.UTF_8)); - } - private List readData() throws IOException { Path path = Paths.get("b.json"); byte[] bytes = Files.readAllBytes(path); @@ -105,9 +183,6 @@ private void appendParts(Map> cache, String parts, Set> loopCodeMap(List parts) throws InterruptedException { final StopWatch stopWatch = new StopWatch(); stopWatch.start("cache"); @@ -165,19 +240,21 @@ private Map> mergeWithMain(StopWatch stopWatch, Map> mergeWithPool(StopWatch stopWatch, Map> cache) throws InterruptedException { stopWatch.start("merge"); + ExecutorService pool = Executors.newFixedThreadPool(7); CountDownLatch latch = new CountDownLatch(cache.entrySet().size()); Map> result = new ConcurrentHashMap<>(); Map handled = new ConcurrentHashMap<>(); for (Map.Entry> entry : cache.entrySet()) { String code = entry.getKey(); - if (handled.containsKey(code)) { + if (Objects.isNull(code) || handled.containsKey(code)) { latch.countDown(); continue; } @@ -194,69 +271,6 @@ private Map> mergeWithPool(StopWatch stopWatch, Map running time (millis) = 3629 - * ----------------------------------------- - * ms % Task name - * ----------------------------------------- - * 00620 017% init - * 00692 019% cache - * 02317 064% merge - */ - @Test - public void testMergeCodeMap() throws IOException, InterruptedException { - TimeUnit.SECONDS.sleep(6); - List parts = readData(); - - for (int i = 0; i < 10; i++) { - Map> result = loopCodeMap(parts); - log.info("finish {}", i); - TimeUnit.SECONDS.sleep(5); - } - - TimeUnit.SECONDS.sleep(100000); - } - - @Test - public void testMergeCodeMapValid() throws InterruptedException { - List parts = new ArrayList<>(); - parts.add(new Parts("A", "B", "C")); - parts.add(new Parts("B", "ii", "X")); - parts.add(new Parts("X", "Y", "P")); - parts.add(new Parts("P", "Xx", "C1")); - parts.add(new Parts("Xx", "uu", "C2")); - parts.add(new Parts("uu", "uuw", "C3")); - - parts.add(new Parts("F", "I", "O")); - - parts.add(new Parts("1", "11", "")); - - parts.add(new Parts("x", " ", null)); - parts.add(new Parts(" ", "uu", null)); - - parts.add(new Parts("22", "2", "222")); - - parts.add(new Parts("y", "c", null)); - parts.add(new Parts("y", "a", null)); - parts.add(new Parts("a", null, "b")); - - - log.info("start"); - Map> result = loopCodeMap(parts); - result.forEach((k, v) -> log.info("{} {}", k, v)); - - Set s = new HashSet<>(); - s.add("F,I,O"); - s.add("2,22,222"); - s.add("1,11"); - s.add("a,b,c,y"); - s.add("A,B,C,C1,C2,C3,P,X,Xx,Y,ii,uu,uuw"); - assertResultMap(s, result); - } - private void assertResultMap(Set exceptMap, Map> result) { Map sortMap = new HashMap<>(); for (Map.Entry> entry : result.entrySet()) { @@ -308,37 +322,6 @@ private void loopFind(Map> cache, Set total, Map> cache, List parts) { - for (Parts part : parts) { - - HashSet tmp = new HashSet<>(Arrays.asList(part.getParts(), part.getFirst(), part.getSecond())); - if (cache.containsKey(part.getParts())) { - Set oldSet = cache.get(part.getParts()); - tmp.addAll(oldSet); - cache.put(part.getParts(), tmp); - } else { - cache.put(part.getParts(), tmp); - - } - if (cache.containsKey(part.getFirst())) { - Set oldSet = cache.get(part.getFirst()); - tmp.addAll(oldSet); - cache.put(part.getFirst(), tmp); - } else { - cache.put(part.getFirst(), tmp); - - } - if (cache.containsKey(part.getSecond())) { - Set oldSet = cache.get(part.getSecond()); - tmp.addAll(oldSet); - cache.put(part.getSecond(), tmp); - } else { - cache.put(part.getSecond(), tmp); - - } - } - } - /** * 当数据集中出现关联的数据较多时,栈的深度会爆炸 * @@ -346,8 +329,7 @@ private void generateKeyCache(Map> cache, List parts) */ @Deprecated private void recursiveFind(Map> cache, Set total, String code) { -// log.info(": code={}", code); - //code为编码一/二/三 +// log.info("code={}", code); total.add(code); Set smallBlock = cache.get(code); if (CollectionUtils.isEmpty(smallBlock)) { @@ -361,28 +343,9 @@ private void recursiveFind(Map> cache, Set total, St .forEach(v -> recursiveFind(cache, total, v)); } - @Test - public void testMergeSet() throws IOException { - List parts = readData(); - - Set> cache = new HashSet<>(); - for (Parts part : parts) { - cache.add(new HashSet<>(Arrays.asList(part.getParts(), part.getFirst(), part.getSecond()))); - } - - log.info("before: {}", cache.size()); -// log.info("{}", cache); - int i = 0; - while (mergeWithForEach(cache)) { - i++; - log.info("merge loop:{} size:{}", loopCount, cache.size()); - } - log.info("result: merge:{} size:{}", i, cache.size()); - log.info("{}", cache); - } - - int loopCount = 0; - + /** + * 双重循环,衰减找出关联数据 O(N2) + */ private boolean mergeWithForEach(Set> cache) { boolean hasSame = false; diff --git a/spring/src/main/java/com/github/kuangcp/proxy/dao/jdkproxy/PermissionInterceptor.java b/spring/src/main/java/com/github/kuangcp/proxy/dao/jdkproxy/PermissionInterceptor.java index 4de59fe6..bc74b7ea 100644 --- a/spring/src/main/java/com/github/kuangcp/proxy/dao/jdkproxy/PermissionInterceptor.java +++ b/spring/src/main/java/com/github/kuangcp/proxy/dao/jdkproxy/PermissionInterceptor.java @@ -2,11 +2,12 @@ import com.github.kuangcp.proxy.dao.common.InterceptorLogic; import com.github.kuangcp.proxy.dao.common.Permission; -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Method; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; + /** * 动态增强,织入权限控制 * @@ -16,8 +17,8 @@ @AllArgsConstructor public class PermissionInterceptor implements InvocationHandler { - private Permission permission; - private Object target; + private final Permission permission; + private final Object target; @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { diff --git a/spring/src/main/java/com/github/kuangcp/proxy/dao/jdkproxy/TransactionInterceptor.java b/spring/src/main/java/com/github/kuangcp/proxy/dao/jdkproxy/TransactionInterceptor.java index 8e0663c4..910f8607 100644 --- a/spring/src/main/java/com/github/kuangcp/proxy/dao/jdkproxy/TransactionInterceptor.java +++ b/spring/src/main/java/com/github/kuangcp/proxy/dao/jdkproxy/TransactionInterceptor.java @@ -2,11 +2,12 @@ import com.github.kuangcp.proxy.dao.common.InterceptorLogic; import com.github.kuangcp.proxy.dao.common.Transaction; -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Method; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; + /** * 动态增强,织入事务控制 * @@ -16,8 +17,8 @@ @AllArgsConstructor public class TransactionInterceptor implements InvocationHandler { - private Transaction transaction; - private Object target; + private final Transaction transaction; + private final Object target; @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { From 31af4416e7688261af29e986c7e0af9f1c5a44bc Mon Sep 17 00:00:00 2001 From: kcp Date: Fri, 22 Jul 2022 14:53:42 +0800 Subject: [PATCH 262/476] feat: timeout task exec control --- .../timoutpool/TimeoutExecPoolTest.java | 101 +++++++++++++++++- 1 file changed, 96 insertions(+), 5 deletions(-) diff --git a/concurrency/src/test/java/situation/timoutpool/TimeoutExecPoolTest.java b/concurrency/src/test/java/situation/timoutpool/TimeoutExecPoolTest.java index 6d07c808..20ea65cc 100644 --- a/concurrency/src/test/java/situation/timoutpool/TimeoutExecPoolTest.java +++ b/concurrency/src/test/java/situation/timoutpool/TimeoutExecPoolTest.java @@ -2,11 +2,11 @@ import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.Test; import java.time.Duration; -import java.util.List; -import java.util.Objects; -import java.util.UUID; +import java.util.*; +import java.util.concurrent.*; import java.util.stream.Collectors; import java.util.stream.IntStream; @@ -29,14 +29,105 @@ public void testRequest() throws InterruptedException { // Thread.currentThread().join(); } + /** + * 可行方案: 使用 ExecutorCompletionService 和 CompletableFuture 组合限制执行时间,汇聚执行结果,取消后续任务 + */ + @Test + public void testCompleteService() { + log.info("start"); + CompletionService cs = new ExecutorCompletionService<>(Executors.newFixedThreadPool(3)); + + int max = 10; + List params = IntStream.range(0, max) + .mapToObj(v -> UUID.randomUUID().toString() + v) + .collect(Collectors.toList()); + List> futures = params.stream().map(v -> cs.submit(() -> this.logic(v))).collect(Collectors.toList()); + + CompletableFuture> future = CompletableFuture.supplyAsync(() -> { + List results = new ArrayList<>(); + for (int i = 0; i < max; i++) { + Future take; + try { + take = cs.take(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + Long result; + try { + result = take.get(); + } catch (InterruptedException | ExecutionException e) { + throw new RuntimeException(e); + } + log.info("{}", result); + results.add(result); + } + return results; + }); + try { + List results = future.get(80000, TimeUnit.MILLISECONDS); + log.info("{}", results); + } catch (InterruptedException | ExecutionException | TimeoutException e) { + log.error("timeout", e); + for (Future longFuture : futures) { + longFuture.cancel(true); + } + } + } + + /** + * 保留部分任务执行数据 中断后续任务 + */ + @Test + public void testCompleteServicePart() { + log.info("start"); + CompletionService cs = new ExecutorCompletionService<>(Executors.newFixedThreadPool(2)); + + int max = 10; + List params = IntStream.range(0, max) + .mapToObj(v -> UUID.randomUUID().toString() + v) + .collect(Collectors.toList()); + List> futures = params.stream().map(v -> cs.submit(() -> this.logic(v))).collect(Collectors.toList()); + + Vector vs = new Vector<>(); + CompletableFuture future = CompletableFuture.runAsync(() -> { + for (int i = 0; i < max; i++) { + Future take; + try { + take = cs.take(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + Long result; + try { + result = take.get(); + vs.add(result); + } catch (InterruptedException | ExecutionException e) { + throw new RuntimeException(e); + } + log.info("{}", result); + } + }); + log.info("wait result"); + try { + future.get(800, TimeUnit.MILLISECONDS); + log.info("{}", vs); + } catch (InterruptedException | ExecutionException | TimeoutException e) { + log.error("timeout", e); + log.warn("part: {}", vs); + for (Future longFuture : futures) { + longFuture.cancel(true); + } + } + } + private Long logic(String param) { if (Objects.isNull(param)) { return 0L; } try { - Thread.sleep(150); + Thread.sleep(350); } catch (InterruptedException e) { - e.printStackTrace(); + log.error("", e); } return (long) param.length(); } From 87a46d4ea44304b814826d244c008cca2a1c40c7 Mon Sep 17 00:00:00 2001 From: kcp Date: Wed, 3 Aug 2022 19:30:30 +0800 Subject: [PATCH 263/476] mod: Resilience4j --- concurrency/build.gradle | 8 ++++ .../test/java/resilience4j/BreakerTest.java | 46 +++++++++++++++++++ dependency.gradle | 33 ++++++------- 3 files changed, 71 insertions(+), 16 deletions(-) create mode 100644 concurrency/src/test/java/resilience4j/BreakerTest.java diff --git a/concurrency/build.gradle b/concurrency/build.gradle index fc4ac4eb..9aade840 100644 --- a/concurrency/build.gradle +++ b/concurrency/build.gradle @@ -17,5 +17,13 @@ dependencies { implementation libs['jedis'] implementation libs['groovy'] implementation libs['common-lang'] + + implementation "io.github.resilience4j:resilience4j-circuitbreaker:$ver.resilience4j" + implementation "io.github.resilience4j:resilience4j-ratelimiter:$ver.resilience4j" + implementation "io.github.resilience4j:resilience4j-retry:$ver.resilience4j" + implementation "io.github.resilience4j:resilience4j-bulkhead:$ver.resilience4j" + implementation "io.github.resilience4j:resilience4j-cache:$ver.resilience4j" + implementation "io.github.resilience4j:resilience4j-timelimiter:$ver.resilience4j" + testImplementation(libs['testng']) } \ No newline at end of file diff --git a/concurrency/src/test/java/resilience4j/BreakerTest.java b/concurrency/src/test/java/resilience4j/BreakerTest.java new file mode 100644 index 00000000..5fa0c37b --- /dev/null +++ b/concurrency/src/test/java/resilience4j/BreakerTest.java @@ -0,0 +1,46 @@ +package resilience4j; + + +import io.github.resilience4j.ratelimiter.RateLimiter; +import io.github.resilience4j.ratelimiter.RateLimiterConfig; +import io.vavr.control.Try; +import org.testng.annotations.Test; + +import java.time.Duration; +import java.util.function.Supplier; + + +/** + * @author KuangChengPing@51peiq.com on 2022-08-03 19:20 + */ +public class BreakerTest { + + private String doAction() { + return "true"; + } + + @Test + public void testBreaker() { + +// Create a custom RateLimiter configuration + RateLimiterConfig config = RateLimiterConfig.custom() + .timeoutDuration(Duration.ofMillis(100)) + .limitRefreshPeriod(Duration.ofSeconds(1)) + .limitForPeriod(1) + .build(); +// Create a RateLimiter + RateLimiter rateLimiter = RateLimiter.of("backendName", config); + +// Decorate your call to BackendService.doSomething() + Supplier restrictedSupplier = RateLimiter + .decorateSupplier(rateLimiter, this::doAction); + +// First call is successful + Try firstTry = Try.ofSupplier(restrictedSupplier); + assert firstTry.isSuccess(); +// Second call fails, because the call was not permitted +// Try secondTry = Try.of(restrictedSupplier); +// assert secondTry.isFailure(); +// assertThat(secondTry.getCause()).isInstanceOf(RequestNotPermitted.class); + } +} diff --git a/dependency.gradle b/dependency.gradle index 7b31cffb..9b3b6861 100644 --- a/dependency.gradle +++ b/dependency.gradle @@ -3,22 +3,23 @@ ext { ver = [ - logback : '1.2.5' - , jedis : '2.9.0' - , junit : '4.13.2' - , groovy : '3.0.8' - , mail : '1.4.7' - , jackson : '2.9.5' - , hamcrest: '1.3' - , kcp_tool: '1.0.8' - , netty : '4.1.67.Final' - , guava : '30.1.1-jre' - , jmh : '1.33' - , kafka : '2.3.1' - , spring : '5.1.20.RELEASE' - , aspectj : '1.9.0' - , hadoop : '2.7.2' - , flink : '1.8.0' + logback : '1.2.5' + , jedis : '2.9.0' + , junit : '4.13.2' + , groovy : '3.0.8' + , mail : '1.4.7' + , jackson : '2.9.5' + , hamcrest : '1.3' + , kcp_tool : '1.0.8' + , netty : '4.1.67.Final' + , guava : '30.1.1-jre' + , jmh : '1.33' + , kafka : '2.3.1' + , spring : '5.1.20.RELEASE' + , aspectj : '1.9.0' + , hadoop : '2.7.2' + , flink : '1.8.0' + , resilience4j: '1.7.0' ] libs = [ // mine tool From aff64977205e5552cf9565d7b59a4127aa6c4237 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Sun, 21 Aug 2022 19:14:46 +0800 Subject: [PATCH 264/476] doc: --- .../main/java/situation/timoutpool/Readme.md | 3 ++ .../situation/timoutpool/TimeoutFuture.java | 28 ++++++++++++++++++- .../test/java/resilience4j/BreakerTest.java | 2 +- .../situation/timoutpool/TimeoutPoolTest.java | 1 - .../commoncode/CommonRelationTest.java | 6 ++-- 5 files changed, 34 insertions(+), 6 deletions(-) diff --git a/concurrency/src/main/java/situation/timoutpool/Readme.md b/concurrency/src/main/java/situation/timoutpool/Readme.md index 3358d242..298555ff 100644 --- a/concurrency/src/main/java/situation/timoutpool/Readme.md +++ b/concurrency/src/main/java/situation/timoutpool/Readme.md @@ -1 +1,4 @@ +## 并发执行任务,丢弃超时的任务 + 对比临时线程池和 CompleteFuture 实现方案 + diff --git a/concurrency/src/main/java/situation/timoutpool/TimeoutFuture.java b/concurrency/src/main/java/situation/timoutpool/TimeoutFuture.java index f2706f0c..a5b0f977 100644 --- a/concurrency/src/main/java/situation/timoutpool/TimeoutFuture.java +++ b/concurrency/src/main/java/situation/timoutpool/TimeoutFuture.java @@ -1,19 +1,45 @@ package situation.timoutpool; +import lombok.extern.slf4j.Slf4j; import situation.timoutpool.base.Param; import situation.timoutpool.base.Result; import situation.timoutpool.base.TaskExecutor; -import java.time.Duration; +import java.util.ArrayList; +import java.util.Collections; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; /** * @author https://github.com/kuangcp on 2021-09-05 02:45 */ +@Slf4j public class TimeoutFuture implements TaskExecutor { @Override public Result execute(Param param, long timeout, TimeUnit timeUnit) { + final Result result = Result.builder().dataList(Collections.synchronizedList(new ArrayList<>())).build(); + final CompletableFuture voidCompletableFuture = CompletableFuture.allOf(); + for (int i = 0; i < param.getTotal(); i++) { + final Param tmpParam = Param.builder().start(i).build(); + voidCompletableFuture.thenRunAsync(() -> { + try { + TimeUnit.MILLISECONDS.sleep(ThreadLocalRandom.current().nextInt(600) + 200); + } catch (InterruptedException e) { + e.printStackTrace(); + } +// log.info("tmpParam={}", tmpParam); + result.getDataList().add(tmpParam.toString()); + }); + } + + try { + voidCompletableFuture.wait(timeout); + } catch (InterruptedException e) { + log.error("", e); + } + return null; } } diff --git a/concurrency/src/test/java/resilience4j/BreakerTest.java b/concurrency/src/test/java/resilience4j/BreakerTest.java index 5fa0c37b..ae86ab45 100644 --- a/concurrency/src/test/java/resilience4j/BreakerTest.java +++ b/concurrency/src/test/java/resilience4j/BreakerTest.java @@ -11,7 +11,7 @@ /** - * @author KuangChengPing@51peiq.com on 2022-08-03 19:20 + * @author https://github.com/kuangcp on 2022-08-03 19:20 */ public class BreakerTest { diff --git a/concurrency/src/test/java/situation/timoutpool/TimeoutPoolTest.java b/concurrency/src/test/java/situation/timoutpool/TimeoutPoolTest.java index 4bc3d349..f406b404 100644 --- a/concurrency/src/test/java/situation/timoutpool/TimeoutPoolTest.java +++ b/concurrency/src/test/java/situation/timoutpool/TimeoutPoolTest.java @@ -10,7 +10,6 @@ import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; -import static org.hamcrest.MatcherAssert.assertThat; /** * @author https://github.com/kuangcp on 2021-09-04 23:42 diff --git a/question/src/test/java/com/github/kuangcp/situation/commoncode/CommonRelationTest.java b/question/src/test/java/com/github/kuangcp/situation/commoncode/CommonRelationTest.java index d40d11aa..f9ff1779 100644 --- a/question/src/test/java/com/github/kuangcp/situation/commoncode/CommonRelationTest.java +++ b/question/src/test/java/com/github/kuangcp/situation/commoncode/CommonRelationTest.java @@ -26,7 +26,7 @@ import java.util.stream.Stream; /** - * @author KuangChengPing@51peiq.com on 2022-06-30 10:07 + * @author https://github.com/kuangcp on 2022-06-30 10:07 */ @Slf4j public class CommonRelationTest { @@ -81,7 +81,7 @@ public void testMergeCodeMap() throws IOException, InterruptedException { for (int i = 0; i < 100; i++) { Map> result = loopCodeMap(parts); log.info("finish {}", i); -// TimeUnit.SECONDS.sleep(5); + TimeUnit.SECONDS.sleep(5); } TimeUnit.SECONDS.sleep(100000); @@ -166,7 +166,7 @@ private List initData() { } private List readData() throws IOException { - Path path = Paths.get("b.json"); + Path path = Paths.get("30w.json"); byte[] bytes = Files.readAllBytes(path); Gson gson = new Gson(); Type type = new TypeToken>() { From 2542735d4fd209eb7cb7815b5575c213b74b0c6f Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Sun, 21 Aug 2022 20:09:15 +0800 Subject: [PATCH 265/476] app: gomoku --- .../java/com/github/kuangcp/gomoku/AI.java | 229 ++++++++++++++++++ .../com/github/kuangcp/gomoku/CheckWin.java | 75 ++++++ .../com/github/kuangcp/gomoku/ChessBoard.java | 198 +++++++++++++++ .../com/github/kuangcp/gomoku/Gomoku.java | 31 +++ 4 files changed, 533 insertions(+) create mode 100644 gui/src/main/java/com/github/kuangcp/gomoku/AI.java create mode 100644 gui/src/main/java/com/github/kuangcp/gomoku/CheckWin.java create mode 100644 gui/src/main/java/com/github/kuangcp/gomoku/ChessBoard.java create mode 100644 gui/src/main/java/com/github/kuangcp/gomoku/Gomoku.java diff --git a/gui/src/main/java/com/github/kuangcp/gomoku/AI.java b/gui/src/main/java/com/github/kuangcp/gomoku/AI.java new file mode 100644 index 00000000..27cec85c --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/gomoku/AI.java @@ -0,0 +1,229 @@ +// Decompiled by Jad v1.5.8e2. Copyright 2001 Pavel Kouznetsov. +// Jad home page: http://kpdus.tripod.com/jad.html +// Decompiler options: packimports(3) fieldsfirst ansi space +// Source File Name: AI.java + +package com.github.kuangcp.gomoku; + +import java.awt.*; +import java.util.Random; + +public class AI { + + private Color chess[][]; + private Color myColor; + private Color antiColor; + private int bestX; + private int bestY; + private int effectiveX1; + private int effectiveY1; + private int effectiveX2; + private int effectiveY2; + private Color colorArr[]; + private final Random rand; + + public AI() { + colorArr = (new Color[]{ + Color.black, Color.white + }); + rand = new Random(); + } + + public boolean setAI(Color chess[][], Color myColor) { + this.chess = chess; + this.myColor = myColor; + antiColor = myColor != Color.black ? Color.black : Color.white; + findEffectiveArea(); + findBestPosition(); + return true; + } + + public int getAIX() { + return bestX; + } + + public int getAIY() { + return bestY; + } + + private void findEffectiveArea() { + for (int i = 0; i <= 14; i++) { + for (int j = 0; j <= 14; j++) + if (chess[i][j] != null) { + effectiveX1 = i; + effectiveY1 = j; + effectiveX2 = i; + effectiveY2 = j; + } + + } + + for (int i = 0; i <= 14; i++) { + for (int j = 0; j <= 14; j++) + if (chess[i][j] != null) { + effectiveX1 = Math.min(effectiveX1, i); + effectiveY1 = Math.min(effectiveY1, j); + effectiveX2 = Math.max(effectiveX2, i); + effectiveY2 = Math.max(effectiveY2, j); + } + + } + + int offset = 2; + effectiveX1 = effectiveX1 - offset < 0 ? effectiveX1 : effectiveX1 - offset; + effectiveY1 = effectiveY1 - offset < 0 ? effectiveY1 : effectiveY1 - offset; + effectiveX2 = effectiveX2 + offset > 14 ? effectiveX2 : effectiveX2 + offset; + effectiveY2 = effectiveY2 + offset > 14 ? effectiveY2 : effectiveY2 + offset; + } + + private void findBestPosition() { + int tempScore = 0; + bestX = effectiveX1; + bestY = effectiveY1; + for (int x = effectiveX1; x <= effectiveX2; x++) { + for (int y = effectiveY1; y <= effectiveY2; y++) + if (chess[x][y] == null && tempScore < getScore(x, y)) { + bestX = x; + bestY = y; + tempScore = getScore(x, y); + } + + } + + if (tempScore == 0) { + bestX = effectiveX1 + rand.nextInt((effectiveX2 + 1) - effectiveX1); + bestY = effectiveY1 + rand.nextInt((effectiveY2 + 1) - effectiveY1); + } + } + + private int contains(int x, int y, Color color) { + if (x < 0 || x > 14 || y < 0 || y > 14) + return -1; + if (chess[x][y] == color) + return 1; + return chess[x][y] != null ? -1 : 0; + } + + private int getScore(int x, int y) { + int tempX = x; + int tempY = y; + int count = 0; + int block = 0; + int totalScore = 0; + Color acolor[]; + int j = (acolor = colorArr).length; + for (int i = 0; i < j; i++) { + Color color = acolor[i]; + x = tempX; + y = tempY; + count = 0; + block = 0; + while (contains(x - 1, y, color) == 1) { + x--; + count++; + } + if (contains(x - 1, y, color) == -1) + block++; + x = tempX; + for (y = tempY; contains(x + 1, y, color) == 1; ) { + x++; + count++; + } + + if (contains(x + 1, y, color) == -1) + block++; + count++; + totalScore += countToScore(count, block, color); + x = tempX; + y = tempY; + count = 0; + block = 0; + while (contains(x, y - 1, color) == 1) { + y--; + count++; + } + if (contains(x, y - 1, color) == -1) + block++; + x = tempX; + for (y = tempY; contains(x, y + 1, color) == 1; ) { + y++; + count++; + } + + if (contains(x, y + 1, color) == -1) + block++; + count++; + totalScore += countToScore(count, block, color); + x = tempX; + y = tempY; + count = 0; + block = 0; + while (contains(x - 1, y - 1, color) == 1) { + x--; + y--; + count++; + } + if (contains(x - 1, y - 1, color) == -1) + block++; + x = tempX; + for (y = tempY; contains(x + 1, y + 1, color) == 1; ) { + x++; + y++; + count++; + } + + if (contains(x + 1, y + 1, color) == -1) + block++; + count++; + totalScore += countToScore(count, block, color); + x = tempX; + y = tempY; + count = 0; + block = 0; + while (contains(x - 1, y + 1, color) == 1) { + x--; + y++; + count++; + } + if (contains(x - 1, y + 1, color) == -1) + block++; + x = tempX; + for (y = tempY; contains(x + 1, y - 1, color) == 1; ) { + x++; + y--; + count++; + } + + if (contains(x + 1, y - 1, color) == -1) + block++; + count++; + totalScore += countToScore(count, block, color); + } + + return totalScore; + } + + private int countToScore(int count, int block, Color color) { + if (block == 2) + return 0; + if (count == 5 && color == myColor) + return 0x3b9aca00; + if (count == 5 && color == antiColor) + return 0x5f5e100; + if (count == 4 && color == myColor && block == 0) + return 0x989680; + if (count == 4 && color == myColor && block == 1) + return 0xf4240; + if (count == 4 && color == antiColor && block == 0) + return 0x186a0; + if (count == 3 && color == myColor && block == 0) + return 10000; + if (count == 4 && color == antiColor && block == 1) + return 1000; + if (count == 3 && color == myColor && block == 1) + return 100; + if (count == 2 && color == myColor && block == 0) + return 10; + return count != 2 || color != myColor || block != 1 ? 0 : 1; + } +} diff --git a/gui/src/main/java/com/github/kuangcp/gomoku/CheckWin.java b/gui/src/main/java/com/github/kuangcp/gomoku/CheckWin.java new file mode 100644 index 00000000..d5839571 --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/gomoku/CheckWin.java @@ -0,0 +1,75 @@ +// Decompiled by Jad v1.5.8e2. Copyright 2001 Pavel Kouznetsov. +// Jad home page: http://kpdus.tripod.com/jad.html +// Decompiler options: packimports(3) fieldsfirst ansi space +// Source File Name: CheckWin.java + +package com.github.kuangcp.gomoku; + +import java.awt.*; + +public class CheckWin { + + public CheckWin() { + } + + public static boolean check(Color chess[][], int x, int y, Color color) { + int count = 0; + int tempX = x; + int tempY = y; + for (; contains(chess, x - 1, y, color); x--) + count++; + + x = tempX; + for (y = tempY; contains(chess, x + 1, y, color); x++) + count++; + + if (count + 1 >= 5) + return true; + count = 0; + x = tempX; + for (y = tempY; contains(chess, x, y - 1, color); y--) + count++; + + x = tempX; + for (y = tempY; contains(chess, x, y + 1, color); y++) + count++; + + if (count + 1 >= 5) + return true; + count = 0; + x = tempX; + for (y = tempY; contains(chess, x - 1, y - 1, color); y--) { + count++; + x--; + } + + x = tempX; + for (y = tempY; contains(chess, x + 1, y + 1, color); y++) { + count++; + x++; + } + + if (count + 1 == 5) + return true; + count = 0; + x = tempX; + for (y = tempY; contains(chess, x - 1, y + 1, color); y++) { + count++; + x--; + } + + x = tempX; + for (y = tempY; contains(chess, x + 1, y - 1, color); y--) { + count++; + x++; + } + + return count + 1 == 5; + } + + private static boolean contains(Color chess[][], int x, int y, Color color) { + if (x < 0 || x > 14 || y < 0 || y > 14) + return false; + return chess[x][y] == color; + } +} diff --git a/gui/src/main/java/com/github/kuangcp/gomoku/ChessBoard.java b/gui/src/main/java/com/github/kuangcp/gomoku/ChessBoard.java new file mode 100644 index 00000000..cb71ff3b --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/gomoku/ChessBoard.java @@ -0,0 +1,198 @@ +// Decompiled by Jad v1.5.8e2. Copyright 2001 Pavel Kouznetsov. +// Jad home page: http://kpdus.tripod.com/jad.html +// Decompiler options: packimports(3) fieldsfirst ansi space +// Source File Name: ChessBoard.java + +package com.github.kuangcp.gomoku; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.event.MouseMotionListener; +import java.util.Arrays; + +/** + * 图标 消息 输入值 选项按钮 + * 整个棋盘 + * + * @author lenovo + */ +public class ChessBoard extends JPanel + implements MouseListener, MouseMotionListener { + + private final int SPACE = 44; + private final int MARGIN = 44; + private final int WIDTH = 704; + private final int HEIGHT = 704; + private final Color BACKGROUND_COLOR; + private Color chess[][]; + private int gridWithMouseX; + private int gridWithMouseY; + private int lastX; + private int lastY; + private String first; + private Color playerColor; + private Color computerColor; + private boolean playerPlay; + private boolean computerPlay; + private final AI ai; + + public ChessBoard() { + BACKGROUND_COLOR = Color.orange; + chess = new Color[15][15]; + gridWithMouseX = -1; + gridWithMouseY = -1; + first = "Player"; + ai = new AI(); + setPreferredSize(new Dimension(704, 704));//设置窗体组件大小 + setBackground(BACKGROUND_COLOR); + addMouseMotionListener(this); + addMouseListener(this); + newGame(); + } + + private void newGame() { + for (int i = 0; i < 15; i++) + Arrays.fill(chess[i], null);//将颜色数组置空 + + repaint(); + //先行者是黑棋 + if (first.equals("Player")) { + playerPlay = true; + playerColor = Color.black; + computerColor = Color.white; + } else if (first.equals("Computer")) { + computerPlay = true; + computerColor = Color.black; + playerColor = Color.white; + computerPlayChess(); + } + } + + private void computerPlayChess() { + boolean isAIdone = ai.setAI(chess, computerColor); + int x = ai.getAIX(); + int y = ai.getAIY(); + chess[x][y] = computerColor; + repaint(); + if (CheckWin.check(chess, x, y, computerColor)) { + Win(computerColor); + } else { + computerPlay = false; + playerPlay = true; + } + } + + private void Win(Color color) { + JOptionPane.showMessageDialog(this, (new StringBuilder(String.valueOf(color != playerColor ? "电脑" : "你"))).append("赢了!").toString()); + newGame(); + } + + private boolean hasChess(int x, int y) { + return chess[x][y] == null; + } + + @Override + protected void paintComponent(Graphics g) { + int offset = 4; + int r = 5; + int chessR = 20; + super.paintComponent(g); + if (gridWithMouseX > 0 && gridWithMouseY > 0 && playerPlay) { + lastX = gridWithMouseX; + lastY = gridWithMouseY; + g.setColor(Color.red); + g.drawRect(lastX, lastY, 44, 44); + g.setColor(BACKGROUND_COLOR); + g.drawLine(lastX + 11, lastY, lastX + 33, lastY); + g.drawLine(lastX + 11, lastY + 44, lastX + 33, lastY + 44); + g.drawLine(lastX, lastY + 11, lastX, lastY + 33); + g.drawLine(lastX + 44, lastY + 11, lastX + 44, lastY + 33); + } else { + g.setColor(BACKGROUND_COLOR); + g.drawRect(lastX, lastY, 44, 44); + } + g.setColor(Color.black); + for (int i = 0; i <= offset; i++) + g.drawRect(44 - i, 44 - i, 616 + 2 * i, 616 + 2 * i); + + for (int i = 2; i <= 14; i++) { + g.drawLine(44 + (i - 1) * 44, 44, 44 + (i - 1) * 44, 660); + g.drawLine(44, 44 + (i - 1) * 44, 660, 44 + (i - 1) * 44); + } + + for (int i = 3; i <= 11; i += 4) { + for (int j = 3; j <= 11; j += 4) + g.fillOval(getXY(i) - r, getXY(j) - r, 2 * r, 2 * r); + + } + + for (int i = 0; i < 15; i++) { + for (int j = 0; j < 15; j++) + if (chess[i][j] != null) { + g.setColor(chess[i][j]); + g.fillOval(getXY(i) - chessR, getXY(j) - chessR, chessR * 2, chessR * 2); + } + + } + + } + + private int getXY(int xy) { + return 44 + xy * 44; + } + + private int getGridXY(int xy) { + return (xy - 22) / 44; + } + + public void mouseMoved(MouseEvent e) { + int x = getGridXY(e.getX()); + int y = getGridXY(e.getY()); + x = x >= 0 ? x : 0; + x = x <= 14 ? x : 14; + y = y >= 0 ? y : 0; + y = y <= 14 ? y : 14; + if (!hasChess(x, y) || e.getX() >= 682 || e.getX() <= 22 || e.getY() >= 682 || e.getY() <= 22) { + gridWithMouseX = -1; + gridWithMouseY = -1; + } else { + gridWithMouseX = getXY(x) - 22; + gridWithMouseY = getXY(y) - 22; + } + repaint(); + } + + public void mouseClicked(MouseEvent e) { + if (playerPlay) { + int x = getGridXY(e.getX()); + int y = getGridXY(e.getY()); + if (hasChess(x, y)) + chess[x][y] = playerColor; + repaint(); + if (CheckWin.check(chess, x, y, playerColor)) { + Win(playerColor); + } else { + playerPlay = false; + computerPlay = true; + computerPlayChess(); + } + } + } + + public void mouseDragged(MouseEvent mouseevent) { + } + + public void mouseEntered(MouseEvent mouseevent) { + } + + public void mouseExited(MouseEvent mouseevent) { + } + + public void mousePressed(MouseEvent mouseevent) { + } + + public void mouseReleased(MouseEvent mouseevent) { + } +} diff --git a/gui/src/main/java/com/github/kuangcp/gomoku/Gomoku.java b/gui/src/main/java/com/github/kuangcp/gomoku/Gomoku.java new file mode 100644 index 00000000..906f7db2 --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/gomoku/Gomoku.java @@ -0,0 +1,31 @@ +// Decompiled by Jad v1.5.8e2. Copyright 2001 Pavel Kouznetsov. +// Jad home page: http://kpdus.tripod.com/jad.html +// Decompiler options: packimports(3) fieldsfirst ansi space +// Source File Name: GameFrame.java + +package com.github.kuangcp.gomoku; + +import javax.swing.*; +import java.awt.*; + +public class Gomoku extends JFrame { + + public Gomoku() { + super("欢乐五子棋"); + ChessBoard chessBoard = new ChessBoard(); + add(chessBoard); + setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);//后台推出 + pack();//自动调整为最佳窗口大小展示 + int screenSizeX = (int) Toolkit.getDefaultToolkit().getScreenSize().getWidth(); + int screenSizeY = (int) Toolkit.getDefaultToolkit().getScreenSize().getHeight(); + int fSizeX = (int) getSize().getWidth(); + int fSizeY = (int) getSize().getHeight(); + setResizable(false); + setBounds((screenSizeX - fSizeX) / 2, (screenSizeY - fSizeY) / 2, fSizeX, fSizeY); + setVisible(true); + } + + public static void main(String[] args) { + new Gomoku(); + } +} From 0d716d49fd61ad5472e5de2027d0a3a1b23c45d0 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Thu, 1 Sep 2022 23:33:01 +0800 Subject: [PATCH 266/476] build --- gui/build.gradle | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/gui/build.gradle b/gui/build.gradle index 5d558374..55be1eb7 100644 --- a/gui/build.gradle +++ b/gui/build.gradle @@ -43,3 +43,9 @@ tasks.register('calc') { application.mainClass = 'com.github.kuangcp.caculator.Calculator' println "Build Calculator ..." } + +tasks.register('gomoku') { + dependsOn build + application.mainClass = 'com.github.kuangcp.gomoku.Gomoku' + println "Build Gomoku ..." +} \ No newline at end of file From 2205d3d0c89648ed137afcfb907f871f7d3d72b8 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Fri, 2 Sep 2022 00:34:46 +0800 Subject: [PATCH 267/476] rm: other mod --- flink/bootstrap.sh | 35 ----- flink/build.gradle | 28 ---- flink/host.conf | 2 - flink/run.sh | 5 - .../github/kuangcp/hi/SimpleJobListener.java | 28 ---- .../com/github/kuangcp/hi/SimpleSink.java | 53 ------- .../com/github/kuangcp/hi/SimpleSource.java | 52 ------- .../github/kuangcp/hi/SimpleStatistic.java | 87 ----------- flink/src/main/resources/log4j.properties | 9 -- flink/upload.sh | 6 - hadoop/build.gradle | 12 -- .../hdfs/hi/GeneralFileActionDemo.java | 133 ----------------- .../hdfs/hi/GeneralFileActionDemoTest.java | 56 ------- kafka/build.gradle | 7 - .../java/com/github/kuangcp/hi/Constants.java | 17 --- .../com/github/kuangcp/hi/ConsumerDemo.java | 74 ---------- .../com/github/kuangcp/hi/ProducerDemo.java | 133 ----------------- .../hi/domain/ProductStatisticJobCommand.java | 41 ------ .../hi/domain/ProductStatisticSpan.java | 62 -------- .../kuangcp/hi/domain/StartCommand.java | 20 --- .../kuangcp/hi/domain/StatisticSpan.java | 25 ---- kafka/src/main/resources/kafka.properties | 2 - .../github/kuangcp/hi/ConsumerDemoTest.java | 22 --- .../github/kuangcp/hi/ProducerDemoTest.java | 25 ---- kafka/src/test/resources/logback-test.xml | 21 --- mybatis/build.gradle | 21 --- mybatis/create.sql | 16 -- .../java/com/github/kuangcp/Application.java | 14 -- .../kuangcp/sharding/manual/AuthUtil.java | 24 --- .../manual/ConsistentHashingAlgorithm.java | 100 ------------- .../manual/ShardingAlgorithmEnum.java | 46 ------ .../manual/ShardingAlgorithmType.java | 9 -- .../sharding/manual/ShardingTable.java | 16 -- .../manual/ShardingTableInterceptor.java | 137 ------------------ .../simple/customer/dao/CustomerDao.java | 25 ---- .../simple/customer/domain/Customer.java | 22 --- .../kuangcp/simple/order/dao/OrderDao.java | 10 -- .../kuangcp/simple/order/domain/Order.java | 28 ---- .../kuangcp/simple/order/dto/OrderDTO.java | 24 --- .../simple/order/service/OrderService.java | 11 -- .../order/service/impl/OrderServiceImpl.java | 86 ----------- .../com/github/kuangcp/stream/Report.java | 36 ----- .../github/kuangcp/stream/dao/ReportDao.java | 42 ------ mybatis/src/main/resources/application.yml | 6 - mybatis/src/main/resources/logback.xml | 79 ---------- .../kuangcp/base/SpringBootTestStarter.java | 11 -- .../ConsistentHashingAlgorithmTest.java | 57 -------- .../dao/CustomerDaoSpringBootTest.java | 45 ------ .../order/dao/OrderDaoSpringBootTest.java | 65 --------- .../service/OrderServiceSpringBootTest.java | 23 --- .../stream/CursorSessionSpringBootTest.java | 118 --------------- .../java/com/github/kuangcp/stream/Readme.md | 11 -- 52 files changed, 2037 deletions(-) delete mode 100755 flink/bootstrap.sh delete mode 100644 flink/build.gradle delete mode 100644 flink/host.conf delete mode 100755 flink/run.sh delete mode 100644 flink/src/main/java/com/github/kuangcp/hi/SimpleJobListener.java delete mode 100644 flink/src/main/java/com/github/kuangcp/hi/SimpleSink.java delete mode 100644 flink/src/main/java/com/github/kuangcp/hi/SimpleSource.java delete mode 100644 flink/src/main/java/com/github/kuangcp/hi/SimpleStatistic.java delete mode 100644 flink/src/main/resources/log4j.properties delete mode 100755 flink/upload.sh delete mode 100644 hadoop/build.gradle delete mode 100644 hadoop/src/main/java/com/github/kuangcp/hdfs/hi/GeneralFileActionDemo.java delete mode 100644 hadoop/src/test/java/com/github/kuangcp/hdfs/hi/GeneralFileActionDemoTest.java delete mode 100644 kafka/build.gradle delete mode 100644 kafka/src/main/java/com/github/kuangcp/hi/Constants.java delete mode 100644 kafka/src/main/java/com/github/kuangcp/hi/ConsumerDemo.java delete mode 100644 kafka/src/main/java/com/github/kuangcp/hi/ProducerDemo.java delete mode 100644 kafka/src/main/java/com/github/kuangcp/hi/domain/ProductStatisticJobCommand.java delete mode 100644 kafka/src/main/java/com/github/kuangcp/hi/domain/ProductStatisticSpan.java delete mode 100644 kafka/src/main/java/com/github/kuangcp/hi/domain/StartCommand.java delete mode 100644 kafka/src/main/java/com/github/kuangcp/hi/domain/StatisticSpan.java delete mode 100644 kafka/src/main/resources/kafka.properties delete mode 100644 kafka/src/test/java/com/github/kuangcp/hi/ConsumerDemoTest.java delete mode 100644 kafka/src/test/java/com/github/kuangcp/hi/ProducerDemoTest.java delete mode 100644 kafka/src/test/resources/logback-test.xml delete mode 100644 mybatis/build.gradle delete mode 100644 mybatis/create.sql delete mode 100644 mybatis/src/main/java/com/github/kuangcp/Application.java delete mode 100644 mybatis/src/main/java/com/github/kuangcp/sharding/manual/AuthUtil.java delete mode 100644 mybatis/src/main/java/com/github/kuangcp/sharding/manual/ConsistentHashingAlgorithm.java delete mode 100644 mybatis/src/main/java/com/github/kuangcp/sharding/manual/ShardingAlgorithmEnum.java delete mode 100644 mybatis/src/main/java/com/github/kuangcp/sharding/manual/ShardingAlgorithmType.java delete mode 100644 mybatis/src/main/java/com/github/kuangcp/sharding/manual/ShardingTable.java delete mode 100644 mybatis/src/main/java/com/github/kuangcp/sharding/manual/ShardingTableInterceptor.java delete mode 100644 mybatis/src/main/java/com/github/kuangcp/simple/customer/dao/CustomerDao.java delete mode 100644 mybatis/src/main/java/com/github/kuangcp/simple/customer/domain/Customer.java delete mode 100644 mybatis/src/main/java/com/github/kuangcp/simple/order/dao/OrderDao.java delete mode 100644 mybatis/src/main/java/com/github/kuangcp/simple/order/domain/Order.java delete mode 100644 mybatis/src/main/java/com/github/kuangcp/simple/order/dto/OrderDTO.java delete mode 100644 mybatis/src/main/java/com/github/kuangcp/simple/order/service/OrderService.java delete mode 100644 mybatis/src/main/java/com/github/kuangcp/simple/order/service/impl/OrderServiceImpl.java delete mode 100644 mybatis/src/main/java/com/github/kuangcp/stream/Report.java delete mode 100644 mybatis/src/main/java/com/github/kuangcp/stream/dao/ReportDao.java delete mode 100644 mybatis/src/main/resources/application.yml delete mode 100644 mybatis/src/main/resources/logback.xml delete mode 100644 mybatis/src/test/java/com/github/kuangcp/base/SpringBootTestStarter.java delete mode 100644 mybatis/src/test/java/com/github/kuangcp/sharding/manual/ConsistentHashingAlgorithmTest.java delete mode 100644 mybatis/src/test/java/com/github/kuangcp/simple/customer/dao/CustomerDaoSpringBootTest.java delete mode 100644 mybatis/src/test/java/com/github/kuangcp/simple/order/dao/OrderDaoSpringBootTest.java delete mode 100644 mybatis/src/test/java/com/github/kuangcp/simple/order/service/OrderServiceSpringBootTest.java delete mode 100644 mybatis/src/test/java/com/github/kuangcp/stream/CursorSessionSpringBootTest.java delete mode 100644 mybatis/src/test/java/com/github/kuangcp/stream/Readme.md diff --git a/flink/bootstrap.sh b/flink/bootstrap.sh deleted file mode 100755 index 80c6d711..00000000 --- a/flink/bootstrap.sh +++ /dev/null @@ -1,35 +0,0 @@ -. ./host.conf -result=$(./upload.sh) - -temp=${result#*flink-web-upload/} -jarId=${temp%%\"*} - -echo "\n start run: " $jarId "\n" - -# 当 job 执行完成后, get请求 才会返回 -run(){ - jobResult=$(./run.sh $1) - - echo $jobResult - temp=${jobResult#*:\"} - jobId=${temp%%\"\}*} - - echo $(date) "jobId: " $jobId -} - -# 当 job 在执行的时候, 并不会在 jobs/ -jobs(){ - host=$1 - for i in $(seq 10); do - sleep 1 - echo "\n start curl " $host/jobs/ "\n" - - curl $host/jobs - - echo "" - done; -} - -jobs $host & - -run $jarId \ No newline at end of file diff --git a/flink/build.gradle b/flink/build.gradle deleted file mode 100644 index 114821ab..00000000 --- a/flink/build.gradle +++ /dev/null @@ -1,28 +0,0 @@ -version = '1.0.0-SNAPSHOT' - -apply plugin: 'java' -apply plugin: 'application' - -dependencies { - implementation project(":common-config") - implementation libs['flink-java'] - implementation libs['flink-clients'] - implementation libs['flink-streaming-java'] -} - -application { - mainClassName = "com.github.kuangcp.hi.SimpleStatistic" -} - -task uberJar(type: Jar) { - archiveClassifier = 'all-dependency' - - from sourceSets.main.output - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith('jar') }.collect { zipTree(it) } - } - manifest { - attributes 'Main-Class': 'com.github.kuangcp.hi.SimpleStatistic' - } -} diff --git a/flink/host.conf b/flink/host.conf deleted file mode 100644 index b80c9610..00000000 --- a/flink/host.conf +++ /dev/null @@ -1,2 +0,0 @@ -host=http://127.0.0.1:8081 -# host=http://127.0.0.1:8081 diff --git a/flink/run.sh b/flink/run.sh deleted file mode 100755 index 369101ea..00000000 --- a/flink/run.sh +++ /dev/null @@ -1,5 +0,0 @@ -. ./host.conf -jarId=$1 - -curl -X POST $host/jars/$jarId/run\?entry-class\=com.github.kuangcp.hi.SimpleStatistic - diff --git a/flink/src/main/java/com/github/kuangcp/hi/SimpleJobListener.java b/flink/src/main/java/com/github/kuangcp/hi/SimpleJobListener.java deleted file mode 100644 index 2e3ad108..00000000 --- a/flink/src/main/java/com/github/kuangcp/hi/SimpleJobListener.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.github.kuangcp.hi; - -import static org.apache.flink.runtime.jobgraph.JobStatus.FINISHED; - -import org.apache.flink.api.common.JobID; -import org.apache.flink.runtime.executiongraph.ExecutionGraph; -import org.apache.flink.runtime.executiongraph.JobStatusListener; -import org.apache.flink.runtime.jobgraph.JobStatus; - -/** - * 如果该对象能注入到 下面类的属性上, 问题就简单一些了 - * - * @author https://github.com/kuangcp on 2019-07-08 11:22 - * @see ExecutionGraph jobStatusListeners - */ -@Deprecated -public class SimpleJobListener implements JobStatusListener { - - @Override - public void jobStatusChanges(JobID jobId, JobStatus newJobStatus, long timestamp, - Throwable error) { - System.out.println(jobId); - - if (FINISHED.equals(newJobStatus)) { - System.out.println("complete"); - } - } -} diff --git a/flink/src/main/java/com/github/kuangcp/hi/SimpleSink.java b/flink/src/main/java/com/github/kuangcp/hi/SimpleSink.java deleted file mode 100644 index 58f3970a..00000000 --- a/flink/src/main/java/com/github/kuangcp/hi/SimpleSink.java +++ /dev/null @@ -1,53 +0,0 @@ -package com.github.kuangcp.hi; - -import java.io.IOException; -import java.util.LinkedList; -import java.util.List; -import lombok.EqualsAndHashCode; -import lombok.extern.slf4j.Slf4j; -import org.apache.flink.api.common.io.RichOutputFormat; -import org.apache.flink.api.java.tuple.Tuple2; -import org.apache.flink.configuration.Configuration; - -/** - * 模拟输出到文件 - * - * @author https://github.com/kuangcp - * @since 2019-05-30 20:36 - */ -@lombok.Data -@Slf4j -@EqualsAndHashCode(callSuper = true) -public class SimpleSink extends RichOutputFormat> { - - private String name; - private long createTime = System.currentTimeMillis(); - // 如果给属性加上 transient 修饰, 就会报错 因为无法同步数据 - private List> resultList = new LinkedList<>(); - - SimpleSink(String name) { - this.name = name; - } - - @Override - public void configure(Configuration parameters) { - } - - @Override - public void open(int taskNumber, int numTasks) throws IOException { - } - - @Override - public void writeRecord(Tuple2 record) throws IOException { - resultList.add(record); - } - - @Override - public void close() { - try { - resultList.forEach(t -> log.info("close by sink: name={} count={}", t.f0, t.f1)); - } catch (Exception e) { - log.error(e.getMessage(), e); - } - } -} diff --git a/flink/src/main/java/com/github/kuangcp/hi/SimpleSource.java b/flink/src/main/java/com/github/kuangcp/hi/SimpleSource.java deleted file mode 100644 index 23afafd2..00000000 --- a/flink/src/main/java/com/github/kuangcp/hi/SimpleSource.java +++ /dev/null @@ -1,52 +0,0 @@ -package com.github.kuangcp.hi; - -import java.time.Month; -import java.util.List; -import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; -import java.util.stream.IntStream; -import lombok.extern.slf4j.Slf4j; - -/** - * 模拟数据库分页查询 - * - * @author https://github.com/kuangcp 2019-06-16 15:12 - */ -@Slf4j -class SimpleSource { - - private String id; - - private static final int pageNum = 40; - private static final int pageSize = 500; - - private int cursor; - - SimpleSource(String id) { - this.id = id; - } - - boolean hasNextPage() { - return cursor < pageNum; - } - - List generateResource() { - delayTime(); - cursor++; - log.info("source: id={} cursor={}", id, cursor); - return IntStream.rangeClosed(1, pageSize) - .mapToObj(i -> Month.of((i + cursor) % 12 + 1).toString()) - .collect(Collectors.toList()); - } - - /** - * 延长Flink执行时间 - */ - private void delayTime() { - try { - TimeUnit.MILLISECONDS.sleep(300); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } -} diff --git a/flink/src/main/java/com/github/kuangcp/hi/SimpleStatistic.java b/flink/src/main/java/com/github/kuangcp/hi/SimpleStatistic.java deleted file mode 100644 index 29afd40f..00000000 --- a/flink/src/main/java/com/github/kuangcp/hi/SimpleStatistic.java +++ /dev/null @@ -1,87 +0,0 @@ -package com.github.kuangcp.hi; - -import java.util.List; -import java.util.Objects; - -import java.util.concurrent.TimeUnit; -import lombok.extern.slf4j.Slf4j; -import org.apache.flink.api.common.JobExecutionResult; -import org.apache.flink.api.common.functions.MapFunction; -import org.apache.flink.api.java.DataSet; -import org.apache.flink.api.java.ExecutionEnvironment; -import org.apache.flink.api.java.operators.AggregateOperator; -import org.apache.flink.api.java.operators.DataSource; -import org.apache.flink.api.java.tuple.Tuple2; - -/** - * Batch 的 调用是通过 REST API的, 只能在发起请求的地方依据返回值来判断是否执行完成和成功 - * - * @author https://github.com/kuangcp - * @since 2019-05-16 16:26 - */ -@Slf4j -public class SimpleStatistic { - - private static final ExecutionEnvironment ENV; - private static final int taskNum = 1; - - static { - ENV = ExecutionEnvironment.getExecutionEnvironment(); - } - - public static void main(String[] args) throws Exception { - log.info("start calculate"); - try { - for (int i = 0; i < taskNum; i++) { - calculateAndAggregate("batch-" + i); - } - } catch (Throwable e) { - log.error(e.getMessage(), e); - } - - ENV.execute("SimpleStatistic"); - } - - /** - * 分页计算并聚合 - */ - private static void calculateAndAggregate(String batchId) { - AggregateOperator> result = null; - - SimpleSource provider = new SimpleSource(batchId); - while (provider.hasNextPage()) { - List data = provider.generateResource(); - DataSource source = ENV.fromCollection(data); - - // 合并窗口内数据 - DataSet> counts = source - .filter(Objects::nonNull) - .map(new Mapper()) - .groupBy(0) - .sum(1); - - // 当前窗口内和历史数据合并 - if (Objects.isNull(result)) { - result = counts.groupBy(0).sum(1).name("cache"); - } else { - DataSet> temp = result.union(counts).name("union cache"); - result = temp.groupBy(0).sum(1).name("merge cache"); - } - } - - if (Objects.isNull(result)) { - return; - } - - result.output(new SimpleSink(batchId)); - } - - public static final class Mapper implements - MapFunction> { - - @Override - public Tuple2 map(String value) { - return new Tuple2<>(value, 1); - } - } -} diff --git a/flink/src/main/resources/log4j.properties b/flink/src/main/resources/log4j.properties deleted file mode 100644 index b4decd71..00000000 --- a/flink/src/main/resources/log4j.properties +++ /dev/null @@ -1,9 +0,0 @@ -log4j.rootLogger=INFO, stdout - -# Direct log messages to stdout -log4j.appender.stdout=org.apache.log4j.ConsoleAppender -log4j.appender.stdout.Target=System.out -log4j.appender.stdout.layout=org.apache.log4j.PatternLayout -log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} [%t] %-5p %c{1}:%L - %m%n - -log4j.logger.com.baturu.ofc.batch = debug diff --git a/flink/upload.sh b/flink/upload.sh deleted file mode 100755 index 4a320dd3..00000000 --- a/flink/upload.sh +++ /dev/null @@ -1,6 +0,0 @@ -. ./host.conf -file=$(find . -iname "*.jar*") -echo $file - -curl -X POST -H "Expect:" -F "jarfile=@$file" $host/jars/upload - diff --git a/hadoop/build.gradle b/hadoop/build.gradle deleted file mode 100644 index b20508c7..00000000 --- a/hadoop/build.gradle +++ /dev/null @@ -1,12 +0,0 @@ -dependencies { - implementation project(":common-config") - implementation (libs["hadoop-common"]) { - exclude group: 'org.slf4j' - } - implementation (libs["hadoop-client"]){ - exclude group: 'org.slf4j' - } - implementation (libs["hadoop-hdfs"]){ - exclude group: 'org.slf4j' - } -} \ No newline at end of file diff --git a/hadoop/src/main/java/com/github/kuangcp/hdfs/hi/GeneralFileActionDemo.java b/hadoop/src/main/java/com/github/kuangcp/hdfs/hi/GeneralFileActionDemo.java deleted file mode 100644 index fb2f85b5..00000000 --- a/hadoop/src/main/java/com/github/kuangcp/hdfs/hi/GeneralFileActionDemo.java +++ /dev/null @@ -1,133 +0,0 @@ -package com.github.kuangcp.hdfs.hi; - -import java.io.BufferedReader; -import java.io.BufferedWriter; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.OutputStreamWriter; -import java.net.URI; -import java.nio.charset.StandardCharsets; -import java.util.Arrays; -import java.util.List; -import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; -import lombok.extern.slf4j.Slf4j; -import org.apache.hadoop.conf.Configuration; -import org.apache.hadoop.fs.FSDataInputStream; -import org.apache.hadoop.fs.FSDataOutputStream; -import org.apache.hadoop.fs.FileStatus; -import org.apache.hadoop.fs.FileSystem; -import org.apache.hadoop.fs.Path; - -/** - * @author https://github.com/kuangcp - * @date 2019-05-20 11:15 - */ -@Slf4j -public class GeneralFileActionDemo { - - public static boolean createDirectory(String url) throws IOException { - FileSystem fileSystem = FileSystem.get(getConfig(url)); - boolean result = fileSystem.mkdirs(new Path(url)); - log.info("result={}", result); - return result; - } - - public static void createNewFile(String url, String content) - throws IOException, InterruptedException { - FileSystem fs = FileSystem.get(getConfig(url)); - - FSDataOutputStream os = fs.create(new Path(URI.create(url).getPath())); - os.write(content.getBytes(StandardCharsets.UTF_8)); - os.flush(); - TimeUnit.SECONDS.sleep(4); - os.close(); - fs.close(); - } - - public static List listFiles(String url) throws IOException { - FileSystem fs = FileSystem.get(URI.create(url), getConfig(url)); - Path path = new Path(url); - FileStatus[] status = fs.listStatus(path); - - return Arrays.stream(status).map(v -> v.getPath().toString()).collect(Collectors.toList()); - //方法1 -// for (FileStatus f : status) { -// log.info(f.getPath().toString()); -// } - - //方法2 -// Path[] listedPaths = FileUtil.stat2Paths(status); -// for (Path p : listedPaths) { -// System.out.println(p.toString()); -// } - } - - public static boolean deleteByURL(String url) throws IOException { - FileSystem fs = FileSystem.get(getConfig(url)); - Path path = new Path(url); - boolean isDeleted = fs.deleteOnExit(path); - fs.close(); - return isDeleted; - } - - public static void writeFileToHDFS() throws IOException { - Configuration configuration = new Configuration(); - configuration.set("fs.defaultFS", "hdfs://localhost:9000"); - FileSystem fileSystem = FileSystem.get(configuration); - //Create a path - String fileName = "read_write_hdfs_example.txt"; - Path hdfsWritePath = new Path("/user/javadeveloperzone/javareadwriteexample/" + fileName); - FSDataOutputStream fsDataOutputStream = fileSystem.create(hdfsWritePath, true); - BufferedWriter bufferedWriter = new BufferedWriter( - new OutputStreamWriter(fsDataOutputStream, StandardCharsets.UTF_8)); - bufferedWriter.write("Java API to write data in HDFS"); - bufferedWriter.newLine(); - bufferedWriter.close(); - fileSystem.close(); - } - - /** - * read the hdfs file content - * - * notice that the url is the full path name - */ - public static void readHDFSFile(String url) throws Exception { - FileSystem fs = FileSystem.get(getConfig(url)); - Path path = new Path(url); - - // check if the file exists - if (!fs.exists(path)) { - throw new Exception("the file is not found ."); - } - - FSDataInputStream is = fs.open(path); - // read line by line - BufferedReader bufferedReader = new BufferedReader( - new InputStreamReader(is, StandardCharsets.UTF_8)); - String line; - while ((line = bufferedReader.readLine()) != null) { - System.out.println(line); - } - - // read as byte[] -// FileStatus stat = fs.getFileStatus(path); -// byte[] buffer = new byte[Integer.parseInt(String.valueOf(stat.getLen()))]; -// is.readFully(0, buffer); - - is.close(); - fs.close(); - } - - private static Configuration getConfig(String url) { - Configuration config = new Configuration(); - config.set("fs.defaultFS", getHost(url)); - - return config; - } - - private static String getHost(String url) { - URI uri = URI.create(url); - return uri.getScheme() + "://" + uri.getHost() + ":" + uri.getPort(); - } -} diff --git a/hadoop/src/test/java/com/github/kuangcp/hdfs/hi/GeneralFileActionDemoTest.java b/hadoop/src/test/java/com/github/kuangcp/hdfs/hi/GeneralFileActionDemoTest.java deleted file mode 100644 index 454788ae..00000000 --- a/hadoop/src/test/java/com/github/kuangcp/hdfs/hi/GeneralFileActionDemoTest.java +++ /dev/null @@ -1,56 +0,0 @@ -package com.github.kuangcp.hdfs.hi; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.core.IsEqual.equalTo; - -import java.io.IOException; -import java.util.List; -import lombok.extern.slf4j.Slf4j; -import org.junit.Test; - -/** - * @author https://github.com/kuangcp - * @date 2019-05-20 11:28 - */ -@Slf4j -public class GeneralFileActionDemoTest { - - private static final String HDFS_HOST = "hdfs://172.16.16.80:8020"; - - @Test - public void testCreateDirectory() throws Exception { - boolean result = GeneralFileActionDemo.createDirectory(HDFS_HOST + "/flink-batch/test"); - assertThat(result, equalTo(true)); - } - - @Test - public void testCreateFile() throws IOException, InterruptedException { - GeneralFileActionDemo.createNewFile(HDFS_HOST + "/input/b.md", "fdasfasdfasd"); - } - - @Test - public void testList() throws IOException { - List files = GeneralFileActionDemo.listFiles(HDFS_HOST + "/flink-batch"); - files.forEach(v -> { - log.info("{}", v); - try { - GeneralFileActionDemo.deleteByURL(v); - } catch (IOException e) { - e.printStackTrace(); - } - }); - } - - @Test - public void testDelete() throws IOException { - boolean result = GeneralFileActionDemo - .deleteByURL(HDFS_HOST + "/flink-batch/1559008370602_MONTH_2019-03-01_2019-03-27_SPU.csv"); - System.out.println(result); - } - - @Test - public void testRead() throws Exception { - String url = "/flink-batch/1559008370602_MONTH_2019-03-01_2019-03-27_SPU.csv"; - GeneralFileActionDemo.readHDFSFile(HDFS_HOST + url); - } -} \ No newline at end of file diff --git a/kafka/build.gradle b/kafka/build.gradle deleted file mode 100644 index b6fd107d..00000000 --- a/kafka/build.gradle +++ /dev/null @@ -1,7 +0,0 @@ -dependencies { - implementation project(":common-config") - implementation libs['kcp-tool'] - implementation libs['kafka-clients'] - implementation libs['jackson_core'] - implementation libs['jackson_databind'] -} \ No newline at end of file diff --git a/kafka/src/main/java/com/github/kuangcp/hi/Constants.java b/kafka/src/main/java/com/github/kuangcp/hi/Constants.java deleted file mode 100644 index 8a1c0ae3..00000000 --- a/kafka/src/main/java/com/github/kuangcp/hi/Constants.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.github.kuangcp.hi; - -/** - * @author kuangcp - * - * Date: 2019-05-20 00:16 - */ -public interface Constants { - - String KAFKA_SERVER = "127.0.0.1:9092"; - - String HI_TOPIC = "Hi"; - - String START_TOPIC = "start"; - - String COMMAND_TOPIC = "OFC_PRODUCT_STATISTIC_JOB_DISPATCHING"; -} diff --git a/kafka/src/main/java/com/github/kuangcp/hi/ConsumerDemo.java b/kafka/src/main/java/com/github/kuangcp/hi/ConsumerDemo.java deleted file mode 100644 index bbc736d0..00000000 --- a/kafka/src/main/java/com/github/kuangcp/hi/ConsumerDemo.java +++ /dev/null @@ -1,74 +0,0 @@ -package com.github.kuangcp.hi; - -import static com.github.kuangcp.hi.Constants.HI_TOPIC; -import static com.github.kuangcp.hi.Constants.KAFKA_SERVER; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.github.kuangcp.hi.domain.ProductStatisticJobCommand; -import com.github.kuangcp.kafka.KafkaConsumerUtil; -import com.github.kuangcp.kafka.common.SimpleMessageExecutor; -import java.io.IOException; -import java.time.Duration; -import java.util.Collections; -import java.util.Properties; -import lombok.extern.slf4j.Slf4j; -import org.apache.kafka.clients.consumer.ConsumerRecord; -import org.apache.kafka.clients.consumer.ConsumerRecords; -import org.apache.kafka.clients.consumer.KafkaConsumer; -import org.apache.kafka.common.serialization.StringDeserializer; - -/** - * @author kuangcp - * - * Date: 2019-05-20 00:11 - */ -@Slf4j -public class ConsumerDemo { - - private static Properties properties = getConf(); - private static ObjectMapper mapper = new ObjectMapper(); - - static void receiveHi() { - SimpleMessageExecutor executor = new SimpleMessageExecutor() { - @Override - public void execute(String message) { - log.info("message={}", message); - } - - @Override - public String getTopic() { - return HI_TOPIC; - } - }; - - KafkaConsumerUtil.consumerPlainText(Duration.ofMillis(1000), - Collections.singletonList(executor)); - } - - static void receiveCommand() throws IOException { - KafkaConsumer kafkaConsumer = new KafkaConsumer<>(properties); - kafkaConsumer.subscribe(Collections.singletonList(Constants.COMMAND_TOPIC)); - while (true) { - ConsumerRecords records = kafkaConsumer.poll(Duration.ofMillis(100)); - for (ConsumerRecord record : records) { - log.info("offset = {}, value = {}", record.offset(), record.value()); - ProductStatisticJobCommand command = mapper - .readValue(record.value(), ProductStatisticJobCommand.class); - System.out.println(command); - } - } - } - - private static Properties getConf() { - Properties properties = new Properties(); - properties.put("bootstrap.servers", KAFKA_SERVER); - properties.put("group.id", "group-1"); - properties.put("enable.auto.commit", "true"); - properties.put("auto.commit.interval.ms", "1000"); - properties.put("auto.offset.reset", "earliest"); - properties.put("session.timeout.ms", "30000"); - properties.put("key.deserializer", StringDeserializer.class.getName()); - properties.put("value.deserializer", StringDeserializer.class.getName()); - return properties; - } -} \ No newline at end of file diff --git a/kafka/src/main/java/com/github/kuangcp/hi/ProducerDemo.java b/kafka/src/main/java/com/github/kuangcp/hi/ProducerDemo.java deleted file mode 100644 index ae93a55e..00000000 --- a/kafka/src/main/java/com/github/kuangcp/hi/ProducerDemo.java +++ /dev/null @@ -1,133 +0,0 @@ -package com.github.kuangcp.hi; - -import static com.github.kuangcp.hi.Constants.HI_TOPIC; -import static com.github.kuangcp.hi.Constants.KAFKA_SERVER; -import static com.github.kuangcp.hi.Constants.START_TOPIC; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.github.kuangcp.hi.domain.ProductStatisticJobCommand; -import com.github.kuangcp.hi.domain.ProductStatisticSpan; -import com.github.kuangcp.hi.domain.StartCommand; -import com.github.kuangcp.io.ResourceTool; -import java.io.IOException; -import java.time.LocalDateTime; -import java.time.ZoneId; -import java.util.Date; -import java.util.HashSet; -import java.util.Properties; -import java.util.UUID; -import lombok.extern.slf4j.Slf4j; -import org.apache.kafka.clients.producer.KafkaProducer; -import org.apache.kafka.clients.producer.Producer; -import org.apache.kafka.clients.producer.ProducerRecord; - -/** - * @author kuangcp - * - * Date: 2019-05-20 00:04 - */ -@Slf4j -public class ProducerDemo { - - private static Properties properties = getConf(); - private static ObjectMapper mapper = new ObjectMapper(); - - static void sendHi() { - Producer producer = null; - try { - producer = new KafkaProducer<>(properties); - for (int i = 0; i < 100; i++) { - String msg = "Message " + i; - producer.send(new ProducerRecord<>(HI_TOPIC, msg)); - log.info("Sent: {}", msg); - } - } catch (Exception e) { - log.error(e.getMessage(), e); - } finally { - try { - ResourceTool.close(producer); - } catch (IOException e) { - log.error(e.getMessage(), e); - } - } - } - - static void sendStart() { - Producer producer = null; - try { - producer = new KafkaProducer<>(properties); - for (int i = 0; i < 20; i++) { - StartCommand msg = StartCommand.builder().place("There").scale(i).startTime(new Date()) - .build(); - producer.send(new ProducerRecord<>(START_TOPIC, mapper.writeValueAsString(msg))); - log.info("Sent: {}", msg); - } - } catch (Exception e) { - log.error(e.getMessage(), e); - } finally { - try { - ResourceTool.close(producer); - } catch (IOException e) { - log.error(e.getMessage(), e); - } - } - } - - static void sendCommand() { - Producer producer = null; - HashSet spans = new HashSet<>(); - spans.add(ProductStatisticSpan.MONTH); - - try { - producer = new KafkaProducer<>(properties); - for (int i = 0; i < 2; i++) { - ProductStatisticJobCommand msg = ProductStatisticJobCommand.builder() - .id(UUID.randomUUID().toString() + "_test_" + i) - .startTime(toDate(LocalDateTime.now().withMonth(1))) - .endTime(toDate(LocalDateTime.now().withMonth(3))) - .productStatisticSpan(spans) - .build(); - ProducerRecord record = new ProducerRecord<>(Constants.COMMAND_TOPIC, - mapper.writeValueAsString(msg)); - - long time = System.currentTimeMillis(); - producer.send(record, (metadata, e) -> { - long elapsedTime = System.currentTimeMillis() - time; - if (metadata != null) { - log.info("sent record(key={} value={}) meta(partition={}, offset={}) time={}", - record.key(), record.value(), metadata.partition(), - metadata.offset(), elapsedTime); - } else { - log.error(e.getMessage(), e); - } - }); - } - } catch (Exception e) { - log.error(e.getMessage(), e); - } finally { - try { - ResourceTool.close(producer); - } catch (IOException e) { - log.error(e.getMessage(), e); - } - } - } - - public static Date toDate(LocalDateTime dateTime) { - return Date.from(dateTime.atZone(ZoneId.systemDefault()).toInstant()); - } - - private static Properties getConf() { - Properties properties = new Properties(); - properties.put("bootstrap.servers", KAFKA_SERVER); - properties.put("acks", "all"); - properties.put("retries", 0); - properties.put("batch.size", 16384); - properties.put("linger.ms", 1); - properties.put("buffer.memory", 33554432); - - properties.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer"); - properties.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer"); - return properties; - } -} diff --git a/kafka/src/main/java/com/github/kuangcp/hi/domain/ProductStatisticJobCommand.java b/kafka/src/main/java/com/github/kuangcp/hi/domain/ProductStatisticJobCommand.java deleted file mode 100644 index 7024b1f9..00000000 --- a/kafka/src/main/java/com/github/kuangcp/hi/domain/ProductStatisticJobCommand.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.github.kuangcp.hi.domain; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.io.Serializable; -import java.util.Date; -import java.util.Set; - -@Data -@Builder -@AllArgsConstructor -@NoArgsConstructor -public class ProductStatisticJobCommand implements Serializable { - - /** - * 任务ID - */ - private String id; - - - /** - * 统计维度 - */ - private Set productStatisticSpan; - - - /** - * 统计起始时间 - */ - private Date startTime; - - - /** - * 统计结束时间 - */ - private Date endTime; - -} diff --git a/kafka/src/main/java/com/github/kuangcp/hi/domain/ProductStatisticSpan.java b/kafka/src/main/java/com/github/kuangcp/hi/domain/ProductStatisticSpan.java deleted file mode 100644 index 7d092e87..00000000 --- a/kafka/src/main/java/com/github/kuangcp/hi/domain/ProductStatisticSpan.java +++ /dev/null @@ -1,62 +0,0 @@ -package com.github.kuangcp.hi.domain; - -import lombok.Getter; - -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.function.Function; -import java.util.stream.Collectors; - -@Getter -public enum ProductStatisticSpan { - - /** - * 按日统计 - */ - DAY("DAY00_", StatisticSpan.SPAN_DAY), - - /** - * 按月统计 - */ - MONTH("MONTH_", StatisticSpan.SPAN_MONTH), - - /** - * 按季度统计 - */ - SEASON("SEASON", StatisticSpan.SPAN_SEASON), - - /** - * 按年统计 - */ - YEAR("YEAR0_", StatisticSpan.SPAN_YEAR); - - private String prefix; - - private Integer span; - - private static final List ALL = Arrays.asList(values()); - - private static final Map MAP = ALL.stream() - .collect(Collectors.toMap(ProductStatisticSpan::getSpan, Function.identity())); - - ProductStatisticSpan(String prefix, Integer span) { - this.prefix = prefix; - this.span = span; - } - - - public static List getAll() { - return ALL; - } - - public static Map getMap() { - return MAP; - } - - public static ProductStatisticSpan getBySpan(Integer span) { - return MAP.getOrDefault(span, ProductStatisticSpan.SEASON); - } - -} - diff --git a/kafka/src/main/java/com/github/kuangcp/hi/domain/StartCommand.java b/kafka/src/main/java/com/github/kuangcp/hi/domain/StartCommand.java deleted file mode 100644 index 588b191e..00000000 --- a/kafka/src/main/java/com/github/kuangcp/hi/domain/StartCommand.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.github.kuangcp.hi.domain; - -import java.util.Date; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class StartCommand { - - private Date startTime; - - private String place; - - private int scale; -} diff --git a/kafka/src/main/java/com/github/kuangcp/hi/domain/StatisticSpan.java b/kafka/src/main/java/com/github/kuangcp/hi/domain/StatisticSpan.java deleted file mode 100644 index 96f409da..00000000 --- a/kafka/src/main/java/com/github/kuangcp/hi/domain/StatisticSpan.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.github.kuangcp.hi.domain; - -public interface StatisticSpan { - - /** - * 天 - */ - Integer SPAN_DAY = 2; - - /** - * 月 - */ - Integer SPAN_MONTH = 3; - - /** - * 季度 - */ - Integer SPAN_SEASON = 5; - - /** - * 年 - */ - Integer SPAN_YEAR = 4; - -} diff --git a/kafka/src/main/resources/kafka.properties b/kafka/src/main/resources/kafka.properties deleted file mode 100644 index 3c0a310c..00000000 --- a/kafka/src/main/resources/kafka.properties +++ /dev/null @@ -1,2 +0,0 @@ -bootstrap.servers=127.0.0.1:9092 -group.id=test-demo \ No newline at end of file diff --git a/kafka/src/test/java/com/github/kuangcp/hi/ConsumerDemoTest.java b/kafka/src/test/java/com/github/kuangcp/hi/ConsumerDemoTest.java deleted file mode 100644 index a86b1f4d..00000000 --- a/kafka/src/test/java/com/github/kuangcp/hi/ConsumerDemoTest.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.github.kuangcp.hi; - -import java.io.IOException; -import org.junit.Test; - -/** - * @author https://github.com/kuangcp - * @date 2019-05-21 16:20 - */ -public class ConsumerDemoTest { - - @Test - public void testReceiveHi() { - ConsumerDemo.receiveHi(); - } - - - @Test - public void testReceiveCommand() throws IOException { - ConsumerDemo.receiveCommand(); - } -} diff --git a/kafka/src/test/java/com/github/kuangcp/hi/ProducerDemoTest.java b/kafka/src/test/java/com/github/kuangcp/hi/ProducerDemoTest.java deleted file mode 100644 index e2fbf236..00000000 --- a/kafka/src/test/java/com/github/kuangcp/hi/ProducerDemoTest.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.github.kuangcp.hi; - -import org.junit.Test; - -/** - * @author https://github.com/kuangcp - * @date 2019-05-21 16:19 - */ -public class ProducerDemoTest { - - @Test - public void testSendCommand() { - ProducerDemo.sendCommand(); - } - - @Test - public void testSendStart() { - ProducerDemo.sendStart(); - } - - @Test - public void testSendHi() { - ProducerDemo.sendHi(); - } -} \ No newline at end of file diff --git a/kafka/src/test/resources/logback-test.xml b/kafka/src/test/resources/logback-test.xml deleted file mode 100644 index bf2a1d8b..00000000 --- a/kafka/src/test/resources/logback-test.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - %d{HH:mm:ss.SSS} %highlight(%-5level) [%thread] %logger{30} %highlight(%M):%yellow(%-3L) %msg%n - - - DEBUG - - - - - - - - - \ No newline at end of file diff --git a/mybatis/build.gradle b/mybatis/build.gradle deleted file mode 100644 index 83e23256..00000000 --- a/mybatis/build.gradle +++ /dev/null @@ -1,21 +0,0 @@ -plugins { - id 'org.springframework.boot' version '2.2.5.RELEASE' -} - -apply plugin: 'java' -apply plugin: 'io.spring.dependency-management' - -repositories { - mavenCentral() -} - -dependencies { - implementation project(":common-config") - implementation("org.springframework.boot:spring-boot-starter-web") - testImplementation("org.springframework.boot:spring-boot-starter-test") - implementation("com.baomidou:mybatis-plus-boot-starter:3.1.2") - implementation("mysql:mysql-connector-java:8.0.17") - - implementation libs["mybatis"] - implementation libs["mybatis-plus"] -} diff --git a/mybatis/create.sql b/mybatis/create.sql deleted file mode 100644 index e7008446..00000000 --- a/mybatis/create.sql +++ /dev/null @@ -1,16 +0,0 @@ -CREATE TABLE customer ( - id bigint(20) auto_increment primary key , - name varchar(20) DEFAULT NULL, - nation int(11) DEFAULT NULL -); -create table normal_order ( - id bigint(20) auto_increment primary key , - user_id bigint(20), - create_time datetime default CURRENT_TIMESTAMP, - update_time datetime default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP, - detail varchar(30), - price decimal(6,2), - discount decimal(6,2), - num int, - pay_time datetime -) \ No newline at end of file diff --git a/mybatis/src/main/java/com/github/kuangcp/Application.java b/mybatis/src/main/java/com/github/kuangcp/Application.java deleted file mode 100644 index 1c97c1d3..00000000 --- a/mybatis/src/main/java/com/github/kuangcp/Application.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.github.kuangcp; - -import org.mybatis.spring.annotation.MapperScan; -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; - -@MapperScan("com.github.kuangcp.**.dao") -@SpringBootApplication() -public class Application { - - public static void main(String[] args) { - SpringApplication.run(Application.class, args); - } -} diff --git a/mybatis/src/main/java/com/github/kuangcp/sharding/manual/AuthUtil.java b/mybatis/src/main/java/com/github/kuangcp/sharding/manual/AuthUtil.java deleted file mode 100644 index 9cfa1288..00000000 --- a/mybatis/src/main/java/com/github/kuangcp/sharding/manual/AuthUtil.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.github.kuangcp.sharding.manual; - -import org.springframework.stereotype.Component; - -/** - * @author https://github.com/kuangcp on 2021-07-11 18:53 - */ -@Component -public class AuthUtil { - - private final ThreadLocal orgIdLocal = new ThreadLocal<>(); - - public void completeAuth(Long orgId) { - orgIdLocal.set(orgId); - } - - public Long getAuthedOrgId() { - return orgIdLocal.get(); - } - - public void clearAuth() { - orgIdLocal.remove(); - } -} diff --git a/mybatis/src/main/java/com/github/kuangcp/sharding/manual/ConsistentHashingAlgorithm.java b/mybatis/src/main/java/com/github/kuangcp/sharding/manual/ConsistentHashingAlgorithm.java deleted file mode 100644 index 2d75d946..00000000 --- a/mybatis/src/main/java/com/github/kuangcp/sharding/manual/ConsistentHashingAlgorithm.java +++ /dev/null @@ -1,100 +0,0 @@ -package com.github.kuangcp.sharding.manual; - -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.*; - -/** - * @author https://github.com/kuangcp on 2021-07-11 19:13 - */ -public class ConsistentHashingAlgorithm { - private MessageDigest md5; - private final int numberOfReplicas = 300; - - private final SortedMap circle = new TreeMap<>(); - private static final Map cache = new HashMap<>(); - - public ConsistentHashingAlgorithm(int totalNode) { - for (int i = 1; i <= totalNode; i++) { - add("_" + i); - } - } - - /** - * TODO 线程不安全,以及环的多节点维护问题 - */ - public static ConsistentHashingAlgorithm useWithCache(int totalNode) { - ConsistentHashingAlgorithm result = cache.get(totalNode); - if (Objects.nonNull(result)) { - return result; - } - ConsistentHashingAlgorithm newOne = new ConsistentHashingAlgorithm(totalNode); - cache.put(totalNode, newOne); - return newOne; - } - - /** - * 添加虚拟节点 - * numberOfReplicas为虚拟节点的数量 - * 例如初始化hash环的时候传入,我们使用300个虚拟节点 - */ - public void add(String node) { - for (int i = 0; i < numberOfReplicas; i++) { - circle.put(this.hash(node.toString() + "_" + i), node); - } - } - - /** - * 移除节点 - */ - public void remove(String node) { - for (int i = 0; i < numberOfReplicas; i++) { - circle.remove(this.hash(node.toString() + i)); - } - } - - public String get(Long key) { - return get(key + ""); - } - - /** - * 获得一个最近的顺时针节点 - * - * @param key 为给定键取Hash,取得顺时针方向上最近的一个虚拟节点对应的实际节点 - */ - public String get(String key) { - if (circle.isEmpty()) { - return null; - } - long hash = this.hash(key); - if (!circle.containsKey(hash)) { - //返回此映射的部分视图,其键大于等于 hash - SortedMap tailMap = circle.tailMap(hash); - hash = tailMap.isEmpty() ? circle.firstKey() : tailMap.firstKey(); - } - return circle.get(hash); - } - - /** - * TODO 线程不安全 - */ - public long hash(String key) { - if (md5 == null) { - try { - md5 = MessageDigest.getInstance("MD5"); - } catch (NoSuchAlgorithmException e) { - throw new IllegalStateException("no md5 algorythm found"); - } - } - - md5.reset(); - md5.update(key.getBytes()); - byte[] bKey = md5.digest(); - - long res = ((long) (bKey[3] & 0xFF) << 24) | - ((long) (bKey[2] & 0xFF) << 16) | - ((long) (bKey[1] & 0xFF) << 8) | - (long) (bKey[0] & 0xFF); - return res & 0xffffffffL; - } -} diff --git a/mybatis/src/main/java/com/github/kuangcp/sharding/manual/ShardingAlgorithmEnum.java b/mybatis/src/main/java/com/github/kuangcp/sharding/manual/ShardingAlgorithmEnum.java deleted file mode 100644 index 70aa3726..00000000 --- a/mybatis/src/main/java/com/github/kuangcp/sharding/manual/ShardingAlgorithmEnum.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.github.kuangcp.sharding.manual; - -import lombok.AllArgsConstructor; -import lombok.Getter; - -import java.util.Objects; -import java.util.Optional; -import java.util.function.BiFunction; - -/** - * @author https://github.com/kuangcp on 2021-07-11 18:41 - */ -@Getter -@AllArgsConstructor -public enum ShardingAlgorithmEnum { - MOD(ShardingAlgorithmType.MOD, (shardingVal, totalSlice) -> { - long index = shardingVal % totalSlice; - return "_" + index; - }), - - CONSISTENT_HASHING(ShardingAlgorithmType.CONSISTENT_HASHING, - (shardingVal, totalCount) -> ConsistentHashingAlgorithm.useWithCache(totalCount).get(shardingVal)), - ; - - private final int type; - /** - * shardingVal 用于分片的值 - * totalSlice 总分片数 - * suffix 子表后缀 - */ - private final BiFunction func; - - - public static Optional of(Integer type) { - if (Objects.isNull(type)) { - return Optional.empty(); - } - for (ShardingAlgorithmEnum value : values()) { - if (Objects.equals(value.type, type)) { - return Optional.of(value); - } - } - return Optional.empty(); - } - -} diff --git a/mybatis/src/main/java/com/github/kuangcp/sharding/manual/ShardingAlgorithmType.java b/mybatis/src/main/java/com/github/kuangcp/sharding/manual/ShardingAlgorithmType.java deleted file mode 100644 index d90db68f..00000000 --- a/mybatis/src/main/java/com/github/kuangcp/sharding/manual/ShardingAlgorithmType.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.github.kuangcp.sharding.manual; - -/** - * @author https://github.com/kuangcp on 2021-07-11 18:59 - */ -public interface ShardingAlgorithmType { - int MOD = 1; - int CONSISTENT_HASHING = 2; -} diff --git a/mybatis/src/main/java/com/github/kuangcp/sharding/manual/ShardingTable.java b/mybatis/src/main/java/com/github/kuangcp/sharding/manual/ShardingTable.java deleted file mode 100644 index eb71605c..00000000 --- a/mybatis/src/main/java/com/github/kuangcp/sharding/manual/ShardingTable.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.github.kuangcp.sharding.manual; - -import java.lang.annotation.*; - -/** - * @author https://github.com/kuangcp on 2021-07-11 18:19 - */ -@Documented -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.TYPE) -public @interface ShardingTable { - - int algorithm(); - - int tableCount(); -} diff --git a/mybatis/src/main/java/com/github/kuangcp/sharding/manual/ShardingTableInterceptor.java b/mybatis/src/main/java/com/github/kuangcp/sharding/manual/ShardingTableInterceptor.java deleted file mode 100644 index 9a4f3296..00000000 --- a/mybatis/src/main/java/com/github/kuangcp/sharding/manual/ShardingTableInterceptor.java +++ /dev/null @@ -1,137 +0,0 @@ -package com.github.kuangcp.sharding.manual; - -import com.baomidou.mybatisplus.annotation.TableName; -import lombok.extern.slf4j.Slf4j; -import org.apache.ibatis.executor.statement.RoutingStatementHandler; -import org.apache.ibatis.executor.statement.StatementHandler; -import org.apache.ibatis.mapping.BoundSql; -import org.apache.ibatis.mapping.MappedStatement; -import org.apache.ibatis.plugin.*; -import org.apache.ibatis.reflection.MetaObject; -import org.apache.ibatis.reflection.SystemMetaObject; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; -import org.springframework.util.StringUtils; - -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.sql.Connection; -import java.util.Properties; - -/** - * @author https://github.com/kuangcp on 2021-07-11 18:19 - */ -@Slf4j -@Component -@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", - args = {Connection.class, Integer.class})}) -public class ShardingTableInterceptor implements Interceptor { - - @Autowired - private AuthUtil authUtil; - - @Override - public Object intercept(Invocation invocation) throws Throwable { - MetaObject metaObject; - MappedStatement mappedStatement; - Object target = invocation.getTarget(); - - if (!(target instanceof RoutingStatementHandler)) { - return invocation.proceed(); - } - RoutingStatementHandler routingStatementHandler = (RoutingStatementHandler) target; - metaObject = SystemMetaObject.forObject(routingStatementHandler); - StatementHandler statementHandler = (StatementHandler) metaObject.getValue("delegate"); - metaObject = SystemMetaObject.forObject(statementHandler); - - mappedStatement = (MappedStatement) metaObject.getValue("mappedStatement"); - BoundSql boundSql = statementHandler.getBoundSql(); - //获取对应的Mapper类 - Class mapperClass = Class.forName(mappedStatement.getId().substring(0, mappedStatement.getId().lastIndexOf("."))); - //获取对应EO - Class eoClass = getEoClass(mapperClass); - if (eoClass.isAnnotationPresent(ShardingTable.class) && eoClass.isAnnotationPresent(TableName.class)) { - String logicTable = eoClass.getAnnotation(TableName.class).value(); - ShardingTable rdsSharding = eoClass.getAnnotation(ShardingTable.class); - int algorithm = rdsSharding.algorithm(); - int tableTotal = rdsSharding.tableCount(); - - Long orgId = authUtil.getAuthedOrgId(); - - // 统一使用 组织id 分表或者不分表 - String subTableName = ShardingAlgorithmEnum.of(algorithm) - .map(ShardingAlgorithmEnum::getFunc) - .map(v -> v.apply(orgId, tableTotal)) - .map(v -> logicTable + v) - .orElse(logicTable); - - if (StringUtils.isEmpty(subTableName)) { - log.error("Unable to obtain subTableName , exec canceled. caseby: {} splitKey's value is null", logicTable); - } else { - String sql = boundSql.getSql(); - //将表名替换为子表名 - sql = sql.replaceAll(logicTable, subTableName); - - metaObject = SystemMetaObject.forObject(boundSql); - metaObject.setValue("sql", sql); - } - } - - return invocation.proceed(); - } - - - @Override - public Object plugin(Object target) { - // TODO Auto-generated method stub - if (target instanceof StatementHandler) { - return Plugin.wrap(target, this); - } else { - return target; - } - } - - @Override - public void setProperties(Properties properties) { - - } - - /** - * 获取Eo class - * - * @param eoMapper - * @return - */ - private Class getEoClass(Class eoMapper) { - Class entityClass = getGenericClass(eoMapper); - if (entityClass != null) { - String eoName = entityClass.getPackage().getName() + "." + StringUtils.delete(entityClass.getSimpleName(), "Eo") + "ExtEo"; - - try { - Class extClass = Class.forName(eoName); - entityClass = extClass; - } catch (ClassNotFoundException exception) { - } - } - - return entityClass; - } - - /** - * 获取接口的泛型类型,如果不存在则返回null - * - * @param clazz - * @return - */ - private Class getGenericClass(Class clazz) { - Type t = clazz.getGenericSuperclass(); - if (t == null) { - t = clazz.getGenericInterfaces()[0]; - } - if (t instanceof ParameterizedType) { - Type[] p = ((ParameterizedType) t).getActualTypeArguments(); - return ((Class) p[0]); - } - return null; - } -} diff --git a/mybatis/src/main/java/com/github/kuangcp/simple/customer/dao/CustomerDao.java b/mybatis/src/main/java/com/github/kuangcp/simple/customer/dao/CustomerDao.java deleted file mode 100644 index c1bc6513..00000000 --- a/mybatis/src/main/java/com/github/kuangcp/simple/customer/dao/CustomerDao.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.github.kuangcp.simple.customer.dao; - -import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import com.github.kuangcp.simple.customer.domain.Customer; -import org.apache.ibatis.annotations.Options; -import org.apache.ibatis.annotations.Param; -import org.apache.ibatis.annotations.ResultType; -import org.apache.ibatis.annotations.Select; -import org.apache.ibatis.mapping.ResultSetType; -import org.apache.ibatis.session.ResultHandler; -import org.springframework.stereotype.Repository; - -import java.util.List; - -@Repository -public interface CustomerDao extends BaseMapper { - - @Select({"select * from customer where name like #{name} "}) - List queryByName(@Param("name") String name); - - @Select("SELECT * from customer") - @Options(resultSetType = ResultSetType.FORWARD_ONLY, fetchSize = Integer.MIN_VALUE) - @ResultType(Customer.class) - void streamQueryAll(ResultHandler handler); -} diff --git a/mybatis/src/main/java/com/github/kuangcp/simple/customer/domain/Customer.java b/mybatis/src/main/java/com/github/kuangcp/simple/customer/domain/Customer.java deleted file mode 100644 index 0affe32e..00000000 --- a/mybatis/src/main/java/com/github/kuangcp/simple/customer/domain/Customer.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.github.kuangcp.simple.customer.domain; - -import com.baomidou.mybatisplus.annotation.TableName; -import com.github.kuangcp.sharding.manual.ShardingAlgorithmType; -import com.github.kuangcp.sharding.manual.ShardingTable; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -@TableName("customer") -@ShardingTable(algorithm = ShardingAlgorithmType.MOD, tableCount = 6) -public class Customer { - - private Long id; - private String name; - private Integer nation; -} diff --git a/mybatis/src/main/java/com/github/kuangcp/simple/order/dao/OrderDao.java b/mybatis/src/main/java/com/github/kuangcp/simple/order/dao/OrderDao.java deleted file mode 100644 index c5024ff8..00000000 --- a/mybatis/src/main/java/com/github/kuangcp/simple/order/dao/OrderDao.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.github.kuangcp.simple.order.dao; - -import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import com.github.kuangcp.simple.order.domain.Order; -import org.springframework.stereotype.Repository; - -@Repository -public interface OrderDao extends BaseMapper { - -} diff --git a/mybatis/src/main/java/com/github/kuangcp/simple/order/domain/Order.java b/mybatis/src/main/java/com/github/kuangcp/simple/order/domain/Order.java deleted file mode 100644 index f0c49853..00000000 --- a/mybatis/src/main/java/com/github/kuangcp/simple/order/domain/Order.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.github.kuangcp.simple.order.domain; - -import com.baomidou.mybatisplus.annotation.TableName; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.math.BigDecimal; -import java.time.LocalDateTime; - -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -@TableName("normal_order") -public class Order { - - private Long id; - private Long userId; - private LocalDateTime createTime; - private LocalDateTime updateTime; - private String detail; - private BigDecimal price; - private BigDecimal discount; - private Integer num; - private LocalDateTime payTime; -} diff --git a/mybatis/src/main/java/com/github/kuangcp/simple/order/dto/OrderDTO.java b/mybatis/src/main/java/com/github/kuangcp/simple/order/dto/OrderDTO.java deleted file mode 100644 index 652ba8a6..00000000 --- a/mybatis/src/main/java/com/github/kuangcp/simple/order/dto/OrderDTO.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.github.kuangcp.simple.order.dto; -import java.math.BigDecimal; -import java.time.LocalDateTime; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class OrderDTO { - private Long id; - private Long userId; - private String username; - private LocalDateTime createTime; - private LocalDateTime updateTime; - private String detail; - private BigDecimal price; - private BigDecimal discount; - private Integer num; - private LocalDateTime payTime; -} diff --git a/mybatis/src/main/java/com/github/kuangcp/simple/order/service/OrderService.java b/mybatis/src/main/java/com/github/kuangcp/simple/order/service/OrderService.java deleted file mode 100644 index 7991fb58..00000000 --- a/mybatis/src/main/java/com/github/kuangcp/simple/order/service/OrderService.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.github.kuangcp.simple.order.service; - -import com.github.kuangcp.simple.order.dto.OrderDTO; -import java.util.List; - -public interface OrderService { - - List queryByUserIdWithLoop(Long userId); - - List queryByUserId(Long userId); -} diff --git a/mybatis/src/main/java/com/github/kuangcp/simple/order/service/impl/OrderServiceImpl.java b/mybatis/src/main/java/com/github/kuangcp/simple/order/service/impl/OrderServiceImpl.java deleted file mode 100644 index a9ee630a..00000000 --- a/mybatis/src/main/java/com/github/kuangcp/simple/order/service/impl/OrderServiceImpl.java +++ /dev/null @@ -1,86 +0,0 @@ -package com.github.kuangcp.simple.order.service.impl; - -import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; -import com.github.kuangcp.simple.customer.dao.CustomerDao; -import com.github.kuangcp.simple.customer.domain.Customer; -import com.github.kuangcp.simple.order.dao.OrderDao; -import com.github.kuangcp.simple.order.domain.Order; -import com.github.kuangcp.simple.order.dto.OrderDTO; -import com.github.kuangcp.simple.order.service.OrderService; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; - -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.function.Function; -import java.util.stream.Collectors; - -@Service -public class OrderServiceImpl implements OrderService { - - @Autowired - private OrderDao orderDao; - - @Autowired - private CustomerDao customerDao; - - @Override - public List queryByUserIdWithLoop(Long userId) { - List orders = orderDao.selectList(new QueryWrapper().eq("user_id", userId)); - - return orders.stream().map(this::convert).collect(Collectors.toList()); - } - - @Override - public List queryByUserId(Long userId) { - List orders = orderDao.selectList(new QueryWrapper().eq("user_id", userId)); - - Set idSet = orders.stream().map(Order::getUserId).collect(Collectors.toSet()); - Map userMap = customerDao.selectBatchIds(idSet).stream() - .collect(Collectors.toMap(Customer::getId, Function.identity(), (front, next) -> next)); - - return orders.stream().map(v -> convert(v, userMap)).collect(Collectors.toList()); - } - - private OrderDTO convert(Order order, Map userMap) { - Customer customer = userMap.get(order.getUserId()); - String username = getName(customer); - return OrderDTO.builder() - .num(order.getNum()) - .userId(order.getUserId()) - .username(username) - .createTime(order.getCreateTime()) - .updateTime(order.getUpdateTime()) - .detail(order.getDetail()) - .price(order.getPrice()) - .discount(order.getDiscount()) - .payTime(order.getPayTime()) - .id(order.getId()) - .build(); - - } - - private OrderDTO convert(Order order) { - Customer customer = customerDao.selectById(order.getUserId()); - - String username = getName(customer); - return OrderDTO.builder() - .num(order.getNum()) - .userId(order.getUserId()) - .username(username) - .createTime(order.getCreateTime()) - .updateTime(order.getUpdateTime()) - .detail(order.getDetail()) - .price(order.getPrice()) - .discount(order.getDiscount()) - .payTime(order.getPayTime()) - .id(order.getId()) - .build(); - } - - private String getName(Customer customer) { - return Optional.ofNullable(customer).map(Customer::getName).orElse(""); - } -} diff --git a/mybatis/src/main/java/com/github/kuangcp/stream/Report.java b/mybatis/src/main/java/com/github/kuangcp/stream/Report.java deleted file mode 100644 index 38e33f83..00000000 --- a/mybatis/src/main/java/com/github/kuangcp/stream/Report.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.github.kuangcp.stream; - -import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.annotation.TableName; -import lombok.*; - -import java.util.Date; - -/** - * @author https://github.com/kuangcp on 2021-07-12 23:50 - */ -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -@EqualsAndHashCode(callSuper = false) -@TableName("report") -public class Report { - private static final long serialVersionUID = 1L; - - /** - * id - */ - @TableId - private Long id; - - /** - * 用户id - */ - private Long userId; - private String b; - private Integer c; - private Integer d; - private Integer e; - private Date statisticsTime; -} diff --git a/mybatis/src/main/java/com/github/kuangcp/stream/dao/ReportDao.java b/mybatis/src/main/java/com/github/kuangcp/stream/dao/ReportDao.java deleted file mode 100644 index ffad2a2c..00000000 --- a/mybatis/src/main/java/com/github/kuangcp/stream/dao/ReportDao.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.github.kuangcp.stream.dao; - -import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import com.github.kuangcp.stream.Report; -import org.apache.ibatis.annotations.Options; -import org.apache.ibatis.annotations.Param; -import org.apache.ibatis.annotations.ResultType; -import org.apache.ibatis.annotations.Select; -import org.apache.ibatis.mapping.ResultSetType; -import org.apache.ibatis.session.ResultHandler; -import org.springframework.stereotype.Repository; - -import java.util.Date; -import java.util.List; - -/** - * @author https://github.com/kuangcp on 2021-07-12 23:52 - */ -@Repository -public interface ReportDao extends BaseMapper { - - @Select("SELECT user_id, b, SUM(c) AS c" + - ", SUM(d) AS d, SUM(e) AS e" + - " FROM report WHERE statistics_time BETWEEN #{start} AND #{end}" + - " AND user_id IN (1,2,3,4,5,6)" + - " GROUP BY b") - @Options(resultSetType = ResultSetType.FORWARD_ONLY, fetchSize = Integer.MIN_VALUE) - @ResultType(Report.class) - void selectAutoList(@Param("start") Date start, - @Param("end") Date end, - ResultHandler handler); - - @Select("SELECT user_id, b, SUM(c) AS c" + - ", SUM(d) AS d, SUM(e) AS e" + - " FROM report WHERE statistics_time BETWEEN #{start} AND #{end}" + - " AND user_id IN (1,2,3,4,5,6)" + - " GROUP BY b LIMIT #{startIdx},#{size}") - List queryByPage(@Param("start") Date start, - @Param("end") Date end, - @Param("startIdx") long startIdx, - @Param("size") long size); -} diff --git a/mybatis/src/main/resources/application.yml b/mybatis/src/main/resources/application.yml deleted file mode 100644 index 8bf8a693..00000000 --- a/mybatis/src/main/resources/application.yml +++ /dev/null @@ -1,6 +0,0 @@ -spring: - datasource: - url: jdbc:mysql://127.0.0.1:3360/mybatis?autoReconnect=true&useUnicode=true&characterEncoding=utf8&useSSL=false - username: root - password: jiushi - maximum-pool-size: 30 diff --git a/mybatis/src/main/resources/logback.xml b/mybatis/src/main/resources/logback.xml deleted file mode 100644 index cb8ba4cc..00000000 --- a/mybatis/src/main/resources/logback.xml +++ /dev/null @@ -1,79 +0,0 @@ - - - - - - - - - %d{HH:mm:ss.SSS} %highlight(%-5level) [%thread] %logger{30} %highlight(%M):%yellow(%-3L) %msg%n - - - INFO - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/mybatis/src/test/java/com/github/kuangcp/base/SpringBootTestStarter.java b/mybatis/src/test/java/com/github/kuangcp/base/SpringBootTestStarter.java deleted file mode 100644 index 3235e510..00000000 --- a/mybatis/src/test/java/com/github/kuangcp/base/SpringBootTestStarter.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.github.kuangcp.base; - -import org.junit.runner.RunWith; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.junit4.SpringRunner; - -@SpringBootTest -@RunWith(SpringRunner.class) -public abstract class SpringBootTestStarter { - -} diff --git a/mybatis/src/test/java/com/github/kuangcp/sharding/manual/ConsistentHashingAlgorithmTest.java b/mybatis/src/test/java/com/github/kuangcp/sharding/manual/ConsistentHashingAlgorithmTest.java deleted file mode 100644 index 8b474b9b..00000000 --- a/mybatis/src/test/java/com/github/kuangcp/sharding/manual/ConsistentHashingAlgorithmTest.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.github.kuangcp.sharding.manual; - -import lombok.extern.slf4j.Slf4j; -import org.junit.Test; - -import java.util.*; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.stream.Collectors; - -/** - * @author https://github.com/kuangcp on 2021-07-11 19:32 - */ -@Slf4j -public class ConsistentHashingAlgorithmTest { - - private Map userMap = new HashMap<>(); - - /** - * 一致性hash算法优势在于 新增删除节点,原始数据迁移较少 - */ - @Test - public void testCompareMoveData() { - for (ShardingAlgorithmEnum func : ShardingAlgorithmEnum.values()) { - log.info("start: func={}", func); - this.userMap = new HashMap<>(); - for (int i = 2; i < 30; i += 3) { - calculatePerIndex(func, i, (int) Math.pow(2, i) * 20); -// calculatePerIndex(func, i, i * 3); - } - } - } - - private void calculatePerIndex(ShardingAlgorithmEnum func, int totalSlice, int totalData) { - Map newUserMap = new HashMap<>(); - for (int i = 0; i < totalData; i++) { - String finalTable = func.getFunc().apply((long) i, totalSlice); -// System.out.println(i + " " + finalTable); - newUserMap.put(i, finalTable); - } - long changCount = userMap.entrySet().stream().filter(v -> { - String newIndex = newUserMap.get(v.getKey()); - return !Objects.equals(newIndex, v.getValue()); - }).count(); - - this.userMap = newUserMap; - Map>> tableMap = userMap.entrySet() - .stream().collect(Collectors.groupingBy(Map.Entry::getValue)); - - AtomicInteger counter = new AtomicInteger(); - tableMap.entrySet().stream() - .sorted(Map.Entry.comparingByKey()).forEach(e -> { - counter.addAndGet(e.getValue().size()); - System.out.println(e.getKey() + ":" + e.getValue().size()); - }); - log.info("{}: count={} changeCount={}", func.name(), counter.get(), changCount); - } -} \ No newline at end of file diff --git a/mybatis/src/test/java/com/github/kuangcp/simple/customer/dao/CustomerDaoSpringBootTest.java b/mybatis/src/test/java/com/github/kuangcp/simple/customer/dao/CustomerDaoSpringBootTest.java deleted file mode 100644 index 69c1a02a..00000000 --- a/mybatis/src/test/java/com/github/kuangcp/simple/customer/dao/CustomerDaoSpringBootTest.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.github.kuangcp.simple.customer.dao; - -import com.github.kuangcp.base.SpringBootTestStarter; -import com.github.kuangcp.sharding.manual.AuthUtil; -import com.github.kuangcp.simple.customer.domain.Customer; -import lombok.extern.slf4j.Slf4j; -import org.junit.Test; -import org.springframework.beans.factory.annotation.Autowired; - -import java.util.List; -import java.util.stream.LongStream; - -@Slf4j -public class CustomerDaoSpringBootTest extends SpringBootTestStarter { - - @Autowired - private CustomerDao customerDao; - @Autowired - private AuthUtil authUtil; - - private void login(Long orgId) { - authUtil.clearAuth(); - authUtil.completeAuth(orgId); - } - - @Test - public void testQuery() { - login(2L); - customerDao.insert(Customer.builder().id(1L).name("myth").build()); - List customers = customerDao.queryByName("myth"); - - customers.forEach(item -> log.info("item={}", item)); - } - - @Test - public void testBulkInsert() { - - LongStream.rangeClosed(1, 1000) - .mapToObj(i -> Customer.builder().id(i) - .id(i) - .nation(3) - .build()) - .forEach(customerDao::insert); - } -} \ No newline at end of file diff --git a/mybatis/src/test/java/com/github/kuangcp/simple/order/dao/OrderDaoSpringBootTest.java b/mybatis/src/test/java/com/github/kuangcp/simple/order/dao/OrderDaoSpringBootTest.java deleted file mode 100644 index faf23b8d..00000000 --- a/mybatis/src/test/java/com/github/kuangcp/simple/order/dao/OrderDaoSpringBootTest.java +++ /dev/null @@ -1,65 +0,0 @@ -package com.github.kuangcp.simple.order.dao; - -import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; -import com.github.kuangcp.base.SpringBootTestStarter; -import com.github.kuangcp.simple.order.domain.Order; -import lombok.extern.slf4j.Slf4j; -import org.junit.Test; -import org.springframework.beans.factory.annotation.Autowired; - -import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.List; -import java.util.function.Consumer; - -@Slf4j -public class OrderDaoSpringBootTest extends SpringBootTestStarter { - - @Autowired - private OrderDao orderDao; - - @Test - public void testQuery() { - Order origin = Order.builder() - .num(1) - .detail("detail") - .createTime(LocalDateTime.now()) - .payTime(LocalDateTime.now()) - .build(); - orderDao.insert(origin); - List orders = orderDao.selectList(new QueryWrapper<>()); - orders.forEach(item -> log.info("{}", item)); - } - - @Test - public void testBulkInsert() throws InterruptedException { - - Consumer consumer = start -> { - Order temp = Order.builder().num(44) - .createTime(LocalDateTime.now()).build(); - for (int i = 0; i < 10000; i++) { - long id = (start + i); - temp.setId(id); - temp.setUserId(id % 400); - orderDao.insert(temp); - } - }; - - List threads = new ArrayList<>(); - for (int i = 0; i < 8; i++) { - int finalI = i; - threads.add(new Thread(() -> consumer.accept(500200 + finalI * 20000))); - } - - threads.forEach(v -> { - try { - v.start(); - v.join(); - } catch (InterruptedException e) { - e.printStackTrace(); - } - }); - - Thread.yield(); - } -} \ No newline at end of file diff --git a/mybatis/src/test/java/com/github/kuangcp/simple/order/service/OrderServiceSpringBootTest.java b/mybatis/src/test/java/com/github/kuangcp/simple/order/service/OrderServiceSpringBootTest.java deleted file mode 100644 index b902092d..00000000 --- a/mybatis/src/test/java/com/github/kuangcp/simple/order/service/OrderServiceSpringBootTest.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.github.kuangcp.simple.order.service; - -import com.github.kuangcp.base.SpringBootTestStarter; -import com.github.kuangcp.simple.order.dto.OrderDTO; -import java.util.List; -import lombok.extern.slf4j.Slf4j; -import org.junit.Test; -import org.springframework.beans.factory.annotation.Autowired; - -@Slf4j -public class OrderServiceSpringBootTest extends SpringBootTestStarter { - - @Autowired - private OrderService orderService; - - @Test - public void testCompareQueryId() { - List result = orderService.queryByUserIdWithLoop(200L); - log.info(": result={}", result); - result = orderService.queryByUserId(200L); - log.info(": result={}", result); - } -} \ No newline at end of file diff --git a/mybatis/src/test/java/com/github/kuangcp/stream/CursorSessionSpringBootTest.java b/mybatis/src/test/java/com/github/kuangcp/stream/CursorSessionSpringBootTest.java deleted file mode 100644 index 3aa235c5..00000000 --- a/mybatis/src/test/java/com/github/kuangcp/stream/CursorSessionSpringBootTest.java +++ /dev/null @@ -1,118 +0,0 @@ -package com.github.kuangcp.stream; - -import com.github.kuangcp.base.SpringBootTestStarter; -import com.github.kuangcp.sharding.manual.AuthUtil; -import com.github.kuangcp.simple.customer.dao.CustomerDao; -import com.github.kuangcp.simple.customer.domain.Customer; -import com.github.kuangcp.stream.dao.ReportDao; -import lombok.extern.slf4j.Slf4j; -import org.junit.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.util.StopWatch; - -import java.time.LocalDateTime; -import java.time.ZoneId; -import java.util.*; -import java.util.stream.Collectors; -import java.util.stream.IntStream; - -/** - * @author https://github.com/kuangcp on 2021-07-12 23:29 - */ -@Slf4j -public class CursorSessionSpringBootTest extends SpringBootTestStarter { - - @Autowired - private CustomerDao customerDao; - @Autowired - private AuthUtil authUtil; - @Autowired - private ReportDao reportDao; - - @Test - public void testStream() throws Exception { - authUtil.completeAuth(3L); - List result = new ArrayList<>(); - customerDao.streamQueryAll(ctx -> { - Customer val = ctx.getResultObject(); - result.add(val); - }); - log.info("result={}", result); - } - - @Test - public void testInsertCache() throws Exception { - List strList = IntStream.range(0, 70000) - .mapToObj(v -> UUID.randomUUID().toString()).collect(Collectors.toList()); - Random random = new Random(); - long userFrame = 1; // 构造不同数据段 - for (int i = 1; i < 120_000; i++) { - LocalDateTime now = LocalDateTime.now(); - LocalDateTime time = now.plusDays(-1 * (i / 2_000)).plusMinutes(i % 30).plusSeconds(i % 33).plusNanos(i % 4000); - Date statisticDate = Date.from(time.atZone(ZoneId.systemDefault()).toInstant()); - - Report report = Report.builder() - .userId((long) i % 6 + userFrame) -// .userId((long) i * new Random().nextInt(300)) - .b(strList.get(random.nextInt(70000))) - .c(i % 7) - .d(i % 4) - .e(i % 2) - .statisticsTime(statisticDate) - .build(); - reportDao.insert(report); - } - } - - @Test - public void testPageQueryOne() throws Exception { - StopWatch stopWatch = new StopWatch(); - stopWatch.start("query"); - Date start = Date.from(LocalDateTime.now().plusDays(-500).atZone(ZoneId.systemDefault()).toInstant()); - List reports = reportDao.queryByPage(start, new Date(), 0, 1000); - stopWatch.stop(); - log.info("reports.size()={}", reports.size()); - log.info("={}", stopWatch.prettyPrint()); - } - - // 90s = 30 * 3s - @Test - public void testPageQueryAll() throws Exception { - StopWatch stopWatch = new StopWatch(); - for (int i = 0; i < 10; i++) { - stopWatch.start("query" + i); - Date start = Date.from(LocalDateTime.now().plusDays(-500).atZone(ZoneId.systemDefault()).toInstant()); - List reports = reportDao.queryByPage(start, new Date(), i * 1000, 1000); - stopWatch.stop(); - log.info("reports.size()={}", reports.size()); - } - log.info("={}", stopWatch.prettyPrint()); - } - - // 3s - @Test - public void testStreamQuery() throws Exception { - Date start = Date.from(LocalDateTime.now().plusDays(-500).atZone(ZoneId.systemDefault()).toInstant()); - - List result = new ArrayList<>(); - StopWatch stopWatch = new StopWatch(); - stopWatch.start("query"); - reportDao.selectAutoList(start, new Date(), ctx -> { - Report val = ctx.getResultObject(); -// try { -// TimeUnit.NANOSECONDS.sleep(100); -// } catch (InterruptedException e) { -// e.printStackTrace(); -// } - result.add(val); - }); - stopWatch.stop(); - log.info("result={} {}ms {}", result.size(), stopWatch.getTotalTimeMillis(), stopWatch.prettyPrint()); - -// Path path = Paths.get("/tmp/report.scv"); -// String content = result.stream().map(report -> String.format("%d,%s,%d,%d,%d", report.getUserId(), -// report.getInquiryCode(), report.getInquiryCount(), report.getHaveGoodsCount(), report.getPurchaseCount())) -// .collect(Collectors.joining("\n")); -// Files.write(path, content.getBytes()); - } -} diff --git a/mybatis/src/test/java/com/github/kuangcp/stream/Readme.md b/mybatis/src/test/java/com/github/kuangcp/stream/Readme.md deleted file mode 100644 index 503934ea..00000000 --- a/mybatis/src/test/java/com/github/kuangcp/stream/Readme.md +++ /dev/null @@ -1,11 +0,0 @@ -使用流优化大数据量查询和导出 - -Stream模式 - -弊端: - -查看Mybatis实现原理,是否有连接高频占用和释放风险 - -优势: - -降低SQL查询扫描行数 \ No newline at end of file From 3b198bef32ca441d3d96c7fad683d42e573d7ad0 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Fri, 2 Sep 2022 00:39:30 +0800 Subject: [PATCH 268/476] rm: common --- algorithms/build.gradle | 2 +- build.gradle | 9 --------- class/build.gradle | 2 +- collection/build.gradle | 2 +- common-config/Readme.md | 1 - common-config/build.gradle | 1 - concurrency/build.gradle | 2 +- generic/build.gradle | 2 +- guava/build.gradle | 2 +- io/build.gradle | 2 +- java8/build.gradle | 2 +- network/build.gradle | 2 +- pattern/build.gradle | 2 +- question/build.gradle | 2 +- settings.gradle | 7 +------ spring/build.gradle | 2 +- test/build.gradle | 2 +- web/build.gradle | 2 +- 18 files changed, 15 insertions(+), 31 deletions(-) delete mode 100644 common-config/Readme.md delete mode 100644 common-config/build.gradle diff --git a/algorithms/build.gradle b/algorithms/build.gradle index 467b4ba2..36e1be10 100644 --- a/algorithms/build.gradle +++ b/algorithms/build.gradle @@ -1,5 +1,5 @@ dependencies { implementation(group: 'com.github.jsonzou', name: 'jmockdata', version: '4.1.2') implementation(libs['common-codec']) - implementation project(":common-config") + } \ No newline at end of file diff --git a/build.gradle b/build.gradle index cc5e1aa1..aa233324 100644 --- a/build.gradle +++ b/build.gradle @@ -6,16 +6,12 @@ buildscript { url "https://plugins.gradle.org/m2/" } } - dependencies { - classpath "org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:2.8" - } } // parent and child project all use this config subprojects { apply plugin: 'java' apply plugin: 'maven-publish' - apply plugin: "org.sonarqube" group = 'com.github.kuangcp' @@ -28,13 +24,8 @@ subprojects { repositories { mavenLocal() - - def aliYun = "https://maven.aliyun.com/nexus/content/groups/public/" def kuangcp = "https://gitee.com/gin9/MavenRepos/raw/master" - maven { - url = aliYun - } maven { url = kuangcp } diff --git a/class/build.gradle b/class/build.gradle index 9beda8a5..1adffe57 100644 --- a/class/build.gradle +++ b/class/build.gradle @@ -1,5 +1,5 @@ dependencies { - implementation project(":common-config") + implementation libs['kcp-tool'] diff --git a/collection/build.gradle b/collection/build.gradle index 3eecfa65..4b45b865 100644 --- a/collection/build.gradle +++ b/collection/build.gradle @@ -2,7 +2,7 @@ plugins { id 'java' } dependencies { - implementation project(":common-config") + implementation libs['kcp-tool'] // implementation libs['testng'] } diff --git a/common-config/Readme.md b/common-config/Readme.md deleted file mode 100644 index ba1b3d81..00000000 --- a/common-config/Readme.md +++ /dev/null @@ -1 +0,0 @@ -# 项目统一配置文件 diff --git a/common-config/build.gradle b/common-config/build.gradle deleted file mode 100644 index 1e83699c..00000000 --- a/common-config/build.gradle +++ /dev/null @@ -1 +0,0 @@ -version = '1.0.0' \ No newline at end of file diff --git a/concurrency/build.gradle b/concurrency/build.gradle index 9aade840..b6ead2ac 100644 --- a/concurrency/build.gradle +++ b/concurrency/build.gradle @@ -9,7 +9,7 @@ application { } dependencies { - implementation project(":common-config") + implementation libs['kcp-tuple'] implementation libs['blade-mvc'] diff --git a/generic/build.gradle b/generic/build.gradle index c9a5e53a..d91597c1 100644 --- a/generic/build.gradle +++ b/generic/build.gradle @@ -1,4 +1,4 @@ dependencies{ - implementation project(":common-config") + implementation rootProject.libs['common-math'] } \ No newline at end of file diff --git a/guava/build.gradle b/guava/build.gradle index f8e03238..8d7b8b56 100644 --- a/guava/build.gradle +++ b/guava/build.gradle @@ -1,4 +1,4 @@ dependencies { - implementation project(":common-config") + implementation libs['guava'] } \ No newline at end of file diff --git a/io/build.gradle b/io/build.gradle index 044aab2c..d4fdeb0d 100644 --- a/io/build.gradle +++ b/io/build.gradle @@ -1,4 +1,4 @@ dependencies { - implementation project(":common-config") + implementation libs['common-lang'] } diff --git a/java8/build.gradle b/java8/build.gradle index be7f0c27..5a11761d 100644 --- a/java8/build.gradle +++ b/java8/build.gradle @@ -1,4 +1,4 @@ dependencies { - implementation project(":common-config") + implementation(rootProject.libs['kcp-tool']) } diff --git a/network/build.gradle b/network/build.gradle index 195dc793..ef69ec0b 100644 --- a/network/build.gradle +++ b/network/build.gradle @@ -1,4 +1,4 @@ dependencies { - implementation project(":common-config") + implementation rootProject.libs['ok-http'] } \ No newline at end of file diff --git a/pattern/build.gradle b/pattern/build.gradle index c0a49352..4877effb 100644 --- a/pattern/build.gradle +++ b/pattern/build.gradle @@ -1,5 +1,5 @@ dependencies { - implementation project(":common-config") + implementation rootProject.libs['guava'] } diff --git a/question/build.gradle b/question/build.gradle index 2131f752..37ab04ef 100644 --- a/question/build.gradle +++ b/question/build.gradle @@ -1,5 +1,5 @@ dependencies { - implementation project(":common-config") + implementation libs['testng'] implementation libs["cglib-3.2.4"] diff --git a/settings.gradle b/settings.gradle index 6b946958..3946935c 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,7 +1,6 @@ rootProject.name = 'JavaBase' include( - 'common-config' - , 'io' + 'io' , 'generic' , 'collection' , 'class' @@ -16,9 +15,5 @@ include( , 'guava' , 'netty' , 'spring' - , 'kafka' - , 'hadoop' - , 'flink' - , 'mybatis' , 'web' ) diff --git a/spring/build.gradle b/spring/build.gradle index e04756b2..3a1d05bb 100644 --- a/spring/build.gradle +++ b/spring/build.gradle @@ -1,5 +1,5 @@ dependencies { - implementation project(":common-config") + implementation libs['MySQL'] implementation libs['cglib'] diff --git a/test/build.gradle b/test/build.gradle index e64af1b9..0ce6a166 100644 --- a/test/build.gradle +++ b/test/build.gradle @@ -1,3 +1,3 @@ dependencies { - implementation project(":common-config") + } \ No newline at end of file diff --git a/web/build.gradle b/web/build.gradle index 5724ebc6..c87a8e6a 100644 --- a/web/build.gradle +++ b/web/build.gradle @@ -1,5 +1,5 @@ dependencies { - implementation project(":common-config") + implementation group: 'javax.validation', name: 'validation-api', version: '2.0.1.Final' implementation group: 'org.hibernate.validator', name: 'hibernate-validator', version: '6.2.0.Final' From 832d0c18b8f6a11ee79c5f1976b241a7c11309c4 Mon Sep 17 00:00:00 2001 From: kuangcp Date: Fri, 25 Aug 2023 17:21:38 +0800 Subject: [PATCH 269/476] from gradle convert to maven --- algorithms/{build.gradle => build.gradle.bak} | 0 algorithms/pom.xml | 24 ++ build.gradle => build.gradle.bak | 0 class/{build.gradle => build.gradle.bak} | 0 class/pom.xml | 73 ++++ class/src/main/java/jvm/oom/HeapOOM.java | 171 ++++---- class/src/main/java/security/AES.java | 5 +- collection/{build.gradle => build.gradle.bak} | 0 collection/pom.xml | 24 ++ .../{build.gradle => build.gradle.bak} | 0 concurrency/pom.xml | 24 ++ dependency.gradle => dependency.gradle.bak | 0 generic/{build.gradle => build.gradle.bak} | 0 generic/pom.xml | 24 ++ guava/{build.gradle => build.gradle.bak} | 0 guava/pom.xml | 24 ++ gui/{build.gradle => build.gradle.bak} | 0 gui/pom.xml | 24 ++ io/{build.gradle => build.gradle.bak} | 0 io/pom.xml | 24 ++ java8/{build.gradle => build.gradle.bak} | 0 java8/pom.xml | 24 ++ netty/{build.gradle => build.gradle.bak} | 0 netty/pom.xml | 24 ++ network/{build.gradle => build.gradle.bak} | 0 network/pom.xml | 24 ++ pattern/{build.gradle => build.gradle.bak} | 0 pattern/pom.xml | 24 ++ pom.xml | 372 ++++++++++++++++++ question/{build.gradle => build.gradle.bak} | 0 question/pom.xml | 24 ++ settings.gradle => settings.gradle.bak | 0 spring/{build.gradle => build.gradle.bak} | 0 spring/pom.xml | 24 ++ test/{build.gradle => build.gradle.bak} | 0 test/pom.xml | 24 ++ web/{build.gradle => build.gradle.bak} | 0 web/pom.xml | 18 + 38 files changed, 889 insertions(+), 86 deletions(-) rename algorithms/{build.gradle => build.gradle.bak} (100%) create mode 100644 algorithms/pom.xml rename build.gradle => build.gradle.bak (100%) rename class/{build.gradle => build.gradle.bak} (100%) create mode 100644 class/pom.xml rename collection/{build.gradle => build.gradle.bak} (100%) create mode 100644 collection/pom.xml rename concurrency/{build.gradle => build.gradle.bak} (100%) create mode 100644 concurrency/pom.xml rename dependency.gradle => dependency.gradle.bak (100%) rename generic/{build.gradle => build.gradle.bak} (100%) create mode 100644 generic/pom.xml rename guava/{build.gradle => build.gradle.bak} (100%) create mode 100644 guava/pom.xml rename gui/{build.gradle => build.gradle.bak} (100%) create mode 100644 gui/pom.xml rename io/{build.gradle => build.gradle.bak} (100%) create mode 100644 io/pom.xml rename java8/{build.gradle => build.gradle.bak} (100%) create mode 100644 java8/pom.xml rename netty/{build.gradle => build.gradle.bak} (100%) create mode 100644 netty/pom.xml rename network/{build.gradle => build.gradle.bak} (100%) create mode 100644 network/pom.xml rename pattern/{build.gradle => build.gradle.bak} (100%) create mode 100644 pattern/pom.xml create mode 100644 pom.xml rename question/{build.gradle => build.gradle.bak} (100%) create mode 100644 question/pom.xml rename settings.gradle => settings.gradle.bak (100%) rename spring/{build.gradle => build.gradle.bak} (100%) create mode 100644 spring/pom.xml rename test/{build.gradle => build.gradle.bak} (100%) create mode 100644 test/pom.xml rename web/{build.gradle => build.gradle.bak} (100%) create mode 100644 web/pom.xml diff --git a/algorithms/build.gradle b/algorithms/build.gradle.bak similarity index 100% rename from algorithms/build.gradle rename to algorithms/build.gradle.bak diff --git a/algorithms/pom.xml b/algorithms/pom.xml new file mode 100644 index 00000000..c7f187fa --- /dev/null +++ b/algorithms/pom.xml @@ -0,0 +1,24 @@ + + + + com.github.kuangcp + java-base + 1.0.0 + + + 4.0.0 + + algorithms + 1.0.0 + + + UTF-8 + UTF-8 + + + + + + diff --git a/build.gradle b/build.gradle.bak similarity index 100% rename from build.gradle rename to build.gradle.bak diff --git a/class/build.gradle b/class/build.gradle.bak similarity index 100% rename from class/build.gradle rename to class/build.gradle.bak diff --git a/class/pom.xml b/class/pom.xml new file mode 100644 index 00000000..4d40ef57 --- /dev/null +++ b/class/pom.xml @@ -0,0 +1,73 @@ + + + + com.github.kuangcp + java-base + 1.0.0 + + + 4.0.0 + + class + 1.0.0 + + + UTF-8 + UTF-8 + + + + + + cglib + cglib + + + + org.apache.commons + commons-lang3 + + + + com.alibaba + fastjson + + + + com.google.code.gson + gson + + + + com.fasterxml.jackson.core + jackson-core + + + com.fasterxml.jackson.core + jackson-databind + + + com.fasterxml.jackson.core + jackson-annotations + + + com.fasterxml.jackson.dataformat + jackson-dataformat-smile + + + + + + org.openjdk.jmh + jmh-core + + + org.openjdk.jmh + jmh-generator-annprocess + + + + + diff --git a/class/src/main/java/jvm/oom/HeapOOM.java b/class/src/main/java/jvm/oom/HeapOOM.java index d1f854b9..0a541b28 100644 --- a/class/src/main/java/jvm/oom/HeapOOM.java +++ b/class/src/main/java/jvm/oom/HeapOOM.java @@ -6,6 +6,7 @@ import java.util.Map; import java.util.WeakHashMap; import java.util.concurrent.TimeUnit; + import lombok.extern.slf4j.Slf4j; /** @@ -14,101 +15,101 @@ @Slf4j public class HeapOOM { - void createArray() { - List data = new ArrayList<>(); - while (true) { - data.add(new byte[1024 * 1024]); - try { - TimeUnit.SECONDS.sleep(1); - } catch (InterruptedException e) { - e.printStackTrace(); - } + void createArray() { + List data = new ArrayList<>(); + while (true) { + data.add(new byte[1024 * 1024]); + try { + TimeUnit.SECONDS.sleep(1); + } catch (InterruptedException e) { + log.error("", e); + } + } } - } - /** - * 重写了hashCode 没有重写 equals - * 导致了该对象在Map这样的集合中作为Key使用时, 不能按预期的覆盖旧值而是共存 - */ - static class Key { + /** + * 重写了hashCode 没有重写 equals + * 导致了该对象在Map这样的集合中作为Key使用时, 不能按预期的覆盖旧值而是共存 + */ + static class Key { - Integer id; + Integer id; + + Key(Integer id) { + this.id = id; + } - Key(Integer id) { - this.id = id; + @Override + public int hashCode() { + return id.hashCode(); + } } - @Override - public int hashCode() { - return id.hashCode(); + void createMap() { + Map map = new HashMap<>(); + testMap(map); } - } - - void createMap() { - Map map = new HashMap<>(); - testMap(map); - } - - private void testMap(Map m) { - while (true) { - for (int i = 0; i < 10000; i++) { - // 因此这个判断就失效了 - if (!m.containsKey(new Key(i))) { - m.put(new Key(i), "Number:" + i); + + private void testMap(Map m) { + while (true) { + for (int i = 0; i < 10000; i++) { + // 因此这个判断就失效了 + if (!m.containsKey(new Key(i))) { + m.put(new Key(i), "Number:" + i); + } + } + log.info("m.size()=" + m.size()); } - } - log.info("m.size()=" + m.size()); } - } - - /** - * 因为 WeakMap 中 如果键没有被外部引用, 就有可能被GC, 当内存不够就会频繁GC, 也就不会OOM - */ - void createWeakMap() { - testMap(new WeakHashMap<>()); - } - - /** - * 变量逃逸和不逃逸 的区别 - * - * 当一个线程发生OOM, 如果能够及时释放内存(堆内存足够大,线程占用CPU不是很高等等), 是不会影响到其他线程的 - */ - public void otherThreadWithOOM() throws Exception { - // 如果将该集合定义在这里 就会被所有线程共享 被引用数会多, 不容易回收, 就容易引起全面的OOM 导致进程退出 + + /** + * 因为 WeakMap 中 如果键没有被外部引用, 就有可能被GC, 当内存不够就会频繁GC, 也就不会OOM + */ + void createWeakMap() { + testMap(new WeakHashMap<>()); + } + + /** + * 变量逃逸和不逃逸 的区别 + *

+ * 当一个线程发生OOM, 如果能够及时释放内存(堆内存足够大,线程占用CPU不是很高等等), 是不会影响到其他线程的 + */ + public void otherThreadWithOOM() throws Exception { + // 如果将该集合定义在这里 就会被所有线程共享 被引用数会多, 不容易回收, 就容易引起全面的OOM 导致进程退出 // List data = new ArrayList<>(); - Thread allocateMemory = new Thread(() -> { - // 但是定义在这里, 就只会被该线程持有, OOM 也只是该线程, 只有小概率会影响到进程内其他线程 - List data = new ArrayList<>(); - while (true) { - // 因为当这里无法分配内存 OOM 后就出循环了, 对象也就可以被回收了 - data.add(new byte[1024 * 10]); - try { - TimeUnit.MILLISECONDS.sleep(4); - } catch (InterruptedException e) { - log.error(e.getMessage(), e); - } + Thread allocateMemory = new Thread(() -> { + // 但是定义在这里, 就只会被该线程持有, OOM 也只是该线程, 只有小概率会影响到进程内其他线程 + List data = new ArrayList<>(); + while (true) { + // 因为当这里无法分配内存 OOM 后就出循环了, 对象也就可以被回收了 + data.add(new byte[1024 * 10]); + try { + TimeUnit.MILLISECONDS.sleep(4); + } catch (InterruptedException e) { + log.error(e.getMessage(), e); + } // log.info("allocate memory"); - } - }); - - Thread timer = new Thread(() -> { - while (true) { - try { - TimeUnit.SECONDS.sleep(3); - } catch (InterruptedException e) { - log.error(e.getMessage(), e); - } - log.info("timer"); - } - }); - - allocateMemory.setName("allocateMemory"); - timer.setName("timer"); - - allocateMemory.start(); - timer.start(); - allocateMemory.join(); - timer.join(); - } + } + }); + + Thread timer = new Thread(() -> { + while (true) { + try { + TimeUnit.SECONDS.sleep(3); + } catch (InterruptedException e) { + log.error(e.getMessage(), e); + } + log.info("timer"); + } + }); + + allocateMemory.setName("allocateMemory"); + timer.setName("timer"); + + allocateMemory.start(); + timer.start(); + allocateMemory.join(); + timer.join(); + } } diff --git a/class/src/main/java/security/AES.java b/class/src/main/java/security/AES.java index 3487ead2..fcd925c5 100644 --- a/class/src/main/java/security/AES.java +++ b/class/src/main/java/security/AES.java @@ -6,9 +6,12 @@ import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.spec.SecretKeySpec; + +import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import sun.misc.BASE64Decoder; +@Slf4j public class AES { //密钥 (需要前端和后端保持一致) @@ -25,7 +28,7 @@ public static String aesDecrypt(String encrypt) { try { return aesDecrypt(encrypt, KEY); } catch (Exception e) { - e.printStackTrace(); + log.error("", e); return ""; } } diff --git a/collection/build.gradle b/collection/build.gradle.bak similarity index 100% rename from collection/build.gradle rename to collection/build.gradle.bak diff --git a/collection/pom.xml b/collection/pom.xml new file mode 100644 index 00000000..33fe2ba5 --- /dev/null +++ b/collection/pom.xml @@ -0,0 +1,24 @@ + + + + com.github.kuangcp + java-base + 1.0.0 + + + 4.0.0 + + collection + 1.0.0 + + + UTF-8 + UTF-8 + + + + + + diff --git a/concurrency/build.gradle b/concurrency/build.gradle.bak similarity index 100% rename from concurrency/build.gradle rename to concurrency/build.gradle.bak diff --git a/concurrency/pom.xml b/concurrency/pom.xml new file mode 100644 index 00000000..ffb90e96 --- /dev/null +++ b/concurrency/pom.xml @@ -0,0 +1,24 @@ + + + + com.github.kuangcp + java-base + 1.0.0 + + + 4.0.0 + + concurrency + 1.0.0 + + + UTF-8 + UTF-8 + + + + + + diff --git a/dependency.gradle b/dependency.gradle.bak similarity index 100% rename from dependency.gradle rename to dependency.gradle.bak diff --git a/generic/build.gradle b/generic/build.gradle.bak similarity index 100% rename from generic/build.gradle rename to generic/build.gradle.bak diff --git a/generic/pom.xml b/generic/pom.xml new file mode 100644 index 00000000..28e42f9c --- /dev/null +++ b/generic/pom.xml @@ -0,0 +1,24 @@ + + + + com.github.kuangcp + java-base + 1.0.0 + + + 4.0.0 + + generic + 1.0.0 + + + UTF-8 + UTF-8 + + + + + + diff --git a/guava/build.gradle b/guava/build.gradle.bak similarity index 100% rename from guava/build.gradle rename to guava/build.gradle.bak diff --git a/guava/pom.xml b/guava/pom.xml new file mode 100644 index 00000000..4d79b65b --- /dev/null +++ b/guava/pom.xml @@ -0,0 +1,24 @@ + + + + com.github.kuangcp + java-base + 1.0.0 + + + 4.0.0 + + guava + 1.0.0 + + + UTF-8 + UTF-8 + + + + + + diff --git a/gui/build.gradle b/gui/build.gradle.bak similarity index 100% rename from gui/build.gradle rename to gui/build.gradle.bak diff --git a/gui/pom.xml b/gui/pom.xml new file mode 100644 index 00000000..1a16bbed --- /dev/null +++ b/gui/pom.xml @@ -0,0 +1,24 @@ + + + + com.github.kuangcp + java-base + 1.0.0 + + + 4.0.0 + + gui + 1.0.0 + + + UTF-8 + UTF-8 + + + + + + diff --git a/io/build.gradle b/io/build.gradle.bak similarity index 100% rename from io/build.gradle rename to io/build.gradle.bak diff --git a/io/pom.xml b/io/pom.xml new file mode 100644 index 00000000..6ea2fb33 --- /dev/null +++ b/io/pom.xml @@ -0,0 +1,24 @@ + + + + com.github.kuangcp + java-base + 1.0.0 + + + 4.0.0 + + io + 1.0.0 + + + UTF-8 + UTF-8 + + + + + + diff --git a/java8/build.gradle b/java8/build.gradle.bak similarity index 100% rename from java8/build.gradle rename to java8/build.gradle.bak diff --git a/java8/pom.xml b/java8/pom.xml new file mode 100644 index 00000000..9d3718f6 --- /dev/null +++ b/java8/pom.xml @@ -0,0 +1,24 @@ + + + + com.github.kuangcp + java-base + 1.0.0 + + + 4.0.0 + + java8 + 1.0.0 + + + UTF-8 + UTF-8 + + + + + + diff --git a/netty/build.gradle b/netty/build.gradle.bak similarity index 100% rename from netty/build.gradle rename to netty/build.gradle.bak diff --git a/netty/pom.xml b/netty/pom.xml new file mode 100644 index 00000000..6771a7a4 --- /dev/null +++ b/netty/pom.xml @@ -0,0 +1,24 @@ + + + + com.github.kuangcp + java-base + 1.0.0 + + + 4.0.0 + + netty + 1.0.0 + + + UTF-8 + UTF-8 + + + + + + diff --git a/network/build.gradle b/network/build.gradle.bak similarity index 100% rename from network/build.gradle rename to network/build.gradle.bak diff --git a/network/pom.xml b/network/pom.xml new file mode 100644 index 00000000..6b8a87c4 --- /dev/null +++ b/network/pom.xml @@ -0,0 +1,24 @@ + + + + com.github.kuangcp + java-base + 1.0.0 + + + 4.0.0 + + network + 1.0.0 + + + UTF-8 + UTF-8 + + + + + + diff --git a/pattern/build.gradle b/pattern/build.gradle.bak similarity index 100% rename from pattern/build.gradle rename to pattern/build.gradle.bak diff --git a/pattern/pom.xml b/pattern/pom.xml new file mode 100644 index 00000000..fc43b262 --- /dev/null +++ b/pattern/pom.xml @@ -0,0 +1,24 @@ + + + + com.github.kuangcp + java-base + 1.0.0 + + + 4.0.0 + + pattern + 1.0.0 + + + UTF-8 + UTF-8 + + + + + + diff --git a/pom.xml b/pom.xml new file mode 100644 index 00000000..3b439414 --- /dev/null +++ b/pom.xml @@ -0,0 +1,372 @@ + + + 4.0.0 + com.github.kuangcp + java-base + 1.0.0 + + + UTF-8 + UTF-8 + 1.8 + + + + + algorithms + class + collection + concurrency + generic + guava + gui + io + java8 + netty + network + pattern + question + spring + test + web + + + pom + + + + + org.projectlombok + lombok + + + + ch.qos.logback + logback-classic + + + + + + + + + org.projectlombok + lombok + 1.18.28 + provided + + + + ch.qos.logback + logback-classic + 1.4.11 + + + + cglib + cglib + 3.2.9 + + + org.apache.commons + commons-lang3 + 3.7 + + + + + com.github.kuangcp + kcp-tool + 1.0.8 + + + com.github.kuangcp + kcp-tuple + 1.0.8 + + + com.github.kuangcp + kcp-core + 1.0.8 + + + org.projectlombok + lombok + 1.18.2 + + + org.apache.commons + commons-lang3 + 3.7 + + + org.apache.commons + commons-collections4 + 4.4 + + + org.codehaus.groovy + groovy-all + 3.0.8 + + + cglib + cglib + 3.2.9 + + + org.apache.commons + commons-math3 + 3.6.1 + + + commons-codec + commons-codec + 1.13 + + + com.google.guava + guava + 30.1.1-jre + + + org.hamcrest + hamcrest-core + 1.3 + + + org.hamcrest + hamcrest-library + 1.3 + + + org.openjdk.jmh + jmh-core + 1.33 + + + org.openjdk.jmh + jmh-generator-annprocess + 1.33 + + + junit + junit + 4.13.2 + + + org.mockito + mockito-core + 2.21.0 + + + org.testng + testng + 6.14.3 + + + com.squareup.okhttp3 + okhttp + 3.13.1 + + + io.netty + netty-all + 4.1.67.Final + + + com.bladejava + blade-mvc + 2.0.15.RELEASE + + + javax.mail + mail + 1.4.7 + + + javax.activation + activation + 1.1.1 + + + redis.clients + jedis + 3.0.1 + + + mysql + mysql-connector-java + 8.0.15 + + + com.google.code.gson + gson + 2.8.5 + + + com.alibaba + fastjson + 1.2.47 + + + com.fasterxml.jackson.core + jackson-core + 2.9.5 + + + com.fasterxml.jackson.core + jackson-databind + 2.9.5 + + + com.fasterxml.jackson.core + jackson-annotations + 2.9.5 + + + com.fasterxml.jackson.dataformat + jackson-dataformat-smile + 2.9.5 + + + org.aspectj + aspectjrt + 1.9.0 + + + org.aspectj + aspectjweaver + 1.9.0 + + + org.springframework + spring-core + 5.1.20.RELEASE + + + org.springframework + spring-context + 5.1.20.RELEASE + + + org.springframework + spring-beans + 5.1.20.RELEASE + + + org.springframework + spring-aop + 5.1.20.RELEASE + + + org.springframework + spring-orm + 5.1.20.RELEASE + + + org.mybatis + mybatis + 3.5.2 + + + com.baomidou + mybatis-plus + 3.1.2 + + + org.apache.kafka + kafka-clients + 2.3.1 + + + org.apache.flink + flink-java + 1.8.0 + + + org.apache.flink + flink-clients_2.12 + 1.8.0 + + + org.apache.flink + flink-streaming-java_2.12 + 1.8.0 + + + org.apache.hadoop + hadoop-common + 2.7.2 + + + org.apache.hadoop + hadoop-client + 2.7.2 + + + org.apache.hadoop + hadoop-hdfs + 2.7.2 + + + cglib + cglib + 3.2.4 + + + asm + asm + 3.1 + + + org.ow2.asm + asm + 5.1 + + + co.paralleluniverse + quasar-core + 0.7.4 + + + co.paralleluniverse + quasar-actors + 0.7.4 + + + co.paralleluniverse + quasar-reactive-streams + 0.7.4 + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.1 + + ${java.version} + ${java.version} + ${project.build.sourceEncoding} + + + + + + + + public + aliyun nexus + http://maven.aliyun.com/nexus/content/groups/public/ + + true + + + + diff --git a/question/build.gradle b/question/build.gradle.bak similarity index 100% rename from question/build.gradle rename to question/build.gradle.bak diff --git a/question/pom.xml b/question/pom.xml new file mode 100644 index 00000000..b146c563 --- /dev/null +++ b/question/pom.xml @@ -0,0 +1,24 @@ + + + + com.github.kuangcp + java-base + 1.0.0 + + + 4.0.0 + + question + 1.0.0 + + + UTF-8 + UTF-8 + + + + + + diff --git a/settings.gradle b/settings.gradle.bak similarity index 100% rename from settings.gradle rename to settings.gradle.bak diff --git a/spring/build.gradle b/spring/build.gradle.bak similarity index 100% rename from spring/build.gradle rename to spring/build.gradle.bak diff --git a/spring/pom.xml b/spring/pom.xml new file mode 100644 index 00000000..898039fd --- /dev/null +++ b/spring/pom.xml @@ -0,0 +1,24 @@ + + + + com.github.kuangcp + java-base + 1.0.0 + + + 4.0.0 + + spring + 1.0.0 + + + UTF-8 + UTF-8 + + + + + + diff --git a/test/build.gradle b/test/build.gradle.bak similarity index 100% rename from test/build.gradle rename to test/build.gradle.bak diff --git a/test/pom.xml b/test/pom.xml new file mode 100644 index 00000000..bbdc7284 --- /dev/null +++ b/test/pom.xml @@ -0,0 +1,24 @@ + + + + com.github.kuangcp + java-base + 1.0.0 + + + 4.0.0 + + test + 1.0.0 + + + UTF-8 + UTF-8 + + + + + + diff --git a/web/build.gradle b/web/build.gradle.bak similarity index 100% rename from web/build.gradle rename to web/build.gradle.bak diff --git a/web/pom.xml b/web/pom.xml new file mode 100644 index 00000000..6d0b875d --- /dev/null +++ b/web/pom.xml @@ -0,0 +1,18 @@ + + + 4.0.0 + com.github.kuangcp + web + 1.0.0 + + + UTF-8 + UTF-8 + + + + + + From 4b0911b6774a33f15960292b12aa2c63bfe356d3 Mon Sep 17 00:00:00 2001 From: kuangcp Date: Fri, 25 Aug 2023 17:28:42 +0800 Subject: [PATCH 270/476] mvn: algo --- algorithms/pom.xml | 10 ++++++++++ class/src/main/java/jvm/oom/MetaspaceOOM.java | 8 ++++++++ 2 files changed, 18 insertions(+) create mode 100644 class/src/main/java/jvm/oom/MetaspaceOOM.java diff --git a/algorithms/pom.xml b/algorithms/pom.xml index c7f187fa..63a2774b 100644 --- a/algorithms/pom.xml +++ b/algorithms/pom.xml @@ -18,6 +18,16 @@ UTF-8 + + com.github.jsonzou + jmockdata + 4.1.2 + + + + commons-codec + commons-codec + diff --git a/class/src/main/java/jvm/oom/MetaspaceOOM.java b/class/src/main/java/jvm/oom/MetaspaceOOM.java new file mode 100644 index 00000000..605c14bd --- /dev/null +++ b/class/src/main/java/jvm/oom/MetaspaceOOM.java @@ -0,0 +1,8 @@ +package jvm.oom; + +/** + * @author kuangcp + * 2023-08-25 17:24 + */ +public class MetaspaceOOM { +} From 238a8ba3520376e3c70d4a65e10163ffa5d4f351 Mon Sep 17 00:00:00 2001 From: kuangcp Date: Fri, 25 Aug 2023 19:30:10 +0800 Subject: [PATCH 271/476] mvn: build most module --- README.md | 11 ++++ algorithms/pom.xml | 1 - concurrency/pom.xml | 28 ++++++++ generic/pom.xml | 5 +- gui/pom.xml | 4 ++ io/pom.xml | 5 +- netty/pom.xml | 11 ++++ pom.xml | 155 ++++++++++++++++++++++++++++++++------------ spring/pom.xml | 50 ++++++++++++++ web/pom.xml | 23 ++++++- 10 files changed, 246 insertions(+), 47 deletions(-) diff --git a/README.md b/README.md index 9b708e44..fa064a70 100644 --- a/README.md +++ b/README.md @@ -31,3 +31,14 @@ > [Java 学习笔记](https://github.com/brianway/java-learning) > [demo](https://gitee.com/code4everything/demo) > [sparrow](https://github.com/david1228/sparrow) + + +## 构建 +> .m2/settings.xml +```xml + + mygrid-restlet + https://maven.restlet.talend.com + maven-restlet + +``` \ No newline at end of file diff --git a/algorithms/pom.xml b/algorithms/pom.xml index 63a2774b..f511be5b 100644 --- a/algorithms/pom.xml +++ b/algorithms/pom.xml @@ -21,7 +21,6 @@ com.github.jsonzou jmockdata - 4.1.2 diff --git a/concurrency/pom.xml b/concurrency/pom.xml index ffb90e96..d94936e4 100644 --- a/concurrency/pom.xml +++ b/concurrency/pom.xml @@ -19,6 +19,34 @@ + + io.github.resilience4j + resilience4j-circuitbreaker + + + + redis.clients + jedis + + + + com.bladejava + blade-mvc + + + + + + + org.apache.commons + commons-lang3 + + + + org.testng + testng + + diff --git a/generic/pom.xml b/generic/pom.xml index 28e42f9c..656fa679 100644 --- a/generic/pom.xml +++ b/generic/pom.xml @@ -18,7 +18,10 @@ UTF-8 - + + org.apache.commons + commons-math3 + diff --git a/gui/pom.xml b/gui/pom.xml index 1a16bbed..f08b3ae9 100644 --- a/gui/pom.xml +++ b/gui/pom.xml @@ -19,6 +19,10 @@ + + + + diff --git a/io/pom.xml b/io/pom.xml index 6ea2fb33..0abcb9a6 100644 --- a/io/pom.xml +++ b/io/pom.xml @@ -18,7 +18,10 @@ UTF-8 - + + org.apache.commons + commons-lang3 + diff --git a/netty/pom.xml b/netty/pom.xml index 6771a7a4..81ca378f 100644 --- a/netty/pom.xml +++ b/netty/pom.xml @@ -19,6 +19,17 @@ + + io.netty + netty-all + + + + org.testng + testng + test + + diff --git a/pom.xml b/pom.xml index 3b439414..6d04e53e 100644 --- a/pom.xml +++ b/pom.xml @@ -21,13 +21,13 @@ concurrency generic guava - gui - io + + java8 - netty - network + + pattern - question + spring test web @@ -65,38 +65,21 @@ 1.4.11 - - cglib - cglib - 3.2.9 - - - org.apache.commons - commons-lang3 - 3.7 - - - - - com.github.kuangcp - kcp-tool - 1.0.8 - - - com.github.kuangcp - kcp-tuple - 1.0.8 - - - com.github.kuangcp - kcp-core - 1.0.8 - - - org.projectlombok - lombok - 1.18.2 - + + + + + + + + + + + + + + + org.apache.commons commons-lang3 @@ -108,10 +91,11 @@ 4.4 - org.codehaus.groovy + org.apache.groovy groovy-all - 3.0.8 + 4.0.14 + cglib cglib @@ -166,6 +150,7 @@ org.testng testng 6.14.3 + test com.squareup.okhttp3 @@ -277,6 +262,17 @@ mybatis-plus 3.1.2 + + org.hibernate + hibernate-core + 5.3.7.Final + + + commons-dbcp + commons-dbcp + 1.4 + + org.apache.kafka kafka-clients @@ -312,11 +308,33 @@ hadoop-hdfs 2.7.2 + - cglib - cglib - 3.2.4 + javax.validation + validation-api + 2.0.1.Final + + org.hibernate.validator + hibernate-validator + 6.2.0.Final + + + javax.el + javax.el-api + 3.0.0 + + + org.glassfish.web + javax.el + 2.2.6 + + + + + + + asm asm @@ -342,6 +360,44 @@ quasar-reactive-streams 0.7.4 + + + com.github.jsonzou + jmockdata + 4.1.2 + + + + io.github.resilience4j + resilience4j-circuitbreaker + 1.7.0 + + + io.github.resilience4j + resilience4j-ratelimiter + 1.7.0 + + + io.github.resilience4j + resilience4j-retry + 1.7.0 + + + io.github.resilience4j + resilience4j-bulkhead + 1.7.0 + + + io.github.resilience4j + resilience4j-cache + 1.7.0 + + + io.github.resilience4j + resilience4j-timelimiter + 1.7.0 + + @@ -360,10 +416,23 @@ + + maven-restlet + Public online Restlet repository + https://maven.restlet.talend.com + public aliyun nexus - http://maven.aliyun.com/nexus/content/groups/public/ + https://maven.aliyun.com/nexus/content/groups/public/ + + true + + + + gitee + kuangcp gitee + https://gitee.com/gin9/MavenRepos/raw/master true diff --git a/spring/pom.xml b/spring/pom.xml index 898039fd..b366eeb6 100644 --- a/spring/pom.xml +++ b/spring/pom.xml @@ -19,6 +19,56 @@ + + mysql + mysql-connector-java + + + + cglib + cglib + + + + org.aspectj + aspectjrt + + + org.aspectj + aspectjweaver + + + + org.springframework + spring-core + + + org.springframework + spring-context + + + org.springframework + spring-beans + + + org.springframework + spring-aop + + + org.springframework + spring-orm + + + + org.hibernate + hibernate-core + + + + commons-dbcp + commons-dbcp + + diff --git a/web/pom.xml b/web/pom.xml index 6d0b875d..b0f5d9d5 100644 --- a/web/pom.xml +++ b/web/pom.xml @@ -2,8 +2,13 @@ + + com.github.kuangcp + java-base + 1.0.0 + + 4.0.0 - com.github.kuangcp web 1.0.0 @@ -13,6 +18,22 @@ + + javax.validation + validation-api + + + org.hibernate.validator + hibernate-validator + + + javax.el + javax.el-api + + + org.glassfish.web + javax.el + From c2b10c4c0709be22aa5e60d94f8bf71bd0073473 Mon Sep 17 00:00:00 2001 From: kuangcp Date: Fri, 25 Aug 2023 19:55:22 +0800 Subject: [PATCH 272/476] mvn: most module --- .../com/github/kuangcp/io/ResourceTool.java | 23 +++++++++ .../com/github/kuangcp/io/ResourceTool.java | 23 +++++++++ pom.xml | 50 +++++++++---------- question/pom.xml | 38 ++++++++++++++ 4 files changed, 109 insertions(+), 25 deletions(-) create mode 100644 io/src/main/java/com/github/kuangcp/io/ResourceTool.java create mode 100644 network/src/main/java/com/github/kuangcp/io/ResourceTool.java diff --git a/io/src/main/java/com/github/kuangcp/io/ResourceTool.java b/io/src/main/java/com/github/kuangcp/io/ResourceTool.java new file mode 100644 index 00000000..6b7c3485 --- /dev/null +++ b/io/src/main/java/com/github/kuangcp/io/ResourceTool.java @@ -0,0 +1,23 @@ +package com.github.kuangcp.io; + +import java.io.Closeable; +import java.io.IOException; +import java.util.Objects; + +/** + * @author kuangchengping@sinohealth.cn + * 2023-08-25 19:37 + */ +public class ResourceTool { + public static void close(Closeable... resources) throws IOException { + if (Objects.isNull(resources)) { + return; + } + for (Closeable closeable : resources) { + if (Objects.isNull(closeable)) { + continue; + } + closeable.close(); + } + } +} diff --git a/network/src/main/java/com/github/kuangcp/io/ResourceTool.java b/network/src/main/java/com/github/kuangcp/io/ResourceTool.java new file mode 100644 index 00000000..6b7c3485 --- /dev/null +++ b/network/src/main/java/com/github/kuangcp/io/ResourceTool.java @@ -0,0 +1,23 @@ +package com.github.kuangcp.io; + +import java.io.Closeable; +import java.io.IOException; +import java.util.Objects; + +/** + * @author kuangchengping@sinohealth.cn + * 2023-08-25 19:37 + */ +public class ResourceTool { + public static void close(Closeable... resources) throws IOException { + if (Objects.isNull(resources)) { + return; + } + for (Closeable closeable : resources) { + if (Objects.isNull(closeable)) { + continue; + } + closeable.close(); + } + } +} diff --git a/pom.xml b/pom.xml index 6d04e53e..6fd0a24f 100644 --- a/pom.xml +++ b/pom.xml @@ -21,11 +21,11 @@ concurrency generic guava - - + + io java8 - - + netty + network pattern spring @@ -46,6 +46,10 @@ ch.qos.logback logback-classic + + junit + junit + @@ -62,24 +66,24 @@ ch.qos.logback logback-classic - 1.4.11 + 1.2.5 - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + org.apache.commons commons-lang3 @@ -330,11 +334,7 @@ 2.2.6 - - - - - + asm asm diff --git a/question/pom.xml b/question/pom.xml index b146c563..05d89058 100644 --- a/question/pom.xml +++ b/question/pom.xml @@ -19,6 +19,44 @@ + + org.testng + testng + test + + + asm + asm + + + org.ow2.asm + asm + + + org.springframework + spring-core + + + org.apache.commons + commons-lang3 + + + org.apache.commons + commons-collections4 + + + com.google.code.gson + gson + + + cglib + cglib + + + cglib + cglib + 3.2.4 + From 22e2679debea56b73d06c71ff0813ddf19f8d336 Mon Sep 17 00:00:00 2001 From: kuangcp Date: Fri, 25 Aug 2023 19:59:29 +0800 Subject: [PATCH 273/476] fix: compile --- .../java/com/github/kuangcp/simple/Score.java | 70 +++++++++---------- guava/pom.xml | 4 ++ pom.xml | 10 +++ 3 files changed, 49 insertions(+), 35 deletions(-) diff --git a/generic/src/test/java/com/github/kuangcp/simple/Score.java b/generic/src/test/java/com/github/kuangcp/simple/Score.java index fb0ee473..29b0c330 100644 --- a/generic/src/test/java/com/github/kuangcp/simple/Score.java +++ b/generic/src/test/java/com/github/kuangcp/simple/Score.java @@ -11,45 +11,45 @@ public class Score implements Comparable, Serializable { - private Float normal; // 平时成绩 - private Float terminal;// 期末成绩 - - /** - * 这个方法一般是 -1 0 1: 本对象 小于 等于 大于 参数对象 或者 负数 0 正数 - * - * @param score 要比较的对象 同类之间的比较 - * @return int类型 - */ - @Override - public int compareTo(Score score) { - double temp = this.normal * 0.3 + this.terminal * 0.7; - double target = score.normal * 0.3 + score.terminal * 0.7; - if (temp == target) { - return 0; - } else if (temp > target) { - return 1; + private Float normal; // 平时成绩 + private Float terminal;// 期末成绩 + + /** + * 这个方法一般是 -1 0 1: 本对象 小于 等于 大于 参数对象 或者 负数 0 正数 + * + * @param score 要比较的对象 同类之间的比较 + * @return int类型 + */ + @Override + public int compareTo(Score score) { + double temp = this.normal * 0.3 + this.terminal * 0.7; + double target = score.normal * 0.3 + score.terminal * 0.7; + if (temp == target) { + return 0; + } else if (temp > target) { + return 1; + } + return -1; } - return -1; - } - public Score(Float normal, Float terminal) { - this.normal = normal; - this.terminal = terminal; - } + public Score(Float normal, Float terminal) { + this.normal = normal; + this.terminal = terminal; + } - public Float getNormal() { - return normal; - } + public Float getNormal() { + return normal; + } - public void setNormal(Float normal) { - this.normal = normal; - } + public void setNormal(Float normal) { + this.normal = normal; + } - public Float getTerminal() { - return terminal; - } + public Float getTerminal() { + return terminal; + } - public void setTerminal(Float terminal) { - this.terminal = terminal; - } + public void setTerminal(Float terminal) { + this.terminal = terminal; + } } diff --git a/guava/pom.xml b/guava/pom.xml index 4d79b65b..0504c8e0 100644 --- a/guava/pom.xml +++ b/guava/pom.xml @@ -19,6 +19,10 @@ + + com.google.guava + guava + diff --git a/pom.xml b/pom.xml index 6fd0a24f..8958f2ac 100644 --- a/pom.xml +++ b/pom.xml @@ -50,6 +50,16 @@ junit junit + + org.hamcrest + hamcrest-core + 1.3 + + + org.hamcrest + hamcrest-library + 1.3 + From 2c1fcf93d8365498ddea5242ef036bd3a01c7d39 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Sat, 26 Aug 2023 13:48:48 +0800 Subject: [PATCH 274/476] cache --- .../main/java/jvm/gc/g1/DesignRegionSize.java | 7 + .../java/jvm/gc/g1/StringDeduplication.java | 18 +- .../src/test/java/time/LocalDateTimeTest.java | 55 +++-- class/src/test/java/time/Readme.md | 11 + .../java/guava/ratelimit/RateLimiterTest.java | 35 +++ .../netty/websocket/NioWebSocketHandler.java | 199 +++++++++--------- .../java/netty/timeServer/TimeClientTest.java | 121 ++++++----- 7 files changed, 259 insertions(+), 187 deletions(-) create mode 100644 class/src/main/java/jvm/gc/g1/DesignRegionSize.java create mode 100644 guava/src/test/java/guava/ratelimit/RateLimiterTest.java diff --git a/class/src/main/java/jvm/gc/g1/DesignRegionSize.java b/class/src/main/java/jvm/gc/g1/DesignRegionSize.java new file mode 100644 index 00000000..131a809c --- /dev/null +++ b/class/src/main/java/jvm/gc/g1/DesignRegionSize.java @@ -0,0 +1,7 @@ +package jvm.gc.g1; + +/** + * @author https://github.com/kuangcp on 2021-08-30 03:33 + */ +public class DesignRegionSize { +} diff --git a/class/src/main/java/jvm/gc/g1/StringDeduplication.java b/class/src/main/java/jvm/gc/g1/StringDeduplication.java index d3da530f..6be094cb 100644 --- a/class/src/main/java/jvm/gc/g1/StringDeduplication.java +++ b/class/src/main/java/jvm/gc/g1/StringDeduplication.java @@ -4,18 +4,18 @@ /** * @author https://github.com/kuangcp on 2021-05-16 23:36 - * + *

* -Xms15m -Xmx15m -XX:+PrintGCDetails -XX:+UseG1GC -XX:+PrintStringTableStatistics -XX:+UseStringDeduplication -XX:+PrintStringDeduplicationStatistics */ public class StringDeduplication { - // TODO 如何设计用例 体现去重特性 - public static void main(String[] args) throws InterruptedException { - for (int j = 0; j < 100; j++) { - for (int i = 0; i < 1000; i++) { - String.valueOf(i).intern(); - } - TimeUnit.MILLISECONDS.sleep(30); + // TODO 如何设计用例 体现去重特性 + public static void main(String[] args) throws InterruptedException { + for (int j = 0; j < 100; j++) { + for (int i = 0; i < 1000; i++) { + String.valueOf(i).intern(); + } + TimeUnit.MILLISECONDS.sleep(30); + } } - } } diff --git a/class/src/test/java/time/LocalDateTimeTest.java b/class/src/test/java/time/LocalDateTimeTest.java index 7e59ee41..ed564704 100644 --- a/class/src/test/java/time/LocalDateTimeTest.java +++ b/class/src/test/java/time/LocalDateTimeTest.java @@ -1,46 +1,55 @@ package time; -import static org.hamcrest.Matchers.lessThan; -import static org.junit.Assert.assertThat; +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; import java.time.LocalDateTime; import java.time.ZoneOffset; -import lombok.extern.slf4j.Slf4j; -import org.junit.Test; +import java.util.Date; + +import static org.hamcrest.Matchers.lessThan; +import static org.junit.Assert.assertThat; /** * @author kuangcp on 18-9-11-下午3:52 * LocalDateTime 本身是和时区没有关联的, * 如果是采用 now() 得到LocalDateTime的实例 就会采用操作系统默认时区 * 如果是采用 of() 得到LocalDateTime的实例 就不会和时区有关系, 采用默认的UTC - * + *

* LocalDateTime 就是 主要由 LocalDate LocalTime 组成的对象, 唯一的私有构造器就说明了这一点 */ @Slf4j public class LocalDateTimeTest { - @Test - public void testNow() { - // 这里默认使用了当前时区 计算得到时间 - LocalDateTime now = LocalDateTime.now(); + @Test + public void testNow() { + // 这里默认使用了当前时区 计算得到时间 + LocalDateTime now = LocalDateTime.now(); + + // 这里将时区的影响消除 + long milli = now.atZone(ZoneOffset.systemDefault()).toInstant().toEpochMilli(); + + log.info(": milli={}", milli); + log.info(": milli={}", System.currentTimeMillis()); - // 这里将时区的影响消除 - long milli = now.atZone(ZoneOffset.systemDefault()).toInstant().toEpochMilli(); + // 这个差就是以上转换消耗的时间 + assertThat(System.currentTimeMillis() - milli, lessThan(10L)); + } - log.info(": milli={}", milli); - log.info(": milli={}", System.currentTimeMillis()); + @Test + public void testOf() { + LocalDateTime of = LocalDateTime.of(2018, 1, 23, 22, 22); + log.info(": of={}", of.toString()); + String instant = of.atZone(ZoneOffset.systemDefault()).toInstant().toString(); + log.info(": instant={}", instant); + } - // 这个差就是以上转换消耗的时间 - assertThat(System.currentTimeMillis() - milli, lessThan(10L)); - } + @Test + public void testDate() throws Exception { + final Date date = new Date(585327600000L); + System.out.println(date); - @Test - public void testOf() { - LocalDateTime of = LocalDateTime.of(2018, 1, 23, 22, 22); - log.info(": of={}", of.toString()); - String instant = of.atZone(ZoneOffset.systemDefault()).toInstant().toString(); - log.info(": instant={}", instant); - } + } } diff --git a/class/src/test/java/time/Readme.md b/class/src/test/java/time/Readme.md index f01516f2..a59c0a6f 100644 --- a/class/src/test/java/time/Readme.md +++ b/class/src/test/java/time/Readme.md @@ -1,5 +1,16 @@ # Java8 中有关时间的类 +Java中时间戳为0时表示的时间是1970-01-01 00:00:00 由于 Date类是和时区有关的,所以实例化的时候会带上时区信息,格式化输出也会带上时区 +但是System.currentTimeMillis()就是时区无关的时间戳。 +以及Java8新增的LocalDateTime等类of()方式实例化都是不带时区信息,但是now()方式实例化不一样,会依据当前时间戳以及时区信息转换得到相对时间戳 + +```java + System.out.println(new Date(0)); // Thu Jan 01 08:00:00 CST 1970 +``` + +- 时区有关 Date +- 时区无关 LocalDateTime LocalDate LocalTime + - java.time 包下 ``` diff --git a/guava/src/test/java/guava/ratelimit/RateLimiterTest.java b/guava/src/test/java/guava/ratelimit/RateLimiterTest.java new file mode 100644 index 00000000..7235dace --- /dev/null +++ b/guava/src/test/java/guava/ratelimit/RateLimiterTest.java @@ -0,0 +1,35 @@ +package guava.ratelimit; + +import com.google.common.util.concurrent.RateLimiter; +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; + +import java.util.concurrent.TimeUnit; + +/** + * @author Kuangcp on 2021-08-30 03:00 + */ +@Slf4j +public class RateLimiterTest { + + @Test + public void testLimit1() throws Exception { + final RateLimiter rateLimiter = RateLimiter.create(3, 5, TimeUnit.SECONDS); + rateLimiter.acquire(); + + // SmoothRateLimiter + new Thread(() -> { + for (int i = 0; i < 1000; i++) { + try { + TimeUnit.MILLISECONDS.sleep(100); + rateLimiter.acquire(); + log.info("run"); + } catch (InterruptedException e) { + log.error("", e); + } + } + }).start(); + + TimeUnit.HOURS.sleep(1); + } +} diff --git a/netty/src/main/java/netty/websocket/NioWebSocketHandler.java b/netty/src/main/java/netty/websocket/NioWebSocketHandler.java index 508a7cfc..35cf6b74 100644 --- a/netty/src/main/java/netty/websocket/NioWebSocketHandler.java +++ b/netty/src/main/java/netty/websocket/NioWebSocketHandler.java @@ -7,130 +7,121 @@ import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; -import io.netty.handler.codec.http.DefaultFullHttpResponse; -import io.netty.handler.codec.http.FullHttpRequest; -import io.netty.handler.codec.http.HttpResponseStatus; -import io.netty.handler.codec.http.HttpUtil; -import io.netty.handler.codec.http.HttpVersion; -import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame; -import io.netty.handler.codec.http.websocketx.PingWebSocketFrame; -import io.netty.handler.codec.http.websocketx.PongWebSocketFrame; -import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; -import io.netty.handler.codec.http.websocketx.WebSocketFrame; -import io.netty.handler.codec.http.websocketx.WebSocketServerHandshaker; -import io.netty.handler.codec.http.websocketx.WebSocketServerHandshakerFactory; +import io.netty.handler.codec.http.*; +import io.netty.handler.codec.http.websocketx.*; import io.netty.util.CharsetUtil; -import java.util.Date; import lombok.extern.slf4j.Slf4j; +import java.util.Date; + /** * @author https://github.com/kuangcp on 2021-05-18 08:33 */ @Slf4j public class NioWebSocketHandler extends SimpleChannelInboundHandler { - private WebSocketServerHandshaker handshaker; - - @Override - protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception { - log.debug("收到消息:" + msg); - if (msg instanceof FullHttpRequest) { - //以http请求形式接入,但是走的是websocket - handleHttpRequest(ctx, (FullHttpRequest) msg); - } else if (msg instanceof WebSocketFrame) { - //处理websocket客户端的消息 - handlerWebSocketFrame(ctx, (WebSocketFrame) msg); - } - } - - @Override - public void channelActive(ChannelHandlerContext ctx) throws Exception { - log.debug("客户端加入连接:" + ctx.channel()); - ChannelSupervise.addChannel(ctx.channel()); - } - - @Override - public void channelInactive(ChannelHandlerContext ctx) throws Exception { - log.debug("客户端断开连接:" + ctx.channel()); - ChannelSupervise.removeChannel(ctx.channel()); - } - - @Override - public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { - ctx.flush(); - } - - private void handlerWebSocketFrame(ChannelHandlerContext ctx, WebSocketFrame frame) { - // 判断是否关闭链路的指令 - if (frame instanceof CloseWebSocketFrame) { - handshaker.close(ctx.channel(), (CloseWebSocketFrame) frame.retain()); - return; + private WebSocketServerHandshaker handshaker; + + @Override + protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception { + log.debug("收到消息:" + msg); + if (msg instanceof FullHttpRequest) { + //以http请求形式接入,但是走的是websocket + handleHttpRequest(ctx, (FullHttpRequest) msg); + } else if (msg instanceof WebSocketFrame) { + //处理websocket客户端的消息 + handlerWebSocketFrame(ctx, (WebSocketFrame) msg); + } } - // 判断是否ping消息 - if (frame instanceof PingWebSocketFrame) { - ctx.channel().write(new PongWebSocketFrame(frame.content().retain())); - return; + @Override + public void channelActive(ChannelHandlerContext ctx) throws Exception { + log.debug("客户端加入连接:" + ctx.channel()); + ChannelSupervise.addChannel(ctx.channel()); } - // 本例程仅支持文本消息,不支持二进制消息 - if (!(frame instanceof TextWebSocketFrame)) { - log.debug("本例程仅支持文本消息,不支持二进制消息"); - throw new UnsupportedOperationException(String.format( - "%s frame types not supported", frame.getClass().getName())); + @Override + public void channelInactive(ChannelHandlerContext ctx) throws Exception { + log.debug("客户端断开连接:" + ctx.channel()); + ChannelSupervise.removeChannel(ctx.channel()); } - // 返回应答消息 - String request = ((TextWebSocketFrame) frame).text(); - log.debug("服务端收到:" + request); - TextWebSocketFrame tws = new TextWebSocketFrame( - new Date().toString() + ctx.channel().id() + ":" + request); + @Override + public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { + ctx.flush(); + } - // 群发至所有连接 + private void handlerWebSocketFrame(ChannelHandlerContext ctx, WebSocketFrame frame) { + // 判断是否关闭链路的指令 + if (frame instanceof CloseWebSocketFrame) { + handshaker.close(ctx.channel(), (CloseWebSocketFrame) frame.retain()); + return; + } + + // 判断是否ping消息 + if (frame instanceof PingWebSocketFrame) { + ctx.channel().write(new PongWebSocketFrame(frame.content().retain())); + return; + } + + // 本例程仅支持文本消息,不支持二进制消息 + if (!(frame instanceof TextWebSocketFrame)) { + log.debug("本例程仅支持文本消息,不支持二进制消息"); + throw new UnsupportedOperationException(String.format( + "%s frame types not supported", frame.getClass().getName())); + } + + // 返回应答消息 + String request = ((TextWebSocketFrame) frame).text(); + log.info("服务端收到:final:{} txt:{}", frame.isFinalFragment(), request.length()); + TextWebSocketFrame tws = new TextWebSocketFrame( + new Date().toString() + ctx.channel().id() + ":" + request); + + // 群发至所有连接 // ChannelSupervise.send2All(tws); - // 返回【谁发的发给谁】 - ctx.channel().writeAndFlush(tws); - } - - /** - * 唯一的一次http请求,用于创建websocket - */ - private void handleHttpRequest(ChannelHandlerContext ctx, FullHttpRequest req) { - //要求Upgrade为websocket,过滤掉get/Post - if (!req.decoderResult().isSuccess() - || (!"websocket".equals(req.headers().get("Upgrade")))) { - //若不是websocket方式,则创建BAD_REQUEST的req,返回给客户端 - sendHttpResponse(ctx, req, new DefaultFullHttpResponse( - HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST)); - return; + // 返回【谁发的发给谁】 + ctx.channel().writeAndFlush(tws); } - WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory( - "ws://localhost:7094/ws", null, false); - handshaker = wsFactory.newHandshaker(req); - if (handshaker == null) { - WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel()); - } else { - handshaker.handshake(ctx.channel(), req); - } - } - - /** - * 拒绝不合法的请求,并返回错误信息 - */ - private static void sendHttpResponse(ChannelHandlerContext ctx, - FullHttpRequest req, DefaultFullHttpResponse res) { - // 返回应答给客户端 - if (res.status().code() != 200) { - ByteBuf buf = Unpooled.copiedBuffer(res.status().toString(), CharsetUtil.UTF_8); - res.content().writeBytes(buf); - buf.release(); + /** + * 唯一的一次http请求,用于创建websocket + */ + private void handleHttpRequest(ChannelHandlerContext ctx, FullHttpRequest req) { + //要求Upgrade为websocket,过滤掉get/Post + if (!req.decoderResult().isSuccess() + || (!"websocket".equals(req.headers().get("Upgrade")))) { + //若不是websocket方式,则创建BAD_REQUEST的req,返回给客户端 + sendHttpResponse(ctx, req, new DefaultFullHttpResponse( + HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST)); + return; + } + + WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory( + "ws://localhost:7094/ws", null, false); + handshaker = wsFactory.newHandshaker(req); + if (handshaker == null) { + WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel()); + } else { + handshaker.handshake(ctx.channel(), req); + } } - ChannelFuture f = ctx.channel().writeAndFlush(res); - // 如果是非Keep-Alive,关闭连接 - if (!HttpUtil.isKeepAlive(req) || res.status().code() != 200) { - f.addListener(ChannelFutureListener.CLOSE); + + /** + * 拒绝不合法的请求,并返回错误信息 + */ + private static void sendHttpResponse(ChannelHandlerContext ctx, + FullHttpRequest req, DefaultFullHttpResponse res) { + // 返回应答给客户端 + if (res.status().code() != 200) { + ByteBuf buf = Unpooled.copiedBuffer(res.status().toString(), CharsetUtil.UTF_8); + res.content().writeBytes(buf); + buf.release(); + } + ChannelFuture f = ctx.channel().writeAndFlush(res); + // 如果是非Keep-Alive,关闭连接 + if (!HttpUtil.isKeepAlive(req) || res.status().code() != 200) { + f.addListener(ChannelFutureListener.CLOSE); + } } - } } diff --git a/netty/src/test/java/netty/timeServer/TimeClientTest.java b/netty/src/test/java/netty/timeServer/TimeClientTest.java index 24973468..20bd4244 100644 --- a/netty/src/test/java/netty/timeServer/TimeClientTest.java +++ b/netty/src/test/java/netty/timeServer/TimeClientTest.java @@ -1,10 +1,11 @@ package netty.timeServer; +import lombok.extern.slf4j.Slf4j; +import org.testng.annotations.Test; + import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -import lombok.extern.slf4j.Slf4j; -import org.testng.annotations.Test; /** * @author https://github.com/kuangcp on 2020-08-22 14:28 @@ -12,66 +13,84 @@ @Slf4j public class TimeClientTest { - private final TimeClient timeClient = new TimeClient(); - - // @Test - @Test(threadPoolSize = 5, invocationCount = 20) - public void testClient() throws Exception { - startClient(); - timeClient.sendMsg(Command.QUERY_TIME); - } - - @Test - public void testConcurrencyClient() throws Exception { - ExecutorService pool = Executors.newFixedThreadPool(2); - - startClient(); - - int num = 100; - CountDownLatch countDownLatch = new CountDownLatch(num); - for (int i = 0; i < num; i++) { - pool.submit(() -> { - try { - timeClient.sendMsg(Command.QUERY_TIME); - } catch (Exception e) { - log.error("", e); - } finally { - countDownLatch.countDown(); - } - }); + private final TimeClient timeClient = new TimeClient(); + + // @Test + @Test(threadPoolSize = 5, invocationCount = 20) + public void testClient() throws Exception { + startClient(); + timeClient.sendMsg(Command.QUERY_TIME); } - countDownLatch.await(); + @Test + public void testConcurrencyClient() throws Exception { + ExecutorService pool = Executors.newFixedThreadPool(2); + + startClient(); + + int num = 100; + CountDownLatch countDownLatch = new CountDownLatch(num); + for (int i = 0; i < num; i++) { + pool.submit(() -> { + try { + timeClient.sendMsg(Command.QUERY_TIME); + } catch (Exception e) { + log.error("", e); + } finally { + countDownLatch.countDown(); + } + }); + } - // why not flush cache - timeClient.flush(); - Thread.sleep(4000); + countDownLatch.await(); -// timeClient.sendMsg(Command.STOP_SERVER); - timeClient.stop(); - } + // why not flush cache + timeClient.flush(); + Thread.sleep(4000); - void startClient() throws InterruptedException { - if (timeClient.isReady()) { - return; +// timeClient.sendMsg(Command.STOP_SERVER); + timeClient.stop(); } - Thread thread = new Thread(() -> { - try { - timeClient.connectLocal(TimeServer.port); - } catch (Exception e) { - log.error("", e); - } - }); + void startClient() throws InterruptedException { + if (timeClient.isReady()) { + return; + } + + Thread thread = new Thread(() -> { + try { + timeClient.connectLocal(TimeServer.port); + } catch (Exception e) { + log.error("", e); + } + }); - thread.start(); + thread.start(); - // 当循环体为空时会陷入死循环,即使已经准备好也没有退出 - while (!timeClient.isReady()) { + // 当循环体为空时会陷入死循环,即使已经准备好也没有退出 + while (!timeClient.isReady()) { // Thread.sleep(10); + } + log.info("client ready"); } - log.info("client ready"); - } + @Test + public void testXX() throws Exception { + System.out.println(0x80); + } + + @Test + public void testNPE() throws Exception { + + for (int i = 0; i < 6000; i++) { + try { + String s = null; + s.charAt(3); + } catch (Exception e) { + log.error("", e); + } + } + Thread.sleep(2000); + } } From a3f6aaccdbba73b41e9b68a6e04da391547e4f5f Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Sat, 26 Aug 2023 15:24:24 +0800 Subject: [PATCH 275/476] oom: metaspace arthas --- class/src/main/java/jvm/oom/MetaspaceOOM.java | 68 +++++ class/src/main/java/security/AES.java | 276 +++++++++--------- .../test/java/jvm/oom/MetaspaceOOMTest.java | 35 +++ class/src/test/resources/logback.xml | 61 ++-- 4 files changed, 271 insertions(+), 169 deletions(-) create mode 100644 class/src/test/java/jvm/oom/MetaspaceOOMTest.java diff --git a/class/src/main/java/jvm/oom/MetaspaceOOM.java b/class/src/main/java/jvm/oom/MetaspaceOOM.java index 605c14bd..de632f96 100644 --- a/class/src/main/java/jvm/oom/MetaspaceOOM.java +++ b/class/src/main/java/jvm/oom/MetaspaceOOM.java @@ -1,8 +1,76 @@ package jvm.oom; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; + +import java.lang.reflect.Method; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.TimeUnit; + /** * @author kuangcp * 2023-08-25 17:24 */ +@Slf4j public class MetaspaceOOM { + + @Getter + private static class Bucket { + private int a; + private int b; + private String c; + private int d; + private String f0; + private String f1; + private String f2; + private String f3; + private String f4; + private String f5; + private String f6; + private String f7; + private String f8; + private String f9; + private String f10; + private String f11; + private String f12; + private String f13; + private String f14; + private String f15; + private String f16; + private String f17; + private String f18; + private String f19; + private String f20; + private String f21; + private String f22; + private String f23; + private String f24; + private String f25; + private String f26; + private String f27; + private String f28; + private String f29; + public List> buildMap() { + return Collections.emptyList(); + } + } + + public static void jacksonAndReflect() throws Exception { + final Bucket bucket = new Bucket(); + for (int j = 0; j < 100; j++) { + log.info("run loop {}", j); + TimeUnit.MILLISECONDS.sleep(350); + for (int i = 0; i < 1000; i++) { + final ObjectMapper mapper = new ObjectMapper(); + final Method buildMap = Bucket.class.getMethod("buildMap"); + + buildMap.invoke(bucket); + final String json = mapper.writeValueAsString(bucket); + mapper.readValue(json, Bucket.class); + } + } + } + } diff --git a/class/src/main/java/security/AES.java b/class/src/main/java/security/AES.java index fcd925c5..d9f35993 100644 --- a/class/src/main/java/security/AES.java +++ b/class/src/main/java/security/AES.java @@ -1,150 +1,150 @@ package security; -import java.math.BigInteger; -import java.nio.charset.StandardCharsets; -import java.util.Base64; -import javax.crypto.Cipher; -import javax.crypto.KeyGenerator; -import javax.crypto.spec.SecretKeySpec; - import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import sun.misc.BASE64Decoder; +import javax.crypto.Cipher; +import javax.crypto.KeyGenerator; +import javax.crypto.spec.SecretKeySpec; +import java.math.BigInteger; +import java.nio.charset.StandardCharsets; +import java.util.Base64; + @Slf4j public class AES { - //密钥 (需要前端和后端保持一致) - private static final String KEY = "qwertyuiqwertyui"; - //算法 - private static final String ALGORITHMSTR = "AES/CTR/PKCS5Padding"; - - /** - * aes解密 - * - * @param encrypt 内容 - */ - public static String aesDecrypt(String encrypt) { - try { - return aesDecrypt(encrypt, KEY); - } catch (Exception e) { - log.error("", e); - return ""; + //密钥 (需要前端和后端保持一致) + private static final String KEY = "qwertyuiqwertyui"; + //算法 + private static final String ALGORITHMSTR = "AES/CTR/PKCS5Padding"; + + /** + * aes解密 + * + * @param encrypt 内容 + */ + public static String aesDecrypt(String encrypt) { + try { + return aesDecrypt(encrypt, KEY); + } catch (Exception e) { + log.error("", e); + return ""; + } + } + + /** + * aes加密 + */ + public static String aesEncrypt(String content) { + try { + return aesEncrypt(content, KEY); + } catch (Exception e) { + log.error("", e); + return ""; + } + } + + /** + * 将byte[]转为各种进制的字符串 + * + * @param bytes byte[] + * @param radix 可以转换进制的范围,从Character.MIN_RADIX到Character.MAX_RADIX,超出范围后变为10进制 + * @return 转换后的字符串 + */ + public static String binary(byte[] bytes, int radix) { + return new BigInteger(1, bytes).toString(radix);// 这里的1代表正数 + } + + /** + * base 64 encode + * + * @param bytes 待编码的byte[] + * @return 编码后的base 64 code + */ + public static String base64Encode(byte[] bytes) { + return Base64.getEncoder().encodeToString(bytes); + } + + /** + * base 64 decode + * + * @param base64Code 待解码的base 64 code + * @return 解码后的byte[] + */ + public static byte[] base64Decode(String base64Code) throws Exception { + return StringUtils.isEmpty(base64Code) ? null : new BASE64Decoder().decodeBuffer(base64Code); } - } - - /** - * aes加密 - */ - public static String aesEncrypt(String content) { - try { - return aesEncrypt(content, KEY); - } catch (Exception e) { - e.printStackTrace(); - return ""; + + + /** + * AES加密 + * + * @param content 待加密的内容 + * @param encryptKey 加密密钥 + * @return 加密后的byte[] + */ + public static byte[] aesEncryptToBytes(String content, String encryptKey) throws Exception { + KeyGenerator kgen = KeyGenerator.getInstance("AES"); + kgen.init(128); + Cipher cipher = Cipher.getInstance(ALGORITHMSTR); + cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(encryptKey.getBytes(), "AES")); + + return cipher.doFinal(content.getBytes(StandardCharsets.UTF_8)); + } + + + /** + * AES加密为base 64 code + * + * @param content 待加密的内容 + * @param encryptKey 加密密钥 + * @return 加密后的base 64 code + */ + public static String aesEncrypt(String content, String encryptKey) throws Exception { + return base64Encode(aesEncryptToBytes(content, encryptKey)); + } + + /** + * AES解密 + * + * @param encryptBytes 待解密的byte[] + * @param decryptKey 解密密钥 + * @return 解密后的String + */ + public static String aesDecryptByBytes(byte[] encryptBytes, String decryptKey) throws Exception { + KeyGenerator kgen = KeyGenerator.getInstance("AES"); + kgen.init(128); + + Cipher cipher = Cipher.getInstance(ALGORITHMSTR); + cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(decryptKey.getBytes(), "AES")); + byte[] decryptBytes = cipher.doFinal(encryptBytes); + return new String(decryptBytes); + } + + + /** + * 将base 64 code AES解密 + * + * @param encryptStr 待解密的base 64 code + * @param decryptKey 解密密钥 + * @return 解密后的string + */ + public static String aesDecrypt(String encryptStr, String decryptKey) throws Exception { + return StringUtils.isEmpty(encryptStr) ? null + : aesDecryptByBytes(base64Decode(encryptStr), decryptKey); + } + + /** + * 测试 + */ + public static void main(String[] args) throws Exception { + String content = "123"; + System.out.println("加密前:" + content); + System.out.println("加密密钥和解密密钥:" + KEY); + String encrypt = aesEncrypt(content, KEY); + System.out.println("加密后:" + encrypt); + String decrypt = aesDecrypt(encrypt, KEY); + System.out.println("解密后:" + decrypt); } - } - - /** - * 将byte[]转为各种进制的字符串 - * - * @param bytes byte[] - * @param radix 可以转换进制的范围,从Character.MIN_RADIX到Character.MAX_RADIX,超出范围后变为10进制 - * @return 转换后的字符串 - */ - public static String binary(byte[] bytes, int radix) { - return new BigInteger(1, bytes).toString(radix);// 这里的1代表正数 - } - - /** - * base 64 encode - * - * @param bytes 待编码的byte[] - * @return 编码后的base 64 code - */ - public static String base64Encode(byte[] bytes) { - return Base64.getEncoder().encodeToString(bytes); - } - - /** - * base 64 decode - * - * @param base64Code 待解码的base 64 code - * @return 解码后的byte[] - */ - public static byte[] base64Decode(String base64Code) throws Exception { - return StringUtils.isEmpty(base64Code) ? null : new BASE64Decoder().decodeBuffer(base64Code); - } - - - /** - * AES加密 - * - * @param content 待加密的内容 - * @param encryptKey 加密密钥 - * @return 加密后的byte[] - */ - public static byte[] aesEncryptToBytes(String content, String encryptKey) throws Exception { - KeyGenerator kgen = KeyGenerator.getInstance("AES"); - kgen.init(128); - Cipher cipher = Cipher.getInstance(ALGORITHMSTR); - cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(encryptKey.getBytes(), "AES")); - - return cipher.doFinal(content.getBytes(StandardCharsets.UTF_8)); - } - - - /** - * AES加密为base 64 code - * - * @param content 待加密的内容 - * @param encryptKey 加密密钥 - * @return 加密后的base 64 code - */ - public static String aesEncrypt(String content, String encryptKey) throws Exception { - return base64Encode(aesEncryptToBytes(content, encryptKey)); - } - - /** - * AES解密 - * - * @param encryptBytes 待解密的byte[] - * @param decryptKey 解密密钥 - * @return 解密后的String - */ - public static String aesDecryptByBytes(byte[] encryptBytes, String decryptKey) throws Exception { - KeyGenerator kgen = KeyGenerator.getInstance("AES"); - kgen.init(128); - - Cipher cipher = Cipher.getInstance(ALGORITHMSTR); - cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(decryptKey.getBytes(), "AES")); - byte[] decryptBytes = cipher.doFinal(encryptBytes); - return new String(decryptBytes); - } - - - /** - * 将base 64 code AES解密 - * - * @param encryptStr 待解密的base 64 code - * @param decryptKey 解密密钥 - * @return 解密后的string - */ - public static String aesDecrypt(String encryptStr, String decryptKey) throws Exception { - return StringUtils.isEmpty(encryptStr) ? null - : aesDecryptByBytes(base64Decode(encryptStr), decryptKey); - } - - /** - * 测试 - */ - public static void main(String[] args) throws Exception { - String content = "123"; - System.out.println("加密前:" + content); - System.out.println("加密密钥和解密密钥:" + KEY); - String encrypt = aesEncrypt(content, KEY); - System.out.println("加密后:" + encrypt); - String decrypt = aesDecrypt(encrypt, KEY); - System.out.println("解密后:" + decrypt); - } } \ No newline at end of file diff --git a/class/src/test/java/jvm/oom/MetaspaceOOMTest.java b/class/src/test/java/jvm/oom/MetaspaceOOMTest.java new file mode 100644 index 00000000..2c00de8d --- /dev/null +++ b/class/src/test/java/jvm/oom/MetaspaceOOMTest.java @@ -0,0 +1,35 @@ +package jvm.oom; + +import org.junit.Test; + +import java.util.concurrent.TimeUnit; + +/** + * @author Kuangcp on 2023-08-26 14:36 + */ +public class MetaspaceOOMTest { + + /** + * 测试Arthas注入对元空间内存的占用情况 + *

+ * -XX:MaxMetaspaceSize=25m -Xmx500m + * 步骤: + * 1. 启动单元测试 + * 2. Arthas注入 + * 3. visualvm 注入 + */ + @Test + public void testArthasInject() throws Exception { + // 睡眠是为了运行 arthas 和 visualvm + + // arthas 注入后 20M,注入前是770K左右 + TimeUnit.SECONDS.sleep(20); + + // 开始反射和JSON序列化后,迅速OOM + // 该方法执行需要15M左右 + MetaspaceOOM.jacksonAndReflect(); + + // 如果是等业务方法执行完后注入,arthas会注入失败,提示OOM + TimeUnit.HOURS.sleep(1); + } +} \ No newline at end of file diff --git a/class/src/test/resources/logback.xml b/class/src/test/resources/logback.xml index 7f94edc7..fd5bcc81 100644 --- a/class/src/test/resources/logback.xml +++ b/class/src/test/resources/logback.xml @@ -1,37 +1,36 @@ - + - - - - - %d{HH:mm:ss.SSS} %highlight(%-5level) [%thread] %logger{30} - %highlight(%M):%yellow(%-3L) %msg%n - - - - DEBUG - - + + + + + %d{HH:mm:ss.SSS} %highlight(%-5level) [%thread] %logger{30}#%highlight(%M):%yellow(%-3L) %msg%n + + + + DEBUG + + - - - ${log.base}debug.log - true - - ${log.base}debug.%d{yyyy-MM-dd}.log - - - %d{MM-dd HH:mm:ss.SSS} |-%-5level |%thread| %logger{36}:%L - %msg%n - - - DEBUG - - + + + ${log.base}debug.log + true + + ${log.base}debug.%d{yyyy-MM-dd}.log + + + %d{MM-dd HH:mm:ss.SSS} |-%-5level |%thread| %logger{36}#%M:%L - %msg%n + + + DEBUG + + - - - - + + + + \ No newline at end of file From 9050e1f2a8f04d9147be806c931e264738d3d28c Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Sat, 26 Aug 2023 15:39:14 +0800 Subject: [PATCH 276/476] mvn: compile all --- class/pom.xml | 5 + .../github/kuangcp/read/ClassScannerTest.java | 93 ++--- .../reflects/InvokeWithInheritParamTest.java | 189 +++++----- .../reflects/ReflectPerformanceTest.java | 132 +++---- .../java/syntax/bit/BitOperatorsTest.java | 324 +++++++++--------- .../syntax/bit/StringPerformanceTest.java | 88 ++--- .../java/syntax/string/SplitDemoTest.java | 79 ++--- gui/pom.xml | 8 +- .../com/github/kuangcp/tank/domain/Brick.java | 30 +- .../com/github/kuangcp/tank/domain/Iron.java | 10 + pom.xml | 36 +- question/pom.xml | 5 + web/Readme.md | 5 + 13 files changed, 504 insertions(+), 500 deletions(-) create mode 100644 web/Readme.md diff --git a/class/pom.xml b/class/pom.xml index 4d40ef57..4f227d3c 100644 --- a/class/pom.xml +++ b/class/pom.xml @@ -25,6 +25,11 @@ cglib + + com.github.kuangcp + kcp-core + + org.apache.commons commons-lang3 diff --git a/class/src/test/java/com/github/kuangcp/read/ClassScannerTest.java b/class/src/test/java/com/github/kuangcp/read/ClassScannerTest.java index 35a600b0..0b643dfd 100644 --- a/class/src/test/java/com/github/kuangcp/read/ClassScannerTest.java +++ b/class/src/test/java/com/github/kuangcp/read/ClassScannerTest.java @@ -1,13 +1,14 @@ package com.github.kuangcp.read; -import com.github.kuangcp.io.ResourceTool; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.time.StopWatch; +import org.junit.Test; + import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Set; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.time.StopWatch; -import org.junit.Test; /** @@ -18,55 +19,55 @@ @Slf4j public class ClassScannerTest { - @Test - public void testGetPackageAllClasses() { - // 递归找到当前类所在包的所有 Base 后缀且继承了抽象逻辑类, 然后生成代码 - List list = new ArrayList<>(); - list.add("*Base"); - ClassScanner scanner = new ClassScanner(true, true, list); + @Test + public void testGetPackageAllClasses() { + // 递归找到当前类所在包的所有 Base 后缀且继承了抽象逻辑类, 然后生成代码 + List list = new ArrayList<>(); + list.add("*Base"); + ClassScanner scanner = new ClassScanner(true, true, list); - System.out.println(ClassScannerTest.class.getPackage().getName()); - Set> clazzSet = scanner - .getPackageAllClasses(ClassScannerTest.class.getPackage().getName(), true); + System.out.println(ClassScannerTest.class.getPackage().getName()); + Set> clazzSet = scanner + .getPackageAllClasses(ClassScannerTest.class.getPackage().getName(), true); - log.info("size: {}", clazzSet.size()); + log.info("size: {}", clazzSet.size()); - for (Class clazz : clazzSet) { - if (clazz != BaseFather.class && BaseFather.class.isAssignableFrom(clazz)) { // 继承自 BaseFather - String rawName = clazz.getSimpleName(); - String name = lowerCaseFirstLetter(rawName); - System.out.printf(" public final static %s %s = new %s();%n", rawName, name, rawName); - } + for (Class clazz : clazzSet) { + if (clazz != BaseFather.class && BaseFather.class.isAssignableFrom(clazz)) { // 继承自 BaseFather + String rawName = clazz.getSimpleName(); + String name = lowerCaseFirstLetter(rawName); + System.out.printf(" public final static %s %s = new %s();%n", rawName, name, rawName); + } + } } - } - @Test - public void testReadJar() { - StopWatch watch = new StopWatch(); - ClassScanner scanner = new ClassScanner(true, true, Collections.emptyList()); - String path = ResourceTool.class.getPackage().getName(); - log.info("path={}", path); + @Test + public void testReadJar() { + StopWatch watch = new StopWatch(); + ClassScanner scanner = new ClassScanner(true, true, Collections.emptyList()); + String path = ObjectMapper.class.getPackage().getName(); + log.info("path={}", path); - watch.start(); - Set> result = scanner.getPackageAllClasses(path, true); - log.info("result {}", result.size()); - result.forEach(System.out::println); - watch.stop(); - System.out.println(watch.toString()); + watch.start(); + Set> result = scanner.getPackageAllClasses(path, true); + log.info("result {}", result.size()); + result.forEach(System.out::println); + watch.stop(); + System.out.println(watch); - watch.reset(); - watch.start(); + watch.reset(); + watch.start(); - scanner = new ClassScanner(true, true, Collections.emptyList()); - result = scanner.getPackageAllClasses("com.google.gson", true); - log.info("result {}", result.size()); - result.forEach(System.out::println); - watch.stop(); - System.out.println(watch.toString()); - } + scanner = new ClassScanner(true, true, Collections.emptyList()); + result = scanner.getPackageAllClasses("com.google.gson", true); + log.info("result {}", result.size()); + result.forEach(System.out::println); + watch.stop(); + System.out.println(watch); + } - private static String lowerCaseFirstLetter(String name) { - return name.length() == 1 ? name.toLowerCase() - : name.substring(0, 1).toLowerCase() + name.substring(1); - } + private static String lowerCaseFirstLetter(String name) { + return name.length() == 1 ? name.toLowerCase() + : name.substring(0, 1).toLowerCase() + name.substring(1); + } } \ No newline at end of file diff --git a/class/src/test/java/com/github/kuangcp/reflects/InvokeWithInheritParamTest.java b/class/src/test/java/com/github/kuangcp/reflects/InvokeWithInheritParamTest.java index 38280e43..696e212c 100644 --- a/class/src/test/java/com/github/kuangcp/reflects/InvokeWithInheritParamTest.java +++ b/class/src/test/java/com/github/kuangcp/reflects/InvokeWithInheritParamTest.java @@ -1,20 +1,21 @@ package com.github.kuangcp.reflects; -import static org.hamcrest.core.IsEqual.equalTo; -import static org.junit.Assert.assertThat; - import com.github.kuangcp.time.GetRunTime; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.extern.slf4j.Slf4j; import org.junit.Ignore; import org.junit.Test; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import static org.hamcrest.core.IsEqual.equalTo; +import static org.junit.Assert.assertThat; + /** * Created by https://github.com/kuangcp - * + *

* 也就是说,可以让参数具有多态性, 但是在反射的时候 参数的类型是一一对应的, 不存在多态 * 当拿到了方法之后, invoke的时候按需放入参数就没有问题 * @@ -23,115 +24,115 @@ @Slf4j public class InvokeWithInheritParamTest { - @Data - class CommonParam { + @Data + class CommonParam { - int score; - } + int score; + } - @EqualsAndHashCode(callSuper = true) - @Data - class RunParam extends CommonParam { + @EqualsAndHashCode(callSuper = true) + @Data + class RunParam extends CommonParam { - String id; - } + String id; + } - class Logic { + class Logic { - boolean isError(RunParam param) { - return param.getScore() > 100; - } + boolean isError(RunParam param) { + return param.getScore() > 100; + } - boolean isFailed(CommonParam param) { - return param.getScore() < 60; + boolean isFailed(CommonParam param) { + return param.getScore() < 60; + } } - } - // 标准的反射方式 - @Test - public void testInvoke() { + // 标准的反射方式 + @Test + public void testInvoke() { - try { - // 这里不能像我设想的那样 根据 CommonParam 获取到 isError 方法 - Method method = Logic.class.getDeclaredMethod("isError", RunParam.class); + try { + // 这里不能像我设想的那样 根据 CommonParam 获取到 isError 方法 + Method method = Logic.class.getDeclaredMethod("isError", RunParam.class); - RunParam runParam = new RunParam(); - runParam.setScore(10); - verifyInvoke(method, runParam, false); + RunParam runParam = new RunParam(); + runParam.setScore(10); + verifyInvoke(method, runParam, false); - runParam.setScore(108); - verifyInvoke(method, runParam, true); - } catch (NoSuchMethodException e) { - e.printStackTrace(); + runParam.setScore(108); + verifyInvoke(method, runParam, true); + } catch (NoSuchMethodException e) { + e.printStackTrace(); + } } - } - - // 但是可以通过在方法上设计参数的多态, 完成共用 - @Test - public void testInvokeBySuper() { - try { - Method method = Logic.class.getDeclaredMethod("isFailed", CommonParam.class); - verifySuite(method); - } catch (NoSuchMethodException e) { - e.printStackTrace(); + + // 但是可以通过在方法上设计参数的多态, 完成共用 + @Test + public void testInvokeBySuper() { + try { + Method method = Logic.class.getDeclaredMethod("isFailed", CommonParam.class); + verifySuite(method); + } catch (NoSuchMethodException e) { + e.printStackTrace(); + } } - } - - /** - * 反射时, 其方法的参数不存在多态的概念 - */ - @Test - public void testInvokeTryTwice() throws NoSuchMethodException { - Method method; - try { - // 所以这里会必然报错 - method = Logic.class.getDeclaredMethod("isFailed", RunParam.class); - } catch (NoSuchMethodException e) { - method = Logic.class.getDeclaredMethod("isFailed", CommonParam.class); + + /** + * 反射时, 其方法的参数不存在多态的概念 + */ + @Test + public void testInvokeTryTwice() throws NoSuchMethodException { + Method method; + try { + // 所以这里会必然报错 + method = Logic.class.getDeclaredMethod("isFailed", RunParam.class); + } catch (NoSuchMethodException e) { + method = Logic.class.getDeclaredMethod("isFailed", CommonParam.class); + } + + verifySuite(method); } - verifySuite(method); - } + /** + * 经过测试发现 通过异常来控制逻辑, 耗时是直接反射 的 4-7 倍 + */ + @Test + @Ignore + public void comparePerformance() throws NoSuchMethodException { - /** - * 经过测试发现 通过异常来控制逻辑, 耗时是直接反射 的 4-7 倍 - */ - @Test - @Ignore - public void comparePerformance() throws NoSuchMethodException { + int concurrent = 10; - int concurrent = 10; + GetRunTime getRunTime = new GetRunTime().startCount(); - GetRunTime getRunTime = new GetRunTime().startCount(); + for (int i = 0; i < concurrent; i++) { + testInvokeTryTwice(); + } + getRunTime.endCount("twice "); - for (int i = 0; i < concurrent; i++) { - testInvokeTryTwice(); + getRunTime.startCount(); + for (int i = 0; i < concurrent; i++) { + testInvokeBySuper(); + } + getRunTime.endCount("correct "); } - getRunTime.endCount("twice "); - getRunTime.startCount(); - for (int i = 0; i < concurrent; i++) { - testInvokeBySuper(); + private void verifySuite(Method method) { + RunParam runParam = new RunParam(); + runParam.setScore(10); + verifyInvoke(method, runParam, true); + + runParam.setScore(108); + verifyInvoke(method, runParam, false); } - getRunTime.endCount("correct "); - } - - private void verifySuite(Method method) { - RunParam runParam = new RunParam(); - runParam.setScore(10); - verifyInvoke(method, runParam, true); - - runParam.setScore(108); - verifyInvoke(method, runParam, false); - } - - private void verifyInvoke(Method method, CommonParam param, boolean expect) { - boolean result = false; - try { - result = (boolean) method.invoke(new Logic(), param); - } catch (IllegalAccessException | InvocationTargetException e) { - e.printStackTrace(); + + private void verifyInvoke(Method method, CommonParam param, boolean expect) { + boolean result = false; + try { + result = (boolean) method.invoke(new Logic(), param); + } catch (IllegalAccessException | InvocationTargetException e) { + e.printStackTrace(); + } + assertThat(result, equalTo(expect)); } - assertThat(result, equalTo(expect)); - } } \ No newline at end of file diff --git a/class/src/test/java/com/github/kuangcp/reflects/ReflectPerformanceTest.java b/class/src/test/java/com/github/kuangcp/reflects/ReflectPerformanceTest.java index 561215df..71bc1e1f 100644 --- a/class/src/test/java/com/github/kuangcp/reflects/ReflectPerformanceTest.java +++ b/class/src/test/java/com/github/kuangcp/reflects/ReflectPerformanceTest.java @@ -1,98 +1,100 @@ package com.github.kuangcp.reflects; import com.github.kuangcp.time.GetRunTime; -import java.lang.reflect.Method; import lombok.Data; import lombok.extern.slf4j.Slf4j; import net.sf.cglib.reflect.FastClass; import net.sf.cglib.reflect.FastMethod; import org.junit.Test; +import java.lang.reflect.Method; + /** * 反射的性能问题 http://www.cnblogs.com/zhishan/p/3195771.html * cglib(已缓存) 耗时 50%-70% 于缓存, 10% 于 原始方式 * TODO 操作字节码方式取代反射 * TODO jmh + * * @author kuangcp */ @Slf4j public class ReflectPerformanceTest { - private static final int LOOP_SIZE = 50_000_000; - private static final GetRunTime time = new GetRunTime(); + private static final int LOOP_SIZE = 50_000_000; + private static final GetRunTime time = new GetRunTime(); - @Data - class TargetObject { + @Data + class TargetObject { - private int num; - } - - // primitive method invoke - @Test - public void testGetSet() { - long sum = 0; - TargetObject targetObject = new TargetObject(); + private int num; + } - time.startCount(); - for (int i = 0; i < LOOP_SIZE; ++i) { - targetObject.setNum(i); - sum += targetObject.getNum(); + // primitive method invoke + @Test + public void testGetSet() { + long sum = 0; + TargetObject targetObject = new TargetObject(); + + time.startCount(); + for (int i = 0; i < LOOP_SIZE; ++i) { + targetObject.setNum(i); + sum += targetObject.getNum(); + } + time.endCountOneLine("invoke get-set method "); + log.info("LOOP_SIZE {} 和是 {} ", LOOP_SIZE, sum); } - time.endCountOneLine("invoke get-set method "); - log.info("LOOP_SIZE {} 和是 {} ", LOOP_SIZE, sum); - } - - @Test - public void testOriginReflect() throws Exception { - long sum = 0; - TargetObject targetObject = new TargetObject(); - - time.startCount(); - for (int i = 0; i < LOOP_SIZE; ++i) { - Method method = targetObject.getClass().getMethod("setNum", int.class); - method.invoke(targetObject, i); - sum += targetObject.getNum(); + + @Test + public void testOriginReflect() throws Exception { + long sum = 0; + TargetObject targetObject = new TargetObject(); + + time.startCount(); + for (int i = 0; i < LOOP_SIZE; ++i) { + Method method = targetObject.getClass().getMethod("setNum", int.class); + method.invoke(targetObject, i); + sum += targetObject.getNum(); + } + + time.endCountOneLine("simple reflect "); + log.info("LOOP_SIZE {} 和是 {} ", LOOP_SIZE, sum); } - time.endCountOneLine("simple reflect "); - log.info("LOOP_SIZE {} 和是 {} ", LOOP_SIZE, sum); - } - - @Test - public void testOriginReflectWithCache() throws Exception { - long sum = 0; - TargetObject targetObject = new TargetObject(); - time.startCount(); - - Method method = targetObject.getClass().getMethod("setNum", int.class); - for (int i = 0; i < LOOP_SIZE; ++i) { - method.invoke(targetObject, i); - int num = targetObject.getNum(); - sum += num; + @Test + public void testOriginReflectWithCache() throws Exception { + long sum = 0; + TargetObject targetObject = new TargetObject(); + time.startCount(); + + Method method = targetObject.getClass().getMethod("setNum", int.class); + for (int i = 0; i < LOOP_SIZE; ++i) { + method.invoke(targetObject, i); + int num = targetObject.getNum(); + sum += num; + } + + time.endCountOneLine("simple reflect with cache"); + log.info("LOOP_SIZE {} 和是 {} ", LOOP_SIZE, sum); } - time.endCountOneLine("simple reflect with cache"); - log.info("LOOP_SIZE {} 和是 {} ", LOOP_SIZE, sum); - } + @Test + public void testCglib() throws Exception { + long sum = 0; + TargetObject targetObject = new TargetObject(); - @Test - public void testCglib() throws Exception { - long sum = 0; - TargetObject targetObject = new TargetObject(); + FastClass testClazz = FastClass.create(TargetObject.class); + FastMethod method = testClazz.getMethod("setNum", new Class[]{int.class}); - FastClass testClazz = FastClass.create(TargetObject.class); - FastMethod method = testClazz.getMethod("setNum", new Class[]{int.class}); + Object[] param = new Object[1]; + time.startCount(); + for (int i = 0; i < LOOP_SIZE; ++i) { + param[0] = i; + method.invoke(targetObject, param); + sum += targetObject.getNum(); + } - Object[] param = new Object[1]; - time.startCount(); - for (int i = 0; i < LOOP_SIZE; ++i) { - param[0] = i; - method.invoke(targetObject, param); - sum += targetObject.getNum(); + time.endCountOneLine("use cglib "); + log.info("LOOP_SIZE {} 和是 {} ", LOOP_SIZE, sum); } - time.endCountOneLine("use cglib "); - log.info("LOOP_SIZE {} 和是 {} ", LOOP_SIZE, sum); - } - } diff --git a/class/src/test/java/syntax/bit/BitOperatorsTest.java b/class/src/test/java/syntax/bit/BitOperatorsTest.java index b4ed3b69..676fac5b 100644 --- a/class/src/test/java/syntax/bit/BitOperatorsTest.java +++ b/class/src/test/java/syntax/bit/BitOperatorsTest.java @@ -1,185 +1,185 @@ package syntax.bit; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.core.IsEqual.equalTo; - import com.github.kuangcp.time.GetRunTime; import com.github.kuangcp.util.ShowBinary; -import java.nio.charset.StandardCharsets; -import java.util.stream.IntStream; -import java.util.stream.Stream; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.junit.Assert; import org.junit.Test; +import java.nio.charset.StandardCharsets; +import java.util.stream.IntStream; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.IsEqual.equalTo; + /** * @author kuangcp on 3/5/19-2:41 PM * https://docs.oracle.com/javase/tutorial/java/nutsandbolts/op3.html * >>> : https://stackoverflow.com/questions/14501233/unsigned-right-shift-operator-in-java - * + *

* 原码=>补码: 取反再加1 除符号位 * 补码=>原码: 减1再取反 除符号位 */ @Slf4j public class BitOperatorsTest { - @Test - public void testSimpleBitOperator() { - // 与 : 1 1 -> 1 否则 0 - // 1010 - // 1001 - // 1000 - assertThat(0b1010 & 0b1001, equalTo(0b1000)); - - // 或 : 0 0 -> 0 否则 1 - assertThat(0b101 | 0b001, equalTo(0b101)); - - // 异或: 不同 -> 1 否则 0 - // 101 - // 001 - // 100 - assertThat(0b101 ^ 0b001, equalTo(0b100)); - - // 非 - assertThat(~0b1110, equalTo(-0b1111)); - assertThat(~0b001, equalTo(-2)); - - // 00000000000000000000000000000001 - show(0b001); - // 11111111111111111111111111111110 - show(~0b001);// 以补码进行存储的, 所以取值时需要转回原码 - - // 减一 11111111111111111111111111111101 - // 取反 10000000000000000000000000000010 - // 也就是 -2 - } - - @Test - public void testBitAndAssignment() { - int num = 0b101011; - num |= 0b101; - assertThat(num, equalTo(0b101111)); - - // 101111 | 1011 - num |= num >>> 2; - assertThat(num, equalTo(0b101111)); - } - - - /** - * 右移 - */ - @Test - public void testRight() { - // 带符号的 右移操作 正数则左补0 负数则左补1 - assertThat(0b101 >> 2, equalTo(0b1)); - - // 11111111111111111111111111111011 -0b101的补码 (也是实际存储的值) - // 11111111111111111111111111111110 右移两位后的补码 - // 10000000000000000000000000000010 从补码 -1 取反 得到 原码 - show(-0b101); - assertThat(-0b101 >> 2, equalTo(-0b10)); - - // 无视符号位的 右移操作 左补0 - assertThat(0b101 >>> 2, equalTo(0b1)); - assertThat(0b101 >>> 3, equalTo(0b0)); - - // 11111111111111111111111111111011 - // 00000000000000000000000000000011 - show(-0b101 >>> 30); - assertThat(-0b101 >>> 30, equalTo(0b11)); - - // 在正数情况下 >> 等价于 >>> - assertThat(0b110011 >> 3, equalTo(0b110011 >>> 3)); - } - - /** - * 左移 - */ - @Test - public void testLeft() { - assertThat(0b11_0110 << 2, equalTo(0b1101_1000)); - - assertThat(0b1 << 31, equalTo(0b1000_0000_0000_0000_0000_0000_0000_0000)); - show(0b1000_0000_0000_0000_0000_0000_0000_0000); - - // 溢出了32位... - assertThat(0b1 << 35, equalTo(0b1000)); - show(0b1 << 35); - } - - private static void show(int result) { - log.info("{} {}", String.format("%32s", ShowBinary.toBinary(result)), result); - } - - @Test - public void testIdempotentOperation() { - for (int i = 1; i < 45; i++) { - int value = idempotentOperations(i); - assertThat(value, equalTo((int) Math.pow(2, Math.floor(Math.log(i) / Math.log(2)) + 1))); - System.out.println(i + " => " + value); + @Test + public void testSimpleBitOperator() { + // 与 : 1 1 -> 1 否则 0 + // 1010 + // 1001 + // 1000 + assertThat(0b1010 & 0b1001, equalTo(0b1000)); + + // 或 : 0 0 -> 0 否则 1 + assertThat(0b101 | 0b001, equalTo(0b101)); + + // 异或: 不同 -> 1 否则 0 + // 101 + // 001 + // 100 + assertThat(0b101 ^ 0b001, equalTo(0b100)); + + // 非 + assertThat(~0b1110, equalTo(-0b1111)); + assertThat(~0b001, equalTo(-2)); + + // 00000000000000000000000000000001 + show(0b001); + // 11111111111111111111111111111110 + show(~0b001);// 以补码进行存储的, 所以取值时需要转回原码 + + // 减一 11111111111111111111111111111101 + // 取反 10000000000000000000000000000010 + // 也就是 -2 + } + + @Test + public void testBitAndAssignment() { + int num = 0b101011; + num |= 0b101; + assertThat(num, equalTo(0b101111)); + + // 101111 | 1011 + num |= num >>> 2; + assertThat(num, equalTo(0b101111)); + } + + + /** + * 右移 + */ + @Test + public void testRight() { + // 带符号的 右移操作 正数则左补0 负数则左补1 + assertThat(0b101 >> 2, equalTo(0b1)); + + // 11111111111111111111111111111011 -0b101的补码 (也是实际存储的值) + // 11111111111111111111111111111110 右移两位后的补码 + // 10000000000000000000000000000010 从补码 -1 取反 得到 原码 + show(-0b101); + assertThat(-0b101 >> 2, equalTo(-0b10)); + + // 无视符号位的 右移操作 左补0 + assertThat(0b101 >>> 2, equalTo(0b1)); + assertThat(0b101 >>> 3, equalTo(0b0)); + + // 11111111111111111111111111111011 + // 00000000000000000000000000000011 + show(-0b101 >>> 30); + assertThat(-0b101 >>> 30, equalTo(0b11)); + + // 在正数情况下 >> 等价于 >>> + assertThat(0b110011 >> 3, equalTo(0b110011 >>> 3)); + } + + /** + * 左移 + */ + @Test + public void testLeft() { + assertThat(0b11_0110 << 2, equalTo(0b1101_1000)); + + assertThat(0b1 << 31, equalTo(0b1000_0000_0000_0000_0000_0000_0000_0000)); + show(0b1000_0000_0000_0000_0000_0000_0000_0000); + + // 溢出了32位... + assertThat(0b1 << 35, equalTo(0b1000)); + show(0b1 << 35); + } + + private static void show(int result) { + log.info("{} {}", String.format("%32s", ShowBinary.toBinary(result)), result); + } + + @Test + public void testIdempotentOperation() { + for (int i = 1; i < 45; i++) { + int value = idempotentOperations(i); + assertThat(value, equalTo((int) Math.pow(2, Math.floor(Math.log(i) / Math.log(2)) + 1))); + System.out.println(i + " => " + value); + } + + assertThat(idempotentOperations(0b1011_0000_0001_1111), equalTo(0b1_0000_0000_0000_0000)); + assertThat(idempotentOperations(0b10011_0000_0001_1111), equalTo(0b10_0000_0000_0000_0000)); + } + + @Test + public void testCompareSpeed() { + GetRunTime runTime = new GetRunTime().startCount(); + IntStream.rangeClosed(1, 10000).forEach(BitOperatorsTest::idempotentOperations); + runTime.endCountOneLine("bit "); + + runTime.startCount(); + IntStream.rangeClosed(1, 10000) + .forEach(i -> Math.pow(2, Math.floor(Math.log(i) / Math.log(2)) + 1)); + runTime.endCountOneLine("log and pow"); + } + + @Test + public void testXORByByte() { + String originMsg = "原始中文内容123Abc"; + + byte[] data = originMsg.getBytes(StandardCharsets.UTF_8); + log.info("data={}", data); + + byte[] one = transfer(data); + log.info("data={}", one); + + byte[] two = transfer(one); + log.info("data={}", two); + + String oneStr = StringUtils.toEncodedString(one, StandardCharsets.UTF_8); + String twoStr = StringUtils.toEncodedString(two, StandardCharsets.UTF_8); + assertThat(twoStr, equalTo(originMsg)); + Assert.assertNotEquals(oneStr, originMsg); + } + + private byte[] transfer(byte[] data) { + byte[] key = {3, 56, 12, 22, 35, 87, 123, 83, 111, 34, 23, 56, 34, 56}; + int maxKey = key.length; + byte[] result = new byte[data.length]; + for (int i = 0; i < data.length; i++) { + result[i] = (byte) (data[i] ^ key[i % maxKey]); + } + return result; } - assertThat(idempotentOperations(0b1011_0000_0001_1111), equalTo(0b1_0000_0000_0000_0000)); - assertThat(idempotentOperations(0b10011_0000_0001_1111), equalTo(0b10_0000_0000_0000_0000)); - } - - @Test - public void testCompareSpeed() { - GetRunTime runTime = new GetRunTime().startCount(); - IntStream.rangeClosed(1, 10000).forEach(BitOperatorsTest::idempotentOperations); - runTime.endCountOneLine("bit "); - - runTime.startCount(); - IntStream.rangeClosed(1, 10000) - .forEach(i -> Math.pow(2, Math.floor(Math.log(i) / Math.log(2)) + 1)); - runTime.endCountOneLine("log and pow"); - } - - @Test - public void testXORByByte() { - String originMsg = "原始中文内容123Abc"; - - byte[] data = originMsg.getBytes(StandardCharsets.UTF_8); - log.info("data={}", data); - - byte[] one = transfer(data); - log.info("data={}", one); - - byte[] two = transfer(one); - log.info("data={}", two); - - String oneStr = StringUtils.toEncodedString(one, StandardCharsets.UTF_8); - String twoStr = StringUtils.toEncodedString(two, StandardCharsets.UTF_8); - assertThat(twoStr, equalTo(originMsg)); - Assert.assertNotEquals(oneStr, originMsg); - } - - private byte[] transfer(byte[] data) { - byte[] key = {3, 56, 12, 22, 35, 87, 123, 83, 111, 34, 23, 56, 34, 56}; - int maxKey = key.length; - byte[] result = new byte[data.length]; - for (int i = 0; i < data.length; i++) { - result[i] = (byte) (data[i] ^ key[i % maxKey]); + /** + * 等价于 num => (int)Math.pow(2, Math.floor(Math.log(i) / Math.log(2)) + 1)) + * 最大使得形成 16位1 + 1 + */ + private static int idempotentOperations(int num) { + int n = num; + // 最高位肯定是1 右移一位,并做或操作然后最高位是两个1 + // 然后右移2位 最高位4个1, + // 然后右移4位 ... 直到所有的位都是1. 所以中间的右移 3 5 6 7 都是多余的 + + n |= n >>> 1; // 11xxx + n |= n >>> 2; // 1111xxx + n |= n >>> 4; // 11111111xx + n |= n >>> 8; // 1111111111111111xx + return n + 1; } - return result; - } - - /** - * 等价于 num => (int)Math.pow(2, Math.floor(Math.log(i) / Math.log(2)) + 1)) - * 最大使得形成 16位1 + 1 - */ - private static int idempotentOperations(int num) { - int n = num; - // 最高位肯定是1 右移一位,并做或操作然后最高位是两个1 - // 然后右移2位 最高位4个1, - // 然后右移4位 ... 直到所有的位都是1. 所以中间的右移 3 5 6 7 都是多余的 - - n |= n >>> 1; // 11xxx - n |= n >>> 2; // 1111xxx - n |= n >>> 4; // 11111111xx - n |= n >>> 8; // 1111111111111111xx - return n + 1; - } } diff --git a/class/src/test/java/syntax/bit/StringPerformanceTest.java b/class/src/test/java/syntax/bit/StringPerformanceTest.java index 5ed8816f..3cbd0147 100644 --- a/class/src/test/java/syntax/bit/StringPerformanceTest.java +++ b/class/src/test/java/syntax/bit/StringPerformanceTest.java @@ -10,54 +10,54 @@ @Slf4j public class StringPerformanceTest { - private int repeat = 10000; - private String strData = "1100101010100111110101001011"; - private int length = strData.length(); - private int intData = 212499787; - - @Test - public void testReadString() { - GetRunTime getRunTime = new GetRunTime().startCount(); - for (int i = 0; i < repeat; i++) { - readString(); + private int repeat = 10000; + private String strData = "1100101010100111110101001011"; + private int length = strData.length(); + private int intData = 212499787; + + @Test + public void testReadString() { + GetRunTime getRunTime = new GetRunTime().startCount(); + for (int i = 0; i < repeat; i++) { + readString(); + } + getRunTime.endCount("string"); } - getRunTime.endCount("string"); - } - - @Test - public void testReadBit() { - GetRunTime getRunTime = new GetRunTime().startCount(); - for (int i = 0; i < repeat; i++) { - readBit(); + + @Test + public void testReadBit() { + GetRunTime getRunTime = new GetRunTime().startCount(); + for (int i = 0; i < repeat; i++) { + readBit(); + } + getRunTime.endCount("bit"); } - getRunTime.endCount("bit"); - } - - private void readBit() { - int temp = intData; - for (int i = 0; i < length; i++) { - if (temp % 2 == 1) { - logic(true); - } else { - logic(false); - } - temp >>= 1; + + private void readBit() { + int temp = intData; + for (int i = 0; i < length; i++) { + if (temp % 2 == 1) { + logic(true); + } else { + logic(false); + } + temp >>= 1; + } } - } - - private void readString() { - char[] chars = strData.toCharArray(); - for (int i = 0; i < length; i++) { - char c = chars[i]; - if ('1' == c) { - logic(true); - } else if ('0' == c) { - logic(false); - } + + private void readString() { + char[] chars = strData.toCharArray(); + for (int i = 0; i < length; i++) { + char c = chars[i]; + if ('1' == c) { + logic(true); + } else if ('0' == c) { + logic(false); + } + } } - } - private void logic(boolean result) { + private void logic(boolean result) { // log.info("result {}", result); - } + } } diff --git a/class/src/test/java/syntax/string/SplitDemoTest.java b/class/src/test/java/syntax/string/SplitDemoTest.java index d27dec03..3bf68323 100644 --- a/class/src/test/java/syntax/string/SplitDemoTest.java +++ b/class/src/test/java/syntax/string/SplitDemoTest.java @@ -1,10 +1,11 @@ package syntax.string; import com.github.kuangcp.time.GetRunTime; -import java.util.Objects; import lombok.extern.slf4j.Slf4j; import org.junit.Test; +import java.util.Objects; + /** * Created by https://github.com/kuangcp * @@ -13,48 +14,48 @@ @Slf4j public class SplitDemoTest { - // 截取第一次 target 出现之前的字符串 - private String removeChar(String origin, String target) { - int indexOf = origin.indexOf(target); - if (indexOf == -1) { - return origin; + // 截取第一次 target 出现之前的字符串 + private String removeChar(String origin, String target) { + int indexOf = origin.indexOf(target); + if (indexOf == -1) { + return origin; + } + return origin.substring(0, indexOf); } - return origin.substring(0, indexOf); - } - // 去除最后一个字符 - private String removeLast(String target) { - if (Objects.isNull(target) || target.isEmpty()) { - return ""; + // 去除最后一个字符 + private String removeLast(String target) { + if (Objects.isNull(target) || target.isEmpty()) { + return ""; + } + return target.substring(0, target.length() - 1); } - return target.substring(0, target.length() - 1); - } - - // 对比去除最后一个字符的性能, 第二个略好一些 - @Test - public void testRemoveChar() { - GetRunTime getRunTime = new GetRunTime().startCount(); - String origin = "122113.11.1.1.1."; - for (int i = 0; i < 10; i++) { - origin = removeChar(origin, "."); - log.info("temp: origin={}", origin); - } - getRunTime.endCount("索引去除"); - getRunTime.startCount(); - for (int i = 0; i < 100; i++) { - removeLast("122113.11.1.1.1."); + // 对比去除最后一个字符的性能, 第二个略好一些 + @Test + public void testRemoveChar() { + GetRunTime getRunTime = new GetRunTime().startCount(); + String origin = "122113.11.1.1.1."; + for (int i = 0; i < 10; i++) { + origin = removeChar(origin, "."); + log.info("temp: origin={}", origin); + } + getRunTime.endCount("索引去除"); + + getRunTime.startCount(); + for (int i = 0; i < 100; i++) { + removeLast("122113.11.1.1.1."); + } + getRunTime.endCount("去除最后一个字符"); } - getRunTime.endCount("去除最后一个字符"); - } - - @Test - public void testEmpty() { - String line = "\"\",QP001,5"; - String[] datas = line.split(","); - System.out.println(datas.length); - for (String data : datas) { - System.out.println(data); + + @Test + public void testEmpty() { + String line = "\"\",QP001,5"; + String[] datas = line.split(","); + System.out.println(datas.length); + for (String data : datas) { + System.out.println(data); + } } - } } \ No newline at end of file diff --git a/gui/pom.xml b/gui/pom.xml index f08b3ae9..8b331ca9 100644 --- a/gui/pom.xml +++ b/gui/pom.xml @@ -19,10 +19,10 @@ - - - - + + com.github.kuangcp + kcp-tool + diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/Brick.java b/gui/src/main/java/com/github/kuangcp/tank/domain/Brick.java index bab7b712..2b2d3b98 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/domain/Brick.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/Brick.java @@ -11,37 +11,11 @@ public Brick(int hx, int hy) { @Override public int getWidth() { - return 5; + return 7; } @Override public int getHeight() { - return 5; + return 7; } - - /** - * 构造器全是沿用父类的,父类要是没有无参构造器,子类有了就属于非法 - * 并且 父类有了含参构造器,子类必须也要有,显式的super一下 - */ - public Brick() { - } - //被舍弃 不使用 - /*画出砖块 20*10大小 为了便于使用 就直接(x,y)到(x,y)画出矩形 虽然实现了但是不利于控制死亡*/ - /*public void d(Graphics g,Vector bricks,int startX,int startY,int endX,int endY){ - -// g.setColor(Color.LIGHT_GRAY);//钢板的颜色 - g.setColor(Color.orange);//砖块颜色 - - for( hx=startX;hxconcurrency generic guava - + gui io java8 netty network pattern - + question spring test web @@ -76,24 +76,24 @@ ch.qos.logback logback-classic - 1.2.5 + 1.2.9 - - - - - - - - - - - - - - - + + com.github.kuangcp + kcp-tool + 1.0.8 + + + com.github.kuangcp + kcp-tuple + 1.0.8 + + + com.github.kuangcp + kcp-core + 1.0.8 + org.apache.commons commons-lang3 diff --git a/question/pom.xml b/question/pom.xml index 05d89058..d82b0458 100644 --- a/question/pom.xml +++ b/question/pom.xml @@ -57,6 +57,11 @@ cglib 3.2.4 + + + com.github.kuangcp + kcp-tool + diff --git a/web/Readme.md b/web/Readme.md new file mode 100644 index 00000000..0f273a37 --- /dev/null +++ b/web/Readme.md @@ -0,0 +1,5 @@ +# Web 开发 + +参数校验 +拦截器 +过滤器 From bd827594b3e2f8b5c9d8ec04e1f12b261eb86d1e Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Sat, 26 Aug 2023 16:21:44 +0800 Subject: [PATCH 277/476] virus --- .../virusbroadcast/VirusBroadcast.java | 2 +- .../virusbroadcast/constant/Constants.java | 12 +- .../constant/PersonStateEnum.java | 51 ++- .../kuangcp/virusbroadcast/domain/City.java | 233 ++++++---- .../kuangcp/virusbroadcast/domain/Person.java | 407 +++++++++--------- .../virusbroadcast/gui/DisplayPanel.java | 147 ++++--- 6 files changed, 452 insertions(+), 400 deletions(-) diff --git a/gui/src/main/java/com/github/kuangcp/virusbroadcast/VirusBroadcast.java b/gui/src/main/java/com/github/kuangcp/virusbroadcast/VirusBroadcast.java index ad0f7793..9b6491bd 100644 --- a/gui/src/main/java/com/github/kuangcp/virusbroadcast/VirusBroadcast.java +++ b/gui/src/main/java/com/github/kuangcp/virusbroadcast/VirusBroadcast.java @@ -21,7 +21,7 @@ public static void main(String[] args) { frame.setSize(1000, 800); frame.setLocationRelativeTo(null); frame.setVisible(true); - frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); executors.submit(panel); executors.submit(City.getInstance()); diff --git a/gui/src/main/java/com/github/kuangcp/virusbroadcast/constant/Constants.java b/gui/src/main/java/com/github/kuangcp/virusbroadcast/constant/Constants.java index b1f7f3fa..08360898 100644 --- a/gui/src/main/java/com/github/kuangcp/virusbroadcast/constant/Constants.java +++ b/gui/src/main/java/com/github/kuangcp/virusbroadcast/constant/Constants.java @@ -5,14 +5,14 @@ public interface Constants { int BASE_RATE = 10000; int ORIGINAL_COUNT = 50;//初始感染数量 - float BROAD_RATE = 0.82f;//传播率 - float SHADOW_TIME = 140;//潜伏时间 + float BROAD_RATE = 0.42f;//传播率 + float SHADOW_TIME = 14;//潜伏时间 int HOSPITAL_RECEIVE_TIME = 10;//医院收治响应时间 - int BED_COUNT = 500;//医院床位 - float INTENTION = 0.49f;//流动意向平均值 - float SAFE_DISTANCE = 2f; // 安全距离 + int BED_COUNT = 1500;//医院床位 + float INTENTION = 0.79f;//流动意向平均值 + float SAFE_DISTANCE = 1f; // 安全距离 int CITY_PERSON_SCALE = 6000; - int DEAD_RATE = 1000; // 每天可能死亡率 万分比 + int DEAD_RATE = 10; // 每天可能死亡率 万分比 int CURE_RATE = 100;// 每天可能治愈率 万分比 } diff --git a/gui/src/main/java/com/github/kuangcp/virusbroadcast/constant/PersonStateEnum.java b/gui/src/main/java/com/github/kuangcp/virusbroadcast/constant/PersonStateEnum.java index b0652788..3fe4d74a 100644 --- a/gui/src/main/java/com/github/kuangcp/virusbroadcast/constant/PersonStateEnum.java +++ b/gui/src/main/java/com/github/kuangcp/virusbroadcast/constant/PersonStateEnum.java @@ -1,40 +1,39 @@ package com.github.kuangcp.virusbroadcast.constant; +import com.github.kuangcp.virusbroadcast.domain.City; +import lombok.Getter; + import java.util.Optional; +import java.util.function.BiConsumer; /** * @author https://github.com/kuangcp on 2020-02-14 12:43 */ +@Getter public enum PersonStateEnum { - NORMAL(PersonState.NORMAL, "normal"), - SHADOW(PersonState.SHADOW, "shadow"), - CONFIRMED(PersonState.CONFIRMED, "confirmed"), - FREEZE(PersonState.FREEZE, "freeze"), - DEAD(PersonState.DEAD, "dead"); - - private int value; - private String desc; + NORMAL(PersonState.NORMAL, "normal", City.Status::setNormal), + SHADOW(PersonState.SHADOW, "shadow", City.Status::setShadow), + CONFIRMED(PersonState.CONFIRMED, "confirmed", City.Status::setConfirmed), + FREEZE(PersonState.FREEZE, "freeze", City.Status::setFreeze), + DEAD(PersonState.DEAD, "dead", City.Status::setDead); - public int getValue() { - return value; - } + private final int value; + private final String desc; + private BiConsumer val; - public String getDesc() { - return desc; - } - - PersonStateEnum(int value, String desc) { - this.value = value; - this.desc = desc; - } + PersonStateEnum(int value, String desc, BiConsumer val) { + this.value = value; + this.desc = desc; + this.val = val; + } - public static Optional getByValue(int value) { - for (PersonStateEnum stateEnum : values()) { - if (stateEnum.value == value) { - return Optional.of(stateEnum); - } + public static Optional getByValue(int value) { + for (PersonStateEnum stateEnum : values()) { + if (stateEnum.value == value) { + return Optional.of(stateEnum); + } + } + return Optional.empty(); } - return Optional.empty(); - } } diff --git a/gui/src/main/java/com/github/kuangcp/virusbroadcast/domain/City.java b/gui/src/main/java/com/github/kuangcp/virusbroadcast/domain/City.java index 5264aa07..ca730a51 100644 --- a/gui/src/main/java/com/github/kuangcp/virusbroadcast/domain/City.java +++ b/gui/src/main/java/com/github/kuangcp/virusbroadcast/domain/City.java @@ -1,10 +1,12 @@ package com.github.kuangcp.virusbroadcast.domain; -import static com.github.kuangcp.virusbroadcast.constant.Constants.CITY_PERSON_SCALE; - import com.github.kuangcp.virusbroadcast.constant.Constants; import com.github.kuangcp.virusbroadcast.constant.PersonState; import com.github.kuangcp.virusbroadcast.constant.PersonStateEnum; +import com.github.kuangcp.virusbroadcast.gui.DisplayPanel; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; + import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -13,102 +15,153 @@ import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Consumer; import java.util.stream.Collectors; import java.util.stream.Stream; -import lombok.extern.slf4j.Slf4j; + +import static com.github.kuangcp.virusbroadcast.constant.Constants.CITY_PERSON_SCALE; @Slf4j public class City implements Runnable { - private int centerX = 400; - private int centerY = 400; - private int personCount = CITY_PERSON_SCALE; - private volatile boolean init = false; - - private static City city = new City(); - public List personList = new CopyOnWriteArrayList<>(); - private static Map counts; - - static { - counts = Stream.of(PersonStateEnum.values()) - .collect(Collectors.toMap(PersonStateEnum::getValue, v -> new AtomicInteger(), - (front, current) -> current)); - counts.get(PersonState.NORMAL).set(CITY_PERSON_SCALE); - } - - private City() { - for (int i = 0; i < this.personCount; i++) { - int x = (int) (100 * ThreadLocalRandom.current().nextGaussian() + centerX); - int y = (int) (100 * ThreadLocalRandom.current().nextGaussian() + centerY); - if (x > 700) { - x = 700; - } - Person person = new Person(x, y); - personList.add(person); + private int centerX = 400; + private int centerY = 400; + private int personCount = CITY_PERSON_SCALE; + private volatile boolean init = false; + + private static final City city = new City(); + public List personList = new CopyOnWriteArrayList<>(); + private static Map counts; + + @Data + public static class Status { + int normal = 0; + + /** + * 潜伏期 + */ + int shadow = 0; + + /** + * 确诊 + */ + int confirmed = 0; + + /** + * 隔离 + */ + int freeze = 0; + + /** + * 死亡 + */ + int dead = 0; + + @Override + public String toString() { + return normal + + " -> " + shadow + + " -> " + confirmed + + " -> " + freeze + + " -> " + dead; + } } - } - - public void initInfected() { - // 初始感染 - for (int i = 0; i < Constants.ORIGINAL_COUNT; i++) { - int index = new Random().nextInt(personList.size() - 1); - Person person = personList.get(index); - - while (person.isInfected()) { - index = new Random().nextInt(personList.size() - 1); - person = personList.get(index); - } - person.beInfected(); + + static { + counts = Stream.of(PersonStateEnum.values()) + .collect(Collectors.toMap(PersonStateEnum::getValue, v -> new AtomicInteger(), + (front, current) -> current)); + counts.get(PersonState.NORMAL).set(CITY_PERSON_SCALE); + } + + private City() { + for (int i = 0; i < this.personCount; i++) { + int x = (int) (100 * ThreadLocalRandom.current().nextGaussian() + centerX); + int y = (int) (100 * ThreadLocalRandom.current().nextGaussian() + centerY); + if (x > 700) { + x = 700; + } + Person person = new Person(x, y); + personList.add(person); + } } - } - - - public static City getInstance() { - return city; - } - - public List getPersonList() { - return personList; - } - - public void dead(Person person) { - personList.remove(person); - } - - public static void trans(int from, int to) { - AtomicInteger fromCount = counts.get(from); - AtomicInteger toCount = counts.get(to); - toCount.incrementAndGet(); - fromCount.decrementAndGet(); - } - - @Override - public void run() { - while (true) { - if (!init) { - this.initInfected(); - this.init = true; - } - try { - Thread.sleep(1000); - showCityInfo(); - } catch (Exception e) { - log.error("", e); - } + + public void initInfected() { + // 初始感染 + for (int i = 0; i < Constants.ORIGINAL_COUNT; i++) { + int index = new Random().nextInt(personList.size() - 1); + Person person = personList.get(index); + + while (person.isInfected()) { + index = new Random().nextInt(personList.size() - 1); + person = personList.get(index); + } + person.beInfected(); + } } - } - - public void showCityInfo() { - StringBuilder temp = new StringBuilder(); - int sum = 0; - for (Entry entry : counts.entrySet()) { - Optional stateOpt = PersonStateEnum.getByValue(entry.getKey()); - if (stateOpt.isPresent()) { - int value = entry.getValue().get(); - temp.append(String.format("%6s: %5d ▌", stateOpt.get().getDesc(), value)); - sum += value; - } + + + public static City getInstance() { + return city; + } + + public List getPersonList() { + return personList; + } + + public void dead(Person person) { + personList.remove(person); + } + + public static void trans(int from, int to) { + AtomicInteger fromCount = counts.get(from); + AtomicInteger toCount = counts.get(to); + toCount.incrementAndGet(); + fromCount.decrementAndGet(); + } + + @Override + public void run() { + while (true) { + if (!init) { + this.initInfected(); + this.init = true; + } + try { + Thread.sleep(1000); + showCityInfo(); + } catch (Exception e) { + log.error("", e); + } + } + } + + public Status buildCityInfo() { + final Status status = new Status(); + for (Entry entry : counts.entrySet()) { + Optional stateOpt = PersonStateEnum.getByValue(entry.getKey()); + if (stateOpt.isPresent()) { + int value = entry.getValue().get(); + final PersonStateEnum personStateEnum = stateOpt.get(); + personStateEnum.getVal().accept(status, value); + } + } + return status; + } + + public void showCityInfo() { + StringBuilder temp = new StringBuilder(); + int sum = 0; + final Status status = new Status(); + final Consumer setConfirmed = status::setConfirmed; + for (Entry entry : counts.entrySet()) { + Optional stateOpt = PersonStateEnum.getByValue(entry.getKey()); + if (stateOpt.isPresent()) { + int value = entry.getValue().get(); + temp.append(String.format("%6s: %5d ▌", stateOpt.get().getDesc(), value)); + sum += value; + } + } + log.info("day:{} {} sum:{}", String.format("%7d", DisplayPanel.worldTime), temp, sum); } - log.info("{} sum:{}", temp.toString(), sum); - } } diff --git a/gui/src/main/java/com/github/kuangcp/virusbroadcast/domain/Person.java b/gui/src/main/java/com/github/kuangcp/virusbroadcast/domain/Person.java index 525d76f0..1f62a99b 100644 --- a/gui/src/main/java/com/github/kuangcp/virusbroadcast/domain/Person.java +++ b/gui/src/main/java/com/github/kuangcp/virusbroadcast/domain/Person.java @@ -1,19 +1,20 @@ package com.github.kuangcp.virusbroadcast.domain; -import static com.github.kuangcp.virusbroadcast.constant.Constants.SAFE_DISTANCE; - import com.github.kuangcp.virusbroadcast.Hospital; import com.github.kuangcp.virusbroadcast.constant.Constants; import com.github.kuangcp.virusbroadcast.constant.PersonState; import com.github.kuangcp.virusbroadcast.gui.DisplayPanel; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; + import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.concurrent.ThreadLocalRandom; import java.util.function.Consumer; -import lombok.Data; -import lombok.extern.slf4j.Slf4j; + +import static com.github.kuangcp.virusbroadcast.constant.Constants.SAFE_DISTANCE; /** * @@ -22,229 +23,221 @@ @Slf4j public class Person { - private Bed bed; - private int x; - private int y; - private MoveTarget moveTarget; - private int state = PersonState.NORMAL; - private static Map> functions = new HashMap<>(); - - int sig = 1; - /** - * 被感染时间 - */ - int infectedTime = 0; - /** - * 确诊时间 - */ - int confirmedTime = 0; - - double targetXU; - double targetYU; - double targetSig = 50; - - public Person(int x, int y) { - this.x = x; - this.y = y; - targetXU = 100 * ThreadLocalRandom.current().nextGaussian() + x; - targetYU = 100 * ThreadLocalRandom.current().nextGaussian() + y; - } - - public boolean wantMove() { - double value = sig * ThreadLocalRandom.current().nextGaussian() + Constants.INTENTION; - return value > 0; - } - - public boolean isInfected() { - return state >= PersonState.SHADOW && state != PersonState.DEAD; - } - - /** - * 被感染 - */ - public void beInfected() { - if (this.isInfected()) { - return; + private Bed bed; + private int x; + private int y; + private MoveTarget moveTarget; + private int state = PersonState.NORMAL; + private static Map> functions = new HashMap<>(); + + int sig = 1; + /** + * 被感染时间 + */ + int infectedTime = 0; + /** + * 确诊时间 + */ + int confirmedTime = 0; + + double targetXU; + double targetYU; + double targetSig = 50; + + public Person(int x, int y) { + this.x = x; + this.y = y; + targetXU = 100 * ThreadLocalRandom.current().nextGaussian() + x; + targetYU = 100 * ThreadLocalRandom.current().nextGaussian() + y; } - City.trans(PersonState.NORMAL, PersonState.SHADOW); - state = PersonState.SHADOW; - infectedTime = DisplayPanel.worldTime; - } - - public void confirmed() { - City.trans(PersonState.SHADOW, PersonState.CONFIRMED); - state = PersonState.CONFIRMED; - confirmedTime = DisplayPanel.worldTime; - } - - public void willInfected() { - if (this.isInfected()) { - return; - } - - // 是否能被其他人感染 - List people = City.getInstance().personList; - for (Person person : people) { - if (person.getState() == PersonState.NORMAL) { - continue; - } - if (this.mayInfected(person)) { - this.beInfected(); - } - } - } - - /** - * 是否感染 - */ - public boolean mayInfected(Person person) { - if (person.getState() == PersonState.NORMAL) { - return false; + public boolean wantMove() { + double value = sig * ThreadLocalRandom.current().nextGaussian() + Constants.INTENTION; + return value > 0; } - float random = ThreadLocalRandom.current().nextFloat(); - return random < Constants.BROAD_RATE && distance(person) < SAFE_DISTANCE; - } - public double distance(Person person) { - return Math.sqrt(Math.pow(x - person.getX(), 2) + Math.pow(y - person.getY(), 2)); - } - - private void moveTo(int x, int y) { - this.x += x; - this.y += y; - } + public boolean isInfected() { + return state >= PersonState.SHADOW && state != PersonState.DEAD; + } - private boolean freeze(Bed bed) { - if (Objects.nonNull(this.bed) || Objects.isNull(bed)) { -// System.out.println("隔离区没有空床位"); - return false; + /** + * 被感染 + */ + public void beInfected() { + if (this.isInfected()) { + return; + } + City.trans(PersonState.NORMAL, PersonState.SHADOW); + state = PersonState.SHADOW; + infectedTime = DisplayPanel.worldTime; } - City.trans(PersonState.CONFIRMED, PersonState.FREEZE); - this.bed = bed; - state = PersonState.FREEZE; - x = bed.getX(); - y = bed.getY(); - bed.setEmpty(false); - return true; - } - - private boolean cure() { - City.trans(PersonState.FREEZE, PersonState.NORMAL); - if (Objects.isNull(this.bed)) { - return false; + public void confirmed() { + City.trans(PersonState.SHADOW, PersonState.CONFIRMED); + state = PersonState.CONFIRMED; + confirmedTime = DisplayPanel.worldTime; } - this.bed.setEmpty(true); - this.bed = null; - this.state = PersonState.NORMAL; - return true; - } - - private boolean dead() { - City.trans(this.state, PersonState.DEAD); - if (Objects.nonNull(this.bed)) { - this.bed.setEmpty(true); - this.bed = null; + + public void willInfected() { + if (this.isInfected()) { + return; + } + + // 是否能被其他人感染 + List people = City.getInstance().personList; + for (Person person : people) { + if (person.getState() == PersonState.NORMAL) { + continue; + } + + if (this.mayInfected(person)) { + this.beInfected(); + } + } } - this.state = PersonState.DEAD; - return true; - } - - // 随机移动 TODO 确诊无法移动 - private void action() { - if (state == PersonState.FREEZE) { - return; + + /** + * 是否感染 + */ + public boolean mayInfected(Person person) { + if (person.getState() == PersonState.NORMAL) { + return false; + } + float random = ThreadLocalRandom.current().nextFloat(); + return random < Constants.BROAD_RATE && distance(person) < SAFE_DISTANCE; } - if (!wantMove()) { - return; + public double distance(Person person) { + return Math.sqrt(Math.pow(x - person.getX(), 2) + Math.pow(y - person.getY(), 2)); } - if (moveTarget == null || moveTarget.isArrived()) { - double targetX = targetSig * ThreadLocalRandom.current().nextGaussian() + targetXU; - double targetY = targetSig * ThreadLocalRandom.current().nextGaussian() + targetYU; - moveTarget = new MoveTarget((int) targetX, (int) targetY); + private void moveTo(int x, int y) { + this.x += x; + this.y += y; } - int dX = moveTarget.getX() - x; - int dY = moveTarget.getY() - y; - double length = Math.sqrt(Math.pow(dX, 2) + Math.pow(dY, 2)); + private boolean freeze(Bed bed) { + if (Objects.nonNull(this.bed) || Objects.isNull(bed)) { +// System.out.println("隔离区没有空床位"); + return false; + } + + City.trans(PersonState.CONFIRMED, PersonState.FREEZE); + this.bed = bed; + state = PersonState.FREEZE; + x = bed.getX(); + y = bed.getY(); + bed.setEmpty(false); + return true; + } - if (length < 1) { - moveTarget.setArrived(true); - return; + private boolean cure() { + City.trans(PersonState.FREEZE, PersonState.NORMAL); + if (Objects.isNull(this.bed)) { + return false; + } + this.bed.setEmpty(true); + this.bed = null; + this.state = PersonState.NORMAL; + return true; } - int udX = (int) (dX / length); - if (udX == 0 && dX != 0) { - if (dX > 0) { - udX = 1; - } else { - udX = -1; - } + + private boolean dead() { + City.trans(this.state, PersonState.DEAD); + if (Objects.nonNull(this.bed)) { + this.bed.setEmpty(true); + this.bed = null; + } + this.state = PersonState.DEAD; + return true; } - int udY = (int) (dY / length); - if (udY == 0 && udY != 0) { - if (dY > 0) { - udY = 1; - } else { - udY = -1; - } + + // 随机移动 TODO 确诊无法移动 + private void action() { + if (state == PersonState.FREEZE) { + return; + } + + if (!wantMove()) { + return; + } + + if (moveTarget == null || moveTarget.isArrived()) { + double targetX = targetSig * ThreadLocalRandom.current().nextGaussian() + targetXU; + double targetY = targetSig * ThreadLocalRandom.current().nextGaussian() + targetYU; + moveTarget = new MoveTarget((int) targetX, (int) targetY); + } + + int dX = moveTarget.getX() - x; + int dY = moveTarget.getY() - y; + double length = Math.sqrt(Math.pow(dX, 2) + Math.pow(dY, 2)); + + if (length < 1) { + moveTarget.setArrived(true); + return; + } + int udX = (int) (dX / length); + if (udX == 0 && dX != 0) { + if (dX > 0) { + udX = 1; + } else { + udX = -1; + } + } + int udY = (int) (dY / length); + if (x > 700) { + moveTarget = null; + if (udX > 0) { + udX = -udX; + } + } + this.moveTo(udX, udY); } - if (x > 700) { - moveTarget = null; - if (udX > 0) { - udX = -udX; - } + public void update() { + functions.get(state).accept(this); } - this.moveTo(udX, udY); - } - - public void update() { - functions.get(state).accept(this); - } - - static { - functions.put(PersonState.NORMAL, p -> { - p.action(); - p.willInfected(); - }); - - functions.put(PersonState.SHADOW, p -> { - if (DisplayPanel.worldTime - p.infectedTime > Constants.SHADOW_TIME) { - p.confirmed(); - } - p.action(); - }); - - functions.put(PersonState.CONFIRMED, p -> { - if (DisplayPanel.worldTime - p.confirmedTime >= Constants.HOSPITAL_RECEIVE_TIME) { - Bed bed = Hospital.INSTANCE.pickBed(); - p.freeze(bed); - } else { - p.action(); - } - - int rate = ThreadLocalRandom.current().nextInt(Constants.BASE_RATE); - if (rate <= Constants.DEAD_RATE) { - p.dead(); - } - }); - - functions.put(PersonState.FREEZE, p -> { - // 看作数轴上两个线段 - int rate = ThreadLocalRandom.current().nextInt(Constants.BASE_RATE); - if (rate <= Constants.CURE_RATE) { - p.cure(); - } else if (rate <= Constants.DEAD_RATE + Constants.CURE_RATE) { - p.dead(); - } - }); - - functions.put(PersonState.DEAD, p -> { + + static { + functions.put(PersonState.NORMAL, p -> { + p.action(); + p.willInfected(); + }); + + functions.put(PersonState.SHADOW, p -> { + if (DisplayPanel.worldTime - p.infectedTime > Constants.SHADOW_TIME) { + p.confirmed(); + } + p.action(); + }); + + functions.put(PersonState.CONFIRMED, p -> { + if (DisplayPanel.worldTime - p.confirmedTime >= Constants.HOSPITAL_RECEIVE_TIME) { + Bed bed = Hospital.INSTANCE.pickBed(); + p.freeze(bed); + } else { + p.action(); + } + + int rate = ThreadLocalRandom.current().nextInt(Constants.BASE_RATE); + if (rate <= Constants.DEAD_RATE) { + p.dead(); + } + }); + + functions.put(PersonState.FREEZE, p -> { + // 看作数轴上两个线段 + int rate = ThreadLocalRandom.current().nextInt(Constants.BASE_RATE); + if (rate <= Constants.CURE_RATE) { + p.cure(); + } else if (rate <= Constants.DEAD_RATE + Constants.CURE_RATE) { + p.dead(); + } + }); + + functions.put(PersonState.DEAD, p -> { // log.info("dead: person={}", p); - City.getInstance().dead(p); - }); - } + City.getInstance().dead(p); + }); + } } diff --git a/gui/src/main/java/com/github/kuangcp/virusbroadcast/gui/DisplayPanel.java b/gui/src/main/java/com/github/kuangcp/virusbroadcast/gui/DisplayPanel.java index d2f618f9..b452327d 100644 --- a/gui/src/main/java/com/github/kuangcp/virusbroadcast/gui/DisplayPanel.java +++ b/gui/src/main/java/com/github/kuangcp/virusbroadcast/gui/DisplayPanel.java @@ -1,16 +1,16 @@ package com.github.kuangcp.virusbroadcast.gui; import com.github.kuangcp.virusbroadcast.Hospital; -import com.github.kuangcp.virusbroadcast.domain.City; import com.github.kuangcp.virusbroadcast.constant.PersonState; +import com.github.kuangcp.virusbroadcast.domain.City; import com.github.kuangcp.virusbroadcast.domain.Person; -import java.awt.Color; -import java.awt.Graphics; +import lombok.extern.slf4j.Slf4j; + +import javax.swing.*; +import java.awt.*; import java.util.HashMap; import java.util.List; import java.util.Map; -import javax.swing.JPanel; -import lombok.extern.slf4j.Slf4j; /** * @@ -18,78 +18,85 @@ @Slf4j public class DisplayPanel extends JPanel implements Runnable { - /** - * 时间线 day - */ - public static int worldTime = 0; - - private volatile boolean runnable = true; - - public DisplayPanel() { - this.setBackground(new Color(0x444444)); - } + /** + * 时间线 day + */ + public static int worldTime = 0; - private static Map colorMap = new HashMap<>(); + private volatile boolean runnable = true; - static { - colorMap.put(PersonState.NORMAL, Color.white); - colorMap.put(PersonState.SHADOW, Color.yellow); - colorMap.put(PersonState.CONFIRMED, Color.red); - colorMap.put(PersonState.FREEZE, Color.blue); - } - - @Override - public void paint(Graphics graphics) { - super.paint(graphics); - - //draw border - graphics.setColor(Color.blue); - Hospital hospital = Hospital.INSTANCE; - graphics.drawRect(hospital.getX(), hospital.getY(), hospital.getWidth(), hospital.getHeight()); - - List personList = City.getInstance().getPersonList(); - - if (personList.isEmpty()) { - this.stop(false); - return; + public DisplayPanel() { + this.setBackground(new Color(0x444444)); } - int sum = 0; - for (Person person : personList) { - if (person.isInfected()) { - sum++; - } - Color color = colorMap.get(person.getState()); - graphics.setColor(color); + private static final Map colorMap = new HashMap<>(); - person.update(); - graphics.fillOval(person.getX(), person.getY(), 3, 3); + static { + colorMap.put(PersonState.NORMAL, Color.white); + colorMap.put(PersonState.SHADOW, Color.yellow); + colorMap.put(PersonState.CONFIRMED, Color.red); + colorMap.put(PersonState.FREEZE, Color.blue); } - if (sum == 0) { - this.stop(true); + + @Override + public void paint(Graphics graphics) { + super.paint(graphics); + + //draw border + graphics.setColor(Color.blue); + Hospital hospital = Hospital.INSTANCE; + graphics.drawRect(hospital.getX(), hospital.getY(), hospital.getWidth(), hospital.getHeight()); + + List personList = City.getInstance().getPersonList(); + + if (personList.isEmpty()) { + this.stop(false); + return; + } + + int sum = 0; + for (Person person : personList) { + if (person.isInfected()) { + sum++; + } + Color color = colorMap.get(person.getState()); + graphics.setColor(color); + + person.update(); + graphics.fillOval(person.getX(), person.getY(), 3, 3); + } + final City.Status status = City.getInstance().buildCityInfo(); + String text = "day:" + worldTime + " " + status.toString(); + graphics.setColor(Color.yellow); + if (sum == 0) { + text = "End of epidemic situation!"; + graphics.drawString(text, 10, 10); + this.stop(true); + } else { + graphics.drawString(text, 10, 10); + } } - } - - @Override - public void run() { - while (runnable) { - this.repaint(); - - try { - Thread.sleep(30); - worldTime++; - } catch (InterruptedException e) { - log.error("", e); - } + + @Override + public void run() { + while (runnable) { + this.repaint(); + + try { + Thread.sleep(20); + worldTime++; + } catch (InterruptedException e) { + log.error("", e); + } + } } - } - - public synchronized void stop(boolean survival) { - this.runnable = false; - if (survival) { - log.warn("End of epidemic situation!"); - } else { - log.warn("No one survived!"); + + public synchronized void stop(boolean survival) { + this.runnable = false; + if (survival) { + log.warn("End of epidemic situation!"); + } else { + log.warn("No one survived!"); + } } - } } From ae7c96f8a8d6dd2a7aa7fb23fa18705bfc849424 Mon Sep 17 00:00:00 2001 From: kuangcp Date: Mon, 28 Aug 2023 10:47:07 +0800 Subject: [PATCH 278/476] fix: fall version --- pom.xml | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/pom.xml b/pom.xml index 2fd58fc2..452fe656 100644 --- a/pom.xml +++ b/pom.xml @@ -82,17 +82,17 @@ com.github.kuangcp kcp-tool - 1.0.8 + 1.0.7 com.github.kuangcp kcp-tuple - 1.0.8 + 1.0.7 com.github.kuangcp kcp-core - 1.0.8 + 1.0.7 org.apache.commons @@ -426,6 +426,11 @@ + + gitee + kuangcp gitee + https://gitee.com/gin9/MavenRepos/raw/master + maven-restlet Public online Restlet repository @@ -439,13 +444,5 @@ true - - gitee - kuangcp gitee - https://gitee.com/gin9/MavenRepos/raw/master - - true - - From 50d65a4e86547f431320973766e27b7f038a7676 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Sat, 26 Aug 2023 16:22:50 +0800 Subject: [PATCH 279/476] Revert "rm: other mod" This reverts commit 2205d3d0c89648ed137afcfb907f871f7d3d72b8. --- flink/bootstrap.sh | 35 +++++ flink/build.gradle | 28 ++++ flink/host.conf | 2 + flink/run.sh | 5 + .../github/kuangcp/hi/SimpleJobListener.java | 28 ++++ .../com/github/kuangcp/hi/SimpleSink.java | 53 +++++++ .../com/github/kuangcp/hi/SimpleSource.java | 52 +++++++ .../github/kuangcp/hi/SimpleStatistic.java | 87 +++++++++++ flink/src/main/resources/log4j.properties | 9 ++ flink/upload.sh | 6 + hadoop/build.gradle | 12 ++ .../hdfs/hi/GeneralFileActionDemo.java | 133 +++++++++++++++++ .../hdfs/hi/GeneralFileActionDemoTest.java | 56 +++++++ kafka/build.gradle | 7 + .../java/com/github/kuangcp/hi/Constants.java | 17 +++ .../com/github/kuangcp/hi/ConsumerDemo.java | 74 ++++++++++ .../com/github/kuangcp/hi/ProducerDemo.java | 133 +++++++++++++++++ .../hi/domain/ProductStatisticJobCommand.java | 41 ++++++ .../hi/domain/ProductStatisticSpan.java | 62 ++++++++ .../kuangcp/hi/domain/StartCommand.java | 20 +++ .../kuangcp/hi/domain/StatisticSpan.java | 25 ++++ kafka/src/main/resources/kafka.properties | 2 + .../github/kuangcp/hi/ConsumerDemoTest.java | 22 +++ .../github/kuangcp/hi/ProducerDemoTest.java | 25 ++++ kafka/src/test/resources/logback-test.xml | 21 +++ mybatis/build.gradle | 21 +++ mybatis/create.sql | 16 ++ .../java/com/github/kuangcp/Application.java | 14 ++ .../kuangcp/sharding/manual/AuthUtil.java | 24 +++ .../manual/ConsistentHashingAlgorithm.java | 100 +++++++++++++ .../manual/ShardingAlgorithmEnum.java | 46 ++++++ .../manual/ShardingAlgorithmType.java | 9 ++ .../sharding/manual/ShardingTable.java | 16 ++ .../manual/ShardingTableInterceptor.java | 137 ++++++++++++++++++ .../simple/customer/dao/CustomerDao.java | 25 ++++ .../simple/customer/domain/Customer.java | 22 +++ .../kuangcp/simple/order/dao/OrderDao.java | 10 ++ .../kuangcp/simple/order/domain/Order.java | 28 ++++ .../kuangcp/simple/order/dto/OrderDTO.java | 24 +++ .../simple/order/service/OrderService.java | 11 ++ .../order/service/impl/OrderServiceImpl.java | 86 +++++++++++ .../com/github/kuangcp/stream/Report.java | 36 +++++ .../github/kuangcp/stream/dao/ReportDao.java | 42 ++++++ mybatis/src/main/resources/application.yml | 6 + mybatis/src/main/resources/logback.xml | 79 ++++++++++ .../kuangcp/base/SpringBootTestStarter.java | 11 ++ .../ConsistentHashingAlgorithmTest.java | 57 ++++++++ .../dao/CustomerDaoSpringBootTest.java | 45 ++++++ .../order/dao/OrderDaoSpringBootTest.java | 65 +++++++++ .../service/OrderServiceSpringBootTest.java | 23 +++ .../stream/CursorSessionSpringBootTest.java | 118 +++++++++++++++ .../java/com/github/kuangcp/stream/Readme.md | 11 ++ 52 files changed, 2037 insertions(+) create mode 100755 flink/bootstrap.sh create mode 100644 flink/build.gradle create mode 100644 flink/host.conf create mode 100755 flink/run.sh create mode 100644 flink/src/main/java/com/github/kuangcp/hi/SimpleJobListener.java create mode 100644 flink/src/main/java/com/github/kuangcp/hi/SimpleSink.java create mode 100644 flink/src/main/java/com/github/kuangcp/hi/SimpleSource.java create mode 100644 flink/src/main/java/com/github/kuangcp/hi/SimpleStatistic.java create mode 100644 flink/src/main/resources/log4j.properties create mode 100755 flink/upload.sh create mode 100644 hadoop/build.gradle create mode 100644 hadoop/src/main/java/com/github/kuangcp/hdfs/hi/GeneralFileActionDemo.java create mode 100644 hadoop/src/test/java/com/github/kuangcp/hdfs/hi/GeneralFileActionDemoTest.java create mode 100644 kafka/build.gradle create mode 100644 kafka/src/main/java/com/github/kuangcp/hi/Constants.java create mode 100644 kafka/src/main/java/com/github/kuangcp/hi/ConsumerDemo.java create mode 100644 kafka/src/main/java/com/github/kuangcp/hi/ProducerDemo.java create mode 100644 kafka/src/main/java/com/github/kuangcp/hi/domain/ProductStatisticJobCommand.java create mode 100644 kafka/src/main/java/com/github/kuangcp/hi/domain/ProductStatisticSpan.java create mode 100644 kafka/src/main/java/com/github/kuangcp/hi/domain/StartCommand.java create mode 100644 kafka/src/main/java/com/github/kuangcp/hi/domain/StatisticSpan.java create mode 100644 kafka/src/main/resources/kafka.properties create mode 100644 kafka/src/test/java/com/github/kuangcp/hi/ConsumerDemoTest.java create mode 100644 kafka/src/test/java/com/github/kuangcp/hi/ProducerDemoTest.java create mode 100644 kafka/src/test/resources/logback-test.xml create mode 100644 mybatis/build.gradle create mode 100644 mybatis/create.sql create mode 100644 mybatis/src/main/java/com/github/kuangcp/Application.java create mode 100644 mybatis/src/main/java/com/github/kuangcp/sharding/manual/AuthUtil.java create mode 100644 mybatis/src/main/java/com/github/kuangcp/sharding/manual/ConsistentHashingAlgorithm.java create mode 100644 mybatis/src/main/java/com/github/kuangcp/sharding/manual/ShardingAlgorithmEnum.java create mode 100644 mybatis/src/main/java/com/github/kuangcp/sharding/manual/ShardingAlgorithmType.java create mode 100644 mybatis/src/main/java/com/github/kuangcp/sharding/manual/ShardingTable.java create mode 100644 mybatis/src/main/java/com/github/kuangcp/sharding/manual/ShardingTableInterceptor.java create mode 100644 mybatis/src/main/java/com/github/kuangcp/simple/customer/dao/CustomerDao.java create mode 100644 mybatis/src/main/java/com/github/kuangcp/simple/customer/domain/Customer.java create mode 100644 mybatis/src/main/java/com/github/kuangcp/simple/order/dao/OrderDao.java create mode 100644 mybatis/src/main/java/com/github/kuangcp/simple/order/domain/Order.java create mode 100644 mybatis/src/main/java/com/github/kuangcp/simple/order/dto/OrderDTO.java create mode 100644 mybatis/src/main/java/com/github/kuangcp/simple/order/service/OrderService.java create mode 100644 mybatis/src/main/java/com/github/kuangcp/simple/order/service/impl/OrderServiceImpl.java create mode 100644 mybatis/src/main/java/com/github/kuangcp/stream/Report.java create mode 100644 mybatis/src/main/java/com/github/kuangcp/stream/dao/ReportDao.java create mode 100644 mybatis/src/main/resources/application.yml create mode 100644 mybatis/src/main/resources/logback.xml create mode 100644 mybatis/src/test/java/com/github/kuangcp/base/SpringBootTestStarter.java create mode 100644 mybatis/src/test/java/com/github/kuangcp/sharding/manual/ConsistentHashingAlgorithmTest.java create mode 100644 mybatis/src/test/java/com/github/kuangcp/simple/customer/dao/CustomerDaoSpringBootTest.java create mode 100644 mybatis/src/test/java/com/github/kuangcp/simple/order/dao/OrderDaoSpringBootTest.java create mode 100644 mybatis/src/test/java/com/github/kuangcp/simple/order/service/OrderServiceSpringBootTest.java create mode 100644 mybatis/src/test/java/com/github/kuangcp/stream/CursorSessionSpringBootTest.java create mode 100644 mybatis/src/test/java/com/github/kuangcp/stream/Readme.md diff --git a/flink/bootstrap.sh b/flink/bootstrap.sh new file mode 100755 index 00000000..80c6d711 --- /dev/null +++ b/flink/bootstrap.sh @@ -0,0 +1,35 @@ +. ./host.conf +result=$(./upload.sh) + +temp=${result#*flink-web-upload/} +jarId=${temp%%\"*} + +echo "\n start run: " $jarId "\n" + +# 当 job 执行完成后, get请求 才会返回 +run(){ + jobResult=$(./run.sh $1) + + echo $jobResult + temp=${jobResult#*:\"} + jobId=${temp%%\"\}*} + + echo $(date) "jobId: " $jobId +} + +# 当 job 在执行的时候, 并不会在 jobs/ +jobs(){ + host=$1 + for i in $(seq 10); do + sleep 1 + echo "\n start curl " $host/jobs/ "\n" + + curl $host/jobs + + echo "" + done; +} + +jobs $host & + +run $jarId \ No newline at end of file diff --git a/flink/build.gradle b/flink/build.gradle new file mode 100644 index 00000000..114821ab --- /dev/null +++ b/flink/build.gradle @@ -0,0 +1,28 @@ +version = '1.0.0-SNAPSHOT' + +apply plugin: 'java' +apply plugin: 'application' + +dependencies { + implementation project(":common-config") + implementation libs['flink-java'] + implementation libs['flink-clients'] + implementation libs['flink-streaming-java'] +} + +application { + mainClassName = "com.github.kuangcp.hi.SimpleStatistic" +} + +task uberJar(type: Jar) { + archiveClassifier = 'all-dependency' + + from sourceSets.main.output + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith('jar') }.collect { zipTree(it) } + } + manifest { + attributes 'Main-Class': 'com.github.kuangcp.hi.SimpleStatistic' + } +} diff --git a/flink/host.conf b/flink/host.conf new file mode 100644 index 00000000..b80c9610 --- /dev/null +++ b/flink/host.conf @@ -0,0 +1,2 @@ +host=http://127.0.0.1:8081 +# host=http://127.0.0.1:8081 diff --git a/flink/run.sh b/flink/run.sh new file mode 100755 index 00000000..369101ea --- /dev/null +++ b/flink/run.sh @@ -0,0 +1,5 @@ +. ./host.conf +jarId=$1 + +curl -X POST $host/jars/$jarId/run\?entry-class\=com.github.kuangcp.hi.SimpleStatistic + diff --git a/flink/src/main/java/com/github/kuangcp/hi/SimpleJobListener.java b/flink/src/main/java/com/github/kuangcp/hi/SimpleJobListener.java new file mode 100644 index 00000000..2e3ad108 --- /dev/null +++ b/flink/src/main/java/com/github/kuangcp/hi/SimpleJobListener.java @@ -0,0 +1,28 @@ +package com.github.kuangcp.hi; + +import static org.apache.flink.runtime.jobgraph.JobStatus.FINISHED; + +import org.apache.flink.api.common.JobID; +import org.apache.flink.runtime.executiongraph.ExecutionGraph; +import org.apache.flink.runtime.executiongraph.JobStatusListener; +import org.apache.flink.runtime.jobgraph.JobStatus; + +/** + * 如果该对象能注入到 下面类的属性上, 问题就简单一些了 + * + * @author https://github.com/kuangcp on 2019-07-08 11:22 + * @see ExecutionGraph jobStatusListeners + */ +@Deprecated +public class SimpleJobListener implements JobStatusListener { + + @Override + public void jobStatusChanges(JobID jobId, JobStatus newJobStatus, long timestamp, + Throwable error) { + System.out.println(jobId); + + if (FINISHED.equals(newJobStatus)) { + System.out.println("complete"); + } + } +} diff --git a/flink/src/main/java/com/github/kuangcp/hi/SimpleSink.java b/flink/src/main/java/com/github/kuangcp/hi/SimpleSink.java new file mode 100644 index 00000000..58f3970a --- /dev/null +++ b/flink/src/main/java/com/github/kuangcp/hi/SimpleSink.java @@ -0,0 +1,53 @@ +package com.github.kuangcp.hi; + +import java.io.IOException; +import java.util.LinkedList; +import java.util.List; +import lombok.EqualsAndHashCode; +import lombok.extern.slf4j.Slf4j; +import org.apache.flink.api.common.io.RichOutputFormat; +import org.apache.flink.api.java.tuple.Tuple2; +import org.apache.flink.configuration.Configuration; + +/** + * 模拟输出到文件 + * + * @author https://github.com/kuangcp + * @since 2019-05-30 20:36 + */ +@lombok.Data +@Slf4j +@EqualsAndHashCode(callSuper = true) +public class SimpleSink extends RichOutputFormat> { + + private String name; + private long createTime = System.currentTimeMillis(); + // 如果给属性加上 transient 修饰, 就会报错 因为无法同步数据 + private List> resultList = new LinkedList<>(); + + SimpleSink(String name) { + this.name = name; + } + + @Override + public void configure(Configuration parameters) { + } + + @Override + public void open(int taskNumber, int numTasks) throws IOException { + } + + @Override + public void writeRecord(Tuple2 record) throws IOException { + resultList.add(record); + } + + @Override + public void close() { + try { + resultList.forEach(t -> log.info("close by sink: name={} count={}", t.f0, t.f1)); + } catch (Exception e) { + log.error(e.getMessage(), e); + } + } +} diff --git a/flink/src/main/java/com/github/kuangcp/hi/SimpleSource.java b/flink/src/main/java/com/github/kuangcp/hi/SimpleSource.java new file mode 100644 index 00000000..23afafd2 --- /dev/null +++ b/flink/src/main/java/com/github/kuangcp/hi/SimpleSource.java @@ -0,0 +1,52 @@ +package com.github.kuangcp.hi; + +import java.time.Month; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import lombok.extern.slf4j.Slf4j; + +/** + * 模拟数据库分页查询 + * + * @author https://github.com/kuangcp 2019-06-16 15:12 + */ +@Slf4j +class SimpleSource { + + private String id; + + private static final int pageNum = 40; + private static final int pageSize = 500; + + private int cursor; + + SimpleSource(String id) { + this.id = id; + } + + boolean hasNextPage() { + return cursor < pageNum; + } + + List generateResource() { + delayTime(); + cursor++; + log.info("source: id={} cursor={}", id, cursor); + return IntStream.rangeClosed(1, pageSize) + .mapToObj(i -> Month.of((i + cursor) % 12 + 1).toString()) + .collect(Collectors.toList()); + } + + /** + * 延长Flink执行时间 + */ + private void delayTime() { + try { + TimeUnit.MILLISECONDS.sleep(300); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } +} diff --git a/flink/src/main/java/com/github/kuangcp/hi/SimpleStatistic.java b/flink/src/main/java/com/github/kuangcp/hi/SimpleStatistic.java new file mode 100644 index 00000000..29afd40f --- /dev/null +++ b/flink/src/main/java/com/github/kuangcp/hi/SimpleStatistic.java @@ -0,0 +1,87 @@ +package com.github.kuangcp.hi; + +import java.util.List; +import java.util.Objects; + +import java.util.concurrent.TimeUnit; +import lombok.extern.slf4j.Slf4j; +import org.apache.flink.api.common.JobExecutionResult; +import org.apache.flink.api.common.functions.MapFunction; +import org.apache.flink.api.java.DataSet; +import org.apache.flink.api.java.ExecutionEnvironment; +import org.apache.flink.api.java.operators.AggregateOperator; +import org.apache.flink.api.java.operators.DataSource; +import org.apache.flink.api.java.tuple.Tuple2; + +/** + * Batch 的 调用是通过 REST API的, 只能在发起请求的地方依据返回值来判断是否执行完成和成功 + * + * @author https://github.com/kuangcp + * @since 2019-05-16 16:26 + */ +@Slf4j +public class SimpleStatistic { + + private static final ExecutionEnvironment ENV; + private static final int taskNum = 1; + + static { + ENV = ExecutionEnvironment.getExecutionEnvironment(); + } + + public static void main(String[] args) throws Exception { + log.info("start calculate"); + try { + for (int i = 0; i < taskNum; i++) { + calculateAndAggregate("batch-" + i); + } + } catch (Throwable e) { + log.error(e.getMessage(), e); + } + + ENV.execute("SimpleStatistic"); + } + + /** + * 分页计算并聚合 + */ + private static void calculateAndAggregate(String batchId) { + AggregateOperator> result = null; + + SimpleSource provider = new SimpleSource(batchId); + while (provider.hasNextPage()) { + List data = provider.generateResource(); + DataSource source = ENV.fromCollection(data); + + // 合并窗口内数据 + DataSet> counts = source + .filter(Objects::nonNull) + .map(new Mapper()) + .groupBy(0) + .sum(1); + + // 当前窗口内和历史数据合并 + if (Objects.isNull(result)) { + result = counts.groupBy(0).sum(1).name("cache"); + } else { + DataSet> temp = result.union(counts).name("union cache"); + result = temp.groupBy(0).sum(1).name("merge cache"); + } + } + + if (Objects.isNull(result)) { + return; + } + + result.output(new SimpleSink(batchId)); + } + + public static final class Mapper implements + MapFunction> { + + @Override + public Tuple2 map(String value) { + return new Tuple2<>(value, 1); + } + } +} diff --git a/flink/src/main/resources/log4j.properties b/flink/src/main/resources/log4j.properties new file mode 100644 index 00000000..b4decd71 --- /dev/null +++ b/flink/src/main/resources/log4j.properties @@ -0,0 +1,9 @@ +log4j.rootLogger=INFO, stdout + +# Direct log messages to stdout +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.Target=System.out +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} [%t] %-5p %c{1}:%L - %m%n + +log4j.logger.com.baturu.ofc.batch = debug diff --git a/flink/upload.sh b/flink/upload.sh new file mode 100755 index 00000000..4a320dd3 --- /dev/null +++ b/flink/upload.sh @@ -0,0 +1,6 @@ +. ./host.conf +file=$(find . -iname "*.jar*") +echo $file + +curl -X POST -H "Expect:" -F "jarfile=@$file" $host/jars/upload + diff --git a/hadoop/build.gradle b/hadoop/build.gradle new file mode 100644 index 00000000..b20508c7 --- /dev/null +++ b/hadoop/build.gradle @@ -0,0 +1,12 @@ +dependencies { + implementation project(":common-config") + implementation (libs["hadoop-common"]) { + exclude group: 'org.slf4j' + } + implementation (libs["hadoop-client"]){ + exclude group: 'org.slf4j' + } + implementation (libs["hadoop-hdfs"]){ + exclude group: 'org.slf4j' + } +} \ No newline at end of file diff --git a/hadoop/src/main/java/com/github/kuangcp/hdfs/hi/GeneralFileActionDemo.java b/hadoop/src/main/java/com/github/kuangcp/hdfs/hi/GeneralFileActionDemo.java new file mode 100644 index 00000000..fb2f85b5 --- /dev/null +++ b/hadoop/src/main/java/com/github/kuangcp/hdfs/hi/GeneralFileActionDemo.java @@ -0,0 +1,133 @@ +package com.github.kuangcp.hdfs.hi; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.net.URI; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; +import lombok.extern.slf4j.Slf4j; +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.fs.FSDataInputStream; +import org.apache.hadoop.fs.FSDataOutputStream; +import org.apache.hadoop.fs.FileStatus; +import org.apache.hadoop.fs.FileSystem; +import org.apache.hadoop.fs.Path; + +/** + * @author https://github.com/kuangcp + * @date 2019-05-20 11:15 + */ +@Slf4j +public class GeneralFileActionDemo { + + public static boolean createDirectory(String url) throws IOException { + FileSystem fileSystem = FileSystem.get(getConfig(url)); + boolean result = fileSystem.mkdirs(new Path(url)); + log.info("result={}", result); + return result; + } + + public static void createNewFile(String url, String content) + throws IOException, InterruptedException { + FileSystem fs = FileSystem.get(getConfig(url)); + + FSDataOutputStream os = fs.create(new Path(URI.create(url).getPath())); + os.write(content.getBytes(StandardCharsets.UTF_8)); + os.flush(); + TimeUnit.SECONDS.sleep(4); + os.close(); + fs.close(); + } + + public static List listFiles(String url) throws IOException { + FileSystem fs = FileSystem.get(URI.create(url), getConfig(url)); + Path path = new Path(url); + FileStatus[] status = fs.listStatus(path); + + return Arrays.stream(status).map(v -> v.getPath().toString()).collect(Collectors.toList()); + //方法1 +// for (FileStatus f : status) { +// log.info(f.getPath().toString()); +// } + + //方法2 +// Path[] listedPaths = FileUtil.stat2Paths(status); +// for (Path p : listedPaths) { +// System.out.println(p.toString()); +// } + } + + public static boolean deleteByURL(String url) throws IOException { + FileSystem fs = FileSystem.get(getConfig(url)); + Path path = new Path(url); + boolean isDeleted = fs.deleteOnExit(path); + fs.close(); + return isDeleted; + } + + public static void writeFileToHDFS() throws IOException { + Configuration configuration = new Configuration(); + configuration.set("fs.defaultFS", "hdfs://localhost:9000"); + FileSystem fileSystem = FileSystem.get(configuration); + //Create a path + String fileName = "read_write_hdfs_example.txt"; + Path hdfsWritePath = new Path("/user/javadeveloperzone/javareadwriteexample/" + fileName); + FSDataOutputStream fsDataOutputStream = fileSystem.create(hdfsWritePath, true); + BufferedWriter bufferedWriter = new BufferedWriter( + new OutputStreamWriter(fsDataOutputStream, StandardCharsets.UTF_8)); + bufferedWriter.write("Java API to write data in HDFS"); + bufferedWriter.newLine(); + bufferedWriter.close(); + fileSystem.close(); + } + + /** + * read the hdfs file content + * + * notice that the url is the full path name + */ + public static void readHDFSFile(String url) throws Exception { + FileSystem fs = FileSystem.get(getConfig(url)); + Path path = new Path(url); + + // check if the file exists + if (!fs.exists(path)) { + throw new Exception("the file is not found ."); + } + + FSDataInputStream is = fs.open(path); + // read line by line + BufferedReader bufferedReader = new BufferedReader( + new InputStreamReader(is, StandardCharsets.UTF_8)); + String line; + while ((line = bufferedReader.readLine()) != null) { + System.out.println(line); + } + + // read as byte[] +// FileStatus stat = fs.getFileStatus(path); +// byte[] buffer = new byte[Integer.parseInt(String.valueOf(stat.getLen()))]; +// is.readFully(0, buffer); + + is.close(); + fs.close(); + } + + private static Configuration getConfig(String url) { + Configuration config = new Configuration(); + config.set("fs.defaultFS", getHost(url)); + + return config; + } + + private static String getHost(String url) { + URI uri = URI.create(url); + return uri.getScheme() + "://" + uri.getHost() + ":" + uri.getPort(); + } +} diff --git a/hadoop/src/test/java/com/github/kuangcp/hdfs/hi/GeneralFileActionDemoTest.java b/hadoop/src/test/java/com/github/kuangcp/hdfs/hi/GeneralFileActionDemoTest.java new file mode 100644 index 00000000..454788ae --- /dev/null +++ b/hadoop/src/test/java/com/github/kuangcp/hdfs/hi/GeneralFileActionDemoTest.java @@ -0,0 +1,56 @@ +package com.github.kuangcp.hdfs.hi; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.IsEqual.equalTo; + +import java.io.IOException; +import java.util.List; +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; + +/** + * @author https://github.com/kuangcp + * @date 2019-05-20 11:28 + */ +@Slf4j +public class GeneralFileActionDemoTest { + + private static final String HDFS_HOST = "hdfs://172.16.16.80:8020"; + + @Test + public void testCreateDirectory() throws Exception { + boolean result = GeneralFileActionDemo.createDirectory(HDFS_HOST + "/flink-batch/test"); + assertThat(result, equalTo(true)); + } + + @Test + public void testCreateFile() throws IOException, InterruptedException { + GeneralFileActionDemo.createNewFile(HDFS_HOST + "/input/b.md", "fdasfasdfasd"); + } + + @Test + public void testList() throws IOException { + List files = GeneralFileActionDemo.listFiles(HDFS_HOST + "/flink-batch"); + files.forEach(v -> { + log.info("{}", v); + try { + GeneralFileActionDemo.deleteByURL(v); + } catch (IOException e) { + e.printStackTrace(); + } + }); + } + + @Test + public void testDelete() throws IOException { + boolean result = GeneralFileActionDemo + .deleteByURL(HDFS_HOST + "/flink-batch/1559008370602_MONTH_2019-03-01_2019-03-27_SPU.csv"); + System.out.println(result); + } + + @Test + public void testRead() throws Exception { + String url = "/flink-batch/1559008370602_MONTH_2019-03-01_2019-03-27_SPU.csv"; + GeneralFileActionDemo.readHDFSFile(HDFS_HOST + url); + } +} \ No newline at end of file diff --git a/kafka/build.gradle b/kafka/build.gradle new file mode 100644 index 00000000..b6fd107d --- /dev/null +++ b/kafka/build.gradle @@ -0,0 +1,7 @@ +dependencies { + implementation project(":common-config") + implementation libs['kcp-tool'] + implementation libs['kafka-clients'] + implementation libs['jackson_core'] + implementation libs['jackson_databind'] +} \ No newline at end of file diff --git a/kafka/src/main/java/com/github/kuangcp/hi/Constants.java b/kafka/src/main/java/com/github/kuangcp/hi/Constants.java new file mode 100644 index 00000000..8a1c0ae3 --- /dev/null +++ b/kafka/src/main/java/com/github/kuangcp/hi/Constants.java @@ -0,0 +1,17 @@ +package com.github.kuangcp.hi; + +/** + * @author kuangcp + * + * Date: 2019-05-20 00:16 + */ +public interface Constants { + + String KAFKA_SERVER = "127.0.0.1:9092"; + + String HI_TOPIC = "Hi"; + + String START_TOPIC = "start"; + + String COMMAND_TOPIC = "OFC_PRODUCT_STATISTIC_JOB_DISPATCHING"; +} diff --git a/kafka/src/main/java/com/github/kuangcp/hi/ConsumerDemo.java b/kafka/src/main/java/com/github/kuangcp/hi/ConsumerDemo.java new file mode 100644 index 00000000..bbc736d0 --- /dev/null +++ b/kafka/src/main/java/com/github/kuangcp/hi/ConsumerDemo.java @@ -0,0 +1,74 @@ +package com.github.kuangcp.hi; + +import static com.github.kuangcp.hi.Constants.HI_TOPIC; +import static com.github.kuangcp.hi.Constants.KAFKA_SERVER; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.kuangcp.hi.domain.ProductStatisticJobCommand; +import com.github.kuangcp.kafka.KafkaConsumerUtil; +import com.github.kuangcp.kafka.common.SimpleMessageExecutor; +import java.io.IOException; +import java.time.Duration; +import java.util.Collections; +import java.util.Properties; +import lombok.extern.slf4j.Slf4j; +import org.apache.kafka.clients.consumer.ConsumerRecord; +import org.apache.kafka.clients.consumer.ConsumerRecords; +import org.apache.kafka.clients.consumer.KafkaConsumer; +import org.apache.kafka.common.serialization.StringDeserializer; + +/** + * @author kuangcp + * + * Date: 2019-05-20 00:11 + */ +@Slf4j +public class ConsumerDemo { + + private static Properties properties = getConf(); + private static ObjectMapper mapper = new ObjectMapper(); + + static void receiveHi() { + SimpleMessageExecutor executor = new SimpleMessageExecutor() { + @Override + public void execute(String message) { + log.info("message={}", message); + } + + @Override + public String getTopic() { + return HI_TOPIC; + } + }; + + KafkaConsumerUtil.consumerPlainText(Duration.ofMillis(1000), + Collections.singletonList(executor)); + } + + static void receiveCommand() throws IOException { + KafkaConsumer kafkaConsumer = new KafkaConsumer<>(properties); + kafkaConsumer.subscribe(Collections.singletonList(Constants.COMMAND_TOPIC)); + while (true) { + ConsumerRecords records = kafkaConsumer.poll(Duration.ofMillis(100)); + for (ConsumerRecord record : records) { + log.info("offset = {}, value = {}", record.offset(), record.value()); + ProductStatisticJobCommand command = mapper + .readValue(record.value(), ProductStatisticJobCommand.class); + System.out.println(command); + } + } + } + + private static Properties getConf() { + Properties properties = new Properties(); + properties.put("bootstrap.servers", KAFKA_SERVER); + properties.put("group.id", "group-1"); + properties.put("enable.auto.commit", "true"); + properties.put("auto.commit.interval.ms", "1000"); + properties.put("auto.offset.reset", "earliest"); + properties.put("session.timeout.ms", "30000"); + properties.put("key.deserializer", StringDeserializer.class.getName()); + properties.put("value.deserializer", StringDeserializer.class.getName()); + return properties; + } +} \ No newline at end of file diff --git a/kafka/src/main/java/com/github/kuangcp/hi/ProducerDemo.java b/kafka/src/main/java/com/github/kuangcp/hi/ProducerDemo.java new file mode 100644 index 00000000..ae93a55e --- /dev/null +++ b/kafka/src/main/java/com/github/kuangcp/hi/ProducerDemo.java @@ -0,0 +1,133 @@ +package com.github.kuangcp.hi; + +import static com.github.kuangcp.hi.Constants.HI_TOPIC; +import static com.github.kuangcp.hi.Constants.KAFKA_SERVER; +import static com.github.kuangcp.hi.Constants.START_TOPIC; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.kuangcp.hi.domain.ProductStatisticJobCommand; +import com.github.kuangcp.hi.domain.ProductStatisticSpan; +import com.github.kuangcp.hi.domain.StartCommand; +import com.github.kuangcp.io.ResourceTool; +import java.io.IOException; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.Date; +import java.util.HashSet; +import java.util.Properties; +import java.util.UUID; +import lombok.extern.slf4j.Slf4j; +import org.apache.kafka.clients.producer.KafkaProducer; +import org.apache.kafka.clients.producer.Producer; +import org.apache.kafka.clients.producer.ProducerRecord; + +/** + * @author kuangcp + * + * Date: 2019-05-20 00:04 + */ +@Slf4j +public class ProducerDemo { + + private static Properties properties = getConf(); + private static ObjectMapper mapper = new ObjectMapper(); + + static void sendHi() { + Producer producer = null; + try { + producer = new KafkaProducer<>(properties); + for (int i = 0; i < 100; i++) { + String msg = "Message " + i; + producer.send(new ProducerRecord<>(HI_TOPIC, msg)); + log.info("Sent: {}", msg); + } + } catch (Exception e) { + log.error(e.getMessage(), e); + } finally { + try { + ResourceTool.close(producer); + } catch (IOException e) { + log.error(e.getMessage(), e); + } + } + } + + static void sendStart() { + Producer producer = null; + try { + producer = new KafkaProducer<>(properties); + for (int i = 0; i < 20; i++) { + StartCommand msg = StartCommand.builder().place("There").scale(i).startTime(new Date()) + .build(); + producer.send(new ProducerRecord<>(START_TOPIC, mapper.writeValueAsString(msg))); + log.info("Sent: {}", msg); + } + } catch (Exception e) { + log.error(e.getMessage(), e); + } finally { + try { + ResourceTool.close(producer); + } catch (IOException e) { + log.error(e.getMessage(), e); + } + } + } + + static void sendCommand() { + Producer producer = null; + HashSet spans = new HashSet<>(); + spans.add(ProductStatisticSpan.MONTH); + + try { + producer = new KafkaProducer<>(properties); + for (int i = 0; i < 2; i++) { + ProductStatisticJobCommand msg = ProductStatisticJobCommand.builder() + .id(UUID.randomUUID().toString() + "_test_" + i) + .startTime(toDate(LocalDateTime.now().withMonth(1))) + .endTime(toDate(LocalDateTime.now().withMonth(3))) + .productStatisticSpan(spans) + .build(); + ProducerRecord record = new ProducerRecord<>(Constants.COMMAND_TOPIC, + mapper.writeValueAsString(msg)); + + long time = System.currentTimeMillis(); + producer.send(record, (metadata, e) -> { + long elapsedTime = System.currentTimeMillis() - time; + if (metadata != null) { + log.info("sent record(key={} value={}) meta(partition={}, offset={}) time={}", + record.key(), record.value(), metadata.partition(), + metadata.offset(), elapsedTime); + } else { + log.error(e.getMessage(), e); + } + }); + } + } catch (Exception e) { + log.error(e.getMessage(), e); + } finally { + try { + ResourceTool.close(producer); + } catch (IOException e) { + log.error(e.getMessage(), e); + } + } + } + + public static Date toDate(LocalDateTime dateTime) { + return Date.from(dateTime.atZone(ZoneId.systemDefault()).toInstant()); + } + + private static Properties getConf() { + Properties properties = new Properties(); + properties.put("bootstrap.servers", KAFKA_SERVER); + properties.put("acks", "all"); + properties.put("retries", 0); + properties.put("batch.size", 16384); + properties.put("linger.ms", 1); + properties.put("buffer.memory", 33554432); + + properties.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer"); + properties.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer"); + return properties; + } +} diff --git a/kafka/src/main/java/com/github/kuangcp/hi/domain/ProductStatisticJobCommand.java b/kafka/src/main/java/com/github/kuangcp/hi/domain/ProductStatisticJobCommand.java new file mode 100644 index 00000000..7024b1f9 --- /dev/null +++ b/kafka/src/main/java/com/github/kuangcp/hi/domain/ProductStatisticJobCommand.java @@ -0,0 +1,41 @@ +package com.github.kuangcp.hi.domain; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.Date; +import java.util.Set; + +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class ProductStatisticJobCommand implements Serializable { + + /** + * 任务ID + */ + private String id; + + + /** + * 统计维度 + */ + private Set productStatisticSpan; + + + /** + * 统计起始时间 + */ + private Date startTime; + + + /** + * 统计结束时间 + */ + private Date endTime; + +} diff --git a/kafka/src/main/java/com/github/kuangcp/hi/domain/ProductStatisticSpan.java b/kafka/src/main/java/com/github/kuangcp/hi/domain/ProductStatisticSpan.java new file mode 100644 index 00000000..7d092e87 --- /dev/null +++ b/kafka/src/main/java/com/github/kuangcp/hi/domain/ProductStatisticSpan.java @@ -0,0 +1,62 @@ +package com.github.kuangcp.hi.domain; + +import lombok.Getter; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +@Getter +public enum ProductStatisticSpan { + + /** + * 按日统计 + */ + DAY("DAY00_", StatisticSpan.SPAN_DAY), + + /** + * 按月统计 + */ + MONTH("MONTH_", StatisticSpan.SPAN_MONTH), + + /** + * 按季度统计 + */ + SEASON("SEASON", StatisticSpan.SPAN_SEASON), + + /** + * 按年统计 + */ + YEAR("YEAR0_", StatisticSpan.SPAN_YEAR); + + private String prefix; + + private Integer span; + + private static final List ALL = Arrays.asList(values()); + + private static final Map MAP = ALL.stream() + .collect(Collectors.toMap(ProductStatisticSpan::getSpan, Function.identity())); + + ProductStatisticSpan(String prefix, Integer span) { + this.prefix = prefix; + this.span = span; + } + + + public static List getAll() { + return ALL; + } + + public static Map getMap() { + return MAP; + } + + public static ProductStatisticSpan getBySpan(Integer span) { + return MAP.getOrDefault(span, ProductStatisticSpan.SEASON); + } + +} + diff --git a/kafka/src/main/java/com/github/kuangcp/hi/domain/StartCommand.java b/kafka/src/main/java/com/github/kuangcp/hi/domain/StartCommand.java new file mode 100644 index 00000000..588b191e --- /dev/null +++ b/kafka/src/main/java/com/github/kuangcp/hi/domain/StartCommand.java @@ -0,0 +1,20 @@ +package com.github.kuangcp.hi.domain; + +import java.util.Date; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class StartCommand { + + private Date startTime; + + private String place; + + private int scale; +} diff --git a/kafka/src/main/java/com/github/kuangcp/hi/domain/StatisticSpan.java b/kafka/src/main/java/com/github/kuangcp/hi/domain/StatisticSpan.java new file mode 100644 index 00000000..96f409da --- /dev/null +++ b/kafka/src/main/java/com/github/kuangcp/hi/domain/StatisticSpan.java @@ -0,0 +1,25 @@ +package com.github.kuangcp.hi.domain; + +public interface StatisticSpan { + + /** + * 天 + */ + Integer SPAN_DAY = 2; + + /** + * 月 + */ + Integer SPAN_MONTH = 3; + + /** + * 季度 + */ + Integer SPAN_SEASON = 5; + + /** + * 年 + */ + Integer SPAN_YEAR = 4; + +} diff --git a/kafka/src/main/resources/kafka.properties b/kafka/src/main/resources/kafka.properties new file mode 100644 index 00000000..3c0a310c --- /dev/null +++ b/kafka/src/main/resources/kafka.properties @@ -0,0 +1,2 @@ +bootstrap.servers=127.0.0.1:9092 +group.id=test-demo \ No newline at end of file diff --git a/kafka/src/test/java/com/github/kuangcp/hi/ConsumerDemoTest.java b/kafka/src/test/java/com/github/kuangcp/hi/ConsumerDemoTest.java new file mode 100644 index 00000000..a86b1f4d --- /dev/null +++ b/kafka/src/test/java/com/github/kuangcp/hi/ConsumerDemoTest.java @@ -0,0 +1,22 @@ +package com.github.kuangcp.hi; + +import java.io.IOException; +import org.junit.Test; + +/** + * @author https://github.com/kuangcp + * @date 2019-05-21 16:20 + */ +public class ConsumerDemoTest { + + @Test + public void testReceiveHi() { + ConsumerDemo.receiveHi(); + } + + + @Test + public void testReceiveCommand() throws IOException { + ConsumerDemo.receiveCommand(); + } +} diff --git a/kafka/src/test/java/com/github/kuangcp/hi/ProducerDemoTest.java b/kafka/src/test/java/com/github/kuangcp/hi/ProducerDemoTest.java new file mode 100644 index 00000000..e2fbf236 --- /dev/null +++ b/kafka/src/test/java/com/github/kuangcp/hi/ProducerDemoTest.java @@ -0,0 +1,25 @@ +package com.github.kuangcp.hi; + +import org.junit.Test; + +/** + * @author https://github.com/kuangcp + * @date 2019-05-21 16:19 + */ +public class ProducerDemoTest { + + @Test + public void testSendCommand() { + ProducerDemo.sendCommand(); + } + + @Test + public void testSendStart() { + ProducerDemo.sendStart(); + } + + @Test + public void testSendHi() { + ProducerDemo.sendHi(); + } +} \ No newline at end of file diff --git a/kafka/src/test/resources/logback-test.xml b/kafka/src/test/resources/logback-test.xml new file mode 100644 index 00000000..bf2a1d8b --- /dev/null +++ b/kafka/src/test/resources/logback-test.xml @@ -0,0 +1,21 @@ + + + + + + + + + %d{HH:mm:ss.SSS} %highlight(%-5level) [%thread] %logger{30} %highlight(%M):%yellow(%-3L) %msg%n + + + DEBUG + + + + + + + + + \ No newline at end of file diff --git a/mybatis/build.gradle b/mybatis/build.gradle new file mode 100644 index 00000000..83e23256 --- /dev/null +++ b/mybatis/build.gradle @@ -0,0 +1,21 @@ +plugins { + id 'org.springframework.boot' version '2.2.5.RELEASE' +} + +apply plugin: 'java' +apply plugin: 'io.spring.dependency-management' + +repositories { + mavenCentral() +} + +dependencies { + implementation project(":common-config") + implementation("org.springframework.boot:spring-boot-starter-web") + testImplementation("org.springframework.boot:spring-boot-starter-test") + implementation("com.baomidou:mybatis-plus-boot-starter:3.1.2") + implementation("mysql:mysql-connector-java:8.0.17") + + implementation libs["mybatis"] + implementation libs["mybatis-plus"] +} diff --git a/mybatis/create.sql b/mybatis/create.sql new file mode 100644 index 00000000..e7008446 --- /dev/null +++ b/mybatis/create.sql @@ -0,0 +1,16 @@ +CREATE TABLE customer ( + id bigint(20) auto_increment primary key , + name varchar(20) DEFAULT NULL, + nation int(11) DEFAULT NULL +); +create table normal_order ( + id bigint(20) auto_increment primary key , + user_id bigint(20), + create_time datetime default CURRENT_TIMESTAMP, + update_time datetime default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP, + detail varchar(30), + price decimal(6,2), + discount decimal(6,2), + num int, + pay_time datetime +) \ No newline at end of file diff --git a/mybatis/src/main/java/com/github/kuangcp/Application.java b/mybatis/src/main/java/com/github/kuangcp/Application.java new file mode 100644 index 00000000..1c97c1d3 --- /dev/null +++ b/mybatis/src/main/java/com/github/kuangcp/Application.java @@ -0,0 +1,14 @@ +package com.github.kuangcp; + +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@MapperScan("com.github.kuangcp.**.dao") +@SpringBootApplication() +public class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } +} diff --git a/mybatis/src/main/java/com/github/kuangcp/sharding/manual/AuthUtil.java b/mybatis/src/main/java/com/github/kuangcp/sharding/manual/AuthUtil.java new file mode 100644 index 00000000..9cfa1288 --- /dev/null +++ b/mybatis/src/main/java/com/github/kuangcp/sharding/manual/AuthUtil.java @@ -0,0 +1,24 @@ +package com.github.kuangcp.sharding.manual; + +import org.springframework.stereotype.Component; + +/** + * @author https://github.com/kuangcp on 2021-07-11 18:53 + */ +@Component +public class AuthUtil { + + private final ThreadLocal orgIdLocal = new ThreadLocal<>(); + + public void completeAuth(Long orgId) { + orgIdLocal.set(orgId); + } + + public Long getAuthedOrgId() { + return orgIdLocal.get(); + } + + public void clearAuth() { + orgIdLocal.remove(); + } +} diff --git a/mybatis/src/main/java/com/github/kuangcp/sharding/manual/ConsistentHashingAlgorithm.java b/mybatis/src/main/java/com/github/kuangcp/sharding/manual/ConsistentHashingAlgorithm.java new file mode 100644 index 00000000..2d75d946 --- /dev/null +++ b/mybatis/src/main/java/com/github/kuangcp/sharding/manual/ConsistentHashingAlgorithm.java @@ -0,0 +1,100 @@ +package com.github.kuangcp.sharding.manual; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.*; + +/** + * @author https://github.com/kuangcp on 2021-07-11 19:13 + */ +public class ConsistentHashingAlgorithm { + private MessageDigest md5; + private final int numberOfReplicas = 300; + + private final SortedMap circle = new TreeMap<>(); + private static final Map cache = new HashMap<>(); + + public ConsistentHashingAlgorithm(int totalNode) { + for (int i = 1; i <= totalNode; i++) { + add("_" + i); + } + } + + /** + * TODO 线程不安全,以及环的多节点维护问题 + */ + public static ConsistentHashingAlgorithm useWithCache(int totalNode) { + ConsistentHashingAlgorithm result = cache.get(totalNode); + if (Objects.nonNull(result)) { + return result; + } + ConsistentHashingAlgorithm newOne = new ConsistentHashingAlgorithm(totalNode); + cache.put(totalNode, newOne); + return newOne; + } + + /** + * 添加虚拟节点 + * numberOfReplicas为虚拟节点的数量 + * 例如初始化hash环的时候传入,我们使用300个虚拟节点 + */ + public void add(String node) { + for (int i = 0; i < numberOfReplicas; i++) { + circle.put(this.hash(node.toString() + "_" + i), node); + } + } + + /** + * 移除节点 + */ + public void remove(String node) { + for (int i = 0; i < numberOfReplicas; i++) { + circle.remove(this.hash(node.toString() + i)); + } + } + + public String get(Long key) { + return get(key + ""); + } + + /** + * 获得一个最近的顺时针节点 + * + * @param key 为给定键取Hash,取得顺时针方向上最近的一个虚拟节点对应的实际节点 + */ + public String get(String key) { + if (circle.isEmpty()) { + return null; + } + long hash = this.hash(key); + if (!circle.containsKey(hash)) { + //返回此映射的部分视图,其键大于等于 hash + SortedMap tailMap = circle.tailMap(hash); + hash = tailMap.isEmpty() ? circle.firstKey() : tailMap.firstKey(); + } + return circle.get(hash); + } + + /** + * TODO 线程不安全 + */ + public long hash(String key) { + if (md5 == null) { + try { + md5 = MessageDigest.getInstance("MD5"); + } catch (NoSuchAlgorithmException e) { + throw new IllegalStateException("no md5 algorythm found"); + } + } + + md5.reset(); + md5.update(key.getBytes()); + byte[] bKey = md5.digest(); + + long res = ((long) (bKey[3] & 0xFF) << 24) | + ((long) (bKey[2] & 0xFF) << 16) | + ((long) (bKey[1] & 0xFF) << 8) | + (long) (bKey[0] & 0xFF); + return res & 0xffffffffL; + } +} diff --git a/mybatis/src/main/java/com/github/kuangcp/sharding/manual/ShardingAlgorithmEnum.java b/mybatis/src/main/java/com/github/kuangcp/sharding/manual/ShardingAlgorithmEnum.java new file mode 100644 index 00000000..70aa3726 --- /dev/null +++ b/mybatis/src/main/java/com/github/kuangcp/sharding/manual/ShardingAlgorithmEnum.java @@ -0,0 +1,46 @@ +package com.github.kuangcp.sharding.manual; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Objects; +import java.util.Optional; +import java.util.function.BiFunction; + +/** + * @author https://github.com/kuangcp on 2021-07-11 18:41 + */ +@Getter +@AllArgsConstructor +public enum ShardingAlgorithmEnum { + MOD(ShardingAlgorithmType.MOD, (shardingVal, totalSlice) -> { + long index = shardingVal % totalSlice; + return "_" + index; + }), + + CONSISTENT_HASHING(ShardingAlgorithmType.CONSISTENT_HASHING, + (shardingVal, totalCount) -> ConsistentHashingAlgorithm.useWithCache(totalCount).get(shardingVal)), + ; + + private final int type; + /** + * shardingVal 用于分片的值 + * totalSlice 总分片数 + * suffix 子表后缀 + */ + private final BiFunction func; + + + public static Optional of(Integer type) { + if (Objects.isNull(type)) { + return Optional.empty(); + } + for (ShardingAlgorithmEnum value : values()) { + if (Objects.equals(value.type, type)) { + return Optional.of(value); + } + } + return Optional.empty(); + } + +} diff --git a/mybatis/src/main/java/com/github/kuangcp/sharding/manual/ShardingAlgorithmType.java b/mybatis/src/main/java/com/github/kuangcp/sharding/manual/ShardingAlgorithmType.java new file mode 100644 index 00000000..d90db68f --- /dev/null +++ b/mybatis/src/main/java/com/github/kuangcp/sharding/manual/ShardingAlgorithmType.java @@ -0,0 +1,9 @@ +package com.github.kuangcp.sharding.manual; + +/** + * @author https://github.com/kuangcp on 2021-07-11 18:59 + */ +public interface ShardingAlgorithmType { + int MOD = 1; + int CONSISTENT_HASHING = 2; +} diff --git a/mybatis/src/main/java/com/github/kuangcp/sharding/manual/ShardingTable.java b/mybatis/src/main/java/com/github/kuangcp/sharding/manual/ShardingTable.java new file mode 100644 index 00000000..eb71605c --- /dev/null +++ b/mybatis/src/main/java/com/github/kuangcp/sharding/manual/ShardingTable.java @@ -0,0 +1,16 @@ +package com.github.kuangcp.sharding.manual; + +import java.lang.annotation.*; + +/** + * @author https://github.com/kuangcp on 2021-07-11 18:19 + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface ShardingTable { + + int algorithm(); + + int tableCount(); +} diff --git a/mybatis/src/main/java/com/github/kuangcp/sharding/manual/ShardingTableInterceptor.java b/mybatis/src/main/java/com/github/kuangcp/sharding/manual/ShardingTableInterceptor.java new file mode 100644 index 00000000..9a4f3296 --- /dev/null +++ b/mybatis/src/main/java/com/github/kuangcp/sharding/manual/ShardingTableInterceptor.java @@ -0,0 +1,137 @@ +package com.github.kuangcp.sharding.manual; + +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.extern.slf4j.Slf4j; +import org.apache.ibatis.executor.statement.RoutingStatementHandler; +import org.apache.ibatis.executor.statement.StatementHandler; +import org.apache.ibatis.mapping.BoundSql; +import org.apache.ibatis.mapping.MappedStatement; +import org.apache.ibatis.plugin.*; +import org.apache.ibatis.reflection.MetaObject; +import org.apache.ibatis.reflection.SystemMetaObject; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.sql.Connection; +import java.util.Properties; + +/** + * @author https://github.com/kuangcp on 2021-07-11 18:19 + */ +@Slf4j +@Component +@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", + args = {Connection.class, Integer.class})}) +public class ShardingTableInterceptor implements Interceptor { + + @Autowired + private AuthUtil authUtil; + + @Override + public Object intercept(Invocation invocation) throws Throwable { + MetaObject metaObject; + MappedStatement mappedStatement; + Object target = invocation.getTarget(); + + if (!(target instanceof RoutingStatementHandler)) { + return invocation.proceed(); + } + RoutingStatementHandler routingStatementHandler = (RoutingStatementHandler) target; + metaObject = SystemMetaObject.forObject(routingStatementHandler); + StatementHandler statementHandler = (StatementHandler) metaObject.getValue("delegate"); + metaObject = SystemMetaObject.forObject(statementHandler); + + mappedStatement = (MappedStatement) metaObject.getValue("mappedStatement"); + BoundSql boundSql = statementHandler.getBoundSql(); + //获取对应的Mapper类 + Class mapperClass = Class.forName(mappedStatement.getId().substring(0, mappedStatement.getId().lastIndexOf("."))); + //获取对应EO + Class eoClass = getEoClass(mapperClass); + if (eoClass.isAnnotationPresent(ShardingTable.class) && eoClass.isAnnotationPresent(TableName.class)) { + String logicTable = eoClass.getAnnotation(TableName.class).value(); + ShardingTable rdsSharding = eoClass.getAnnotation(ShardingTable.class); + int algorithm = rdsSharding.algorithm(); + int tableTotal = rdsSharding.tableCount(); + + Long orgId = authUtil.getAuthedOrgId(); + + // 统一使用 组织id 分表或者不分表 + String subTableName = ShardingAlgorithmEnum.of(algorithm) + .map(ShardingAlgorithmEnum::getFunc) + .map(v -> v.apply(orgId, tableTotal)) + .map(v -> logicTable + v) + .orElse(logicTable); + + if (StringUtils.isEmpty(subTableName)) { + log.error("Unable to obtain subTableName , exec canceled. caseby: {} splitKey's value is null", logicTable); + } else { + String sql = boundSql.getSql(); + //将表名替换为子表名 + sql = sql.replaceAll(logicTable, subTableName); + + metaObject = SystemMetaObject.forObject(boundSql); + metaObject.setValue("sql", sql); + } + } + + return invocation.proceed(); + } + + + @Override + public Object plugin(Object target) { + // TODO Auto-generated method stub + if (target instanceof StatementHandler) { + return Plugin.wrap(target, this); + } else { + return target; + } + } + + @Override + public void setProperties(Properties properties) { + + } + + /** + * 获取Eo class + * + * @param eoMapper + * @return + */ + private Class getEoClass(Class eoMapper) { + Class entityClass = getGenericClass(eoMapper); + if (entityClass != null) { + String eoName = entityClass.getPackage().getName() + "." + StringUtils.delete(entityClass.getSimpleName(), "Eo") + "ExtEo"; + + try { + Class extClass = Class.forName(eoName); + entityClass = extClass; + } catch (ClassNotFoundException exception) { + } + } + + return entityClass; + } + + /** + * 获取接口的泛型类型,如果不存在则返回null + * + * @param clazz + * @return + */ + private Class getGenericClass(Class clazz) { + Type t = clazz.getGenericSuperclass(); + if (t == null) { + t = clazz.getGenericInterfaces()[0]; + } + if (t instanceof ParameterizedType) { + Type[] p = ((ParameterizedType) t).getActualTypeArguments(); + return ((Class) p[0]); + } + return null; + } +} diff --git a/mybatis/src/main/java/com/github/kuangcp/simple/customer/dao/CustomerDao.java b/mybatis/src/main/java/com/github/kuangcp/simple/customer/dao/CustomerDao.java new file mode 100644 index 00000000..c1bc6513 --- /dev/null +++ b/mybatis/src/main/java/com/github/kuangcp/simple/customer/dao/CustomerDao.java @@ -0,0 +1,25 @@ +package com.github.kuangcp.simple.customer.dao; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.github.kuangcp.simple.customer.domain.Customer; +import org.apache.ibatis.annotations.Options; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.ResultType; +import org.apache.ibatis.annotations.Select; +import org.apache.ibatis.mapping.ResultSetType; +import org.apache.ibatis.session.ResultHandler; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public interface CustomerDao extends BaseMapper { + + @Select({"select * from customer where name like #{name} "}) + List queryByName(@Param("name") String name); + + @Select("SELECT * from customer") + @Options(resultSetType = ResultSetType.FORWARD_ONLY, fetchSize = Integer.MIN_VALUE) + @ResultType(Customer.class) + void streamQueryAll(ResultHandler handler); +} diff --git a/mybatis/src/main/java/com/github/kuangcp/simple/customer/domain/Customer.java b/mybatis/src/main/java/com/github/kuangcp/simple/customer/domain/Customer.java new file mode 100644 index 00000000..0affe32e --- /dev/null +++ b/mybatis/src/main/java/com/github/kuangcp/simple/customer/domain/Customer.java @@ -0,0 +1,22 @@ +package com.github.kuangcp.simple.customer.domain; + +import com.baomidou.mybatisplus.annotation.TableName; +import com.github.kuangcp.sharding.manual.ShardingAlgorithmType; +import com.github.kuangcp.sharding.manual.ShardingTable; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@TableName("customer") +@ShardingTable(algorithm = ShardingAlgorithmType.MOD, tableCount = 6) +public class Customer { + + private Long id; + private String name; + private Integer nation; +} diff --git a/mybatis/src/main/java/com/github/kuangcp/simple/order/dao/OrderDao.java b/mybatis/src/main/java/com/github/kuangcp/simple/order/dao/OrderDao.java new file mode 100644 index 00000000..c5024ff8 --- /dev/null +++ b/mybatis/src/main/java/com/github/kuangcp/simple/order/dao/OrderDao.java @@ -0,0 +1,10 @@ +package com.github.kuangcp.simple.order.dao; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.github.kuangcp.simple.order.domain.Order; +import org.springframework.stereotype.Repository; + +@Repository +public interface OrderDao extends BaseMapper { + +} diff --git a/mybatis/src/main/java/com/github/kuangcp/simple/order/domain/Order.java b/mybatis/src/main/java/com/github/kuangcp/simple/order/domain/Order.java new file mode 100644 index 00000000..f0c49853 --- /dev/null +++ b/mybatis/src/main/java/com/github/kuangcp/simple/order/domain/Order.java @@ -0,0 +1,28 @@ +package com.github.kuangcp.simple.order.domain; + +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@TableName("normal_order") +public class Order { + + private Long id; + private Long userId; + private LocalDateTime createTime; + private LocalDateTime updateTime; + private String detail; + private BigDecimal price; + private BigDecimal discount; + private Integer num; + private LocalDateTime payTime; +} diff --git a/mybatis/src/main/java/com/github/kuangcp/simple/order/dto/OrderDTO.java b/mybatis/src/main/java/com/github/kuangcp/simple/order/dto/OrderDTO.java new file mode 100644 index 00000000..652ba8a6 --- /dev/null +++ b/mybatis/src/main/java/com/github/kuangcp/simple/order/dto/OrderDTO.java @@ -0,0 +1,24 @@ +package com.github.kuangcp.simple.order.dto; +import java.math.BigDecimal; +import java.time.LocalDateTime; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class OrderDTO { + private Long id; + private Long userId; + private String username; + private LocalDateTime createTime; + private LocalDateTime updateTime; + private String detail; + private BigDecimal price; + private BigDecimal discount; + private Integer num; + private LocalDateTime payTime; +} diff --git a/mybatis/src/main/java/com/github/kuangcp/simple/order/service/OrderService.java b/mybatis/src/main/java/com/github/kuangcp/simple/order/service/OrderService.java new file mode 100644 index 00000000..7991fb58 --- /dev/null +++ b/mybatis/src/main/java/com/github/kuangcp/simple/order/service/OrderService.java @@ -0,0 +1,11 @@ +package com.github.kuangcp.simple.order.service; + +import com.github.kuangcp.simple.order.dto.OrderDTO; +import java.util.List; + +public interface OrderService { + + List queryByUserIdWithLoop(Long userId); + + List queryByUserId(Long userId); +} diff --git a/mybatis/src/main/java/com/github/kuangcp/simple/order/service/impl/OrderServiceImpl.java b/mybatis/src/main/java/com/github/kuangcp/simple/order/service/impl/OrderServiceImpl.java new file mode 100644 index 00000000..a9ee630a --- /dev/null +++ b/mybatis/src/main/java/com/github/kuangcp/simple/order/service/impl/OrderServiceImpl.java @@ -0,0 +1,86 @@ +package com.github.kuangcp.simple.order.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.github.kuangcp.simple.customer.dao.CustomerDao; +import com.github.kuangcp.simple.customer.domain.Customer; +import com.github.kuangcp.simple.order.dao.OrderDao; +import com.github.kuangcp.simple.order.domain.Order; +import com.github.kuangcp.simple.order.dto.OrderDTO; +import com.github.kuangcp.simple.order.service.OrderService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; + +@Service +public class OrderServiceImpl implements OrderService { + + @Autowired + private OrderDao orderDao; + + @Autowired + private CustomerDao customerDao; + + @Override + public List queryByUserIdWithLoop(Long userId) { + List orders = orderDao.selectList(new QueryWrapper().eq("user_id", userId)); + + return orders.stream().map(this::convert).collect(Collectors.toList()); + } + + @Override + public List queryByUserId(Long userId) { + List orders = orderDao.selectList(new QueryWrapper().eq("user_id", userId)); + + Set idSet = orders.stream().map(Order::getUserId).collect(Collectors.toSet()); + Map userMap = customerDao.selectBatchIds(idSet).stream() + .collect(Collectors.toMap(Customer::getId, Function.identity(), (front, next) -> next)); + + return orders.stream().map(v -> convert(v, userMap)).collect(Collectors.toList()); + } + + private OrderDTO convert(Order order, Map userMap) { + Customer customer = userMap.get(order.getUserId()); + String username = getName(customer); + return OrderDTO.builder() + .num(order.getNum()) + .userId(order.getUserId()) + .username(username) + .createTime(order.getCreateTime()) + .updateTime(order.getUpdateTime()) + .detail(order.getDetail()) + .price(order.getPrice()) + .discount(order.getDiscount()) + .payTime(order.getPayTime()) + .id(order.getId()) + .build(); + + } + + private OrderDTO convert(Order order) { + Customer customer = customerDao.selectById(order.getUserId()); + + String username = getName(customer); + return OrderDTO.builder() + .num(order.getNum()) + .userId(order.getUserId()) + .username(username) + .createTime(order.getCreateTime()) + .updateTime(order.getUpdateTime()) + .detail(order.getDetail()) + .price(order.getPrice()) + .discount(order.getDiscount()) + .payTime(order.getPayTime()) + .id(order.getId()) + .build(); + } + + private String getName(Customer customer) { + return Optional.ofNullable(customer).map(Customer::getName).orElse(""); + } +} diff --git a/mybatis/src/main/java/com/github/kuangcp/stream/Report.java b/mybatis/src/main/java/com/github/kuangcp/stream/Report.java new file mode 100644 index 00000000..38e33f83 --- /dev/null +++ b/mybatis/src/main/java/com/github/kuangcp/stream/Report.java @@ -0,0 +1,36 @@ +package com.github.kuangcp.stream; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +import java.util.Date; + +/** + * @author https://github.com/kuangcp on 2021-07-12 23:50 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = false) +@TableName("report") +public class Report { + private static final long serialVersionUID = 1L; + + /** + * id + */ + @TableId + private Long id; + + /** + * 用户id + */ + private Long userId; + private String b; + private Integer c; + private Integer d; + private Integer e; + private Date statisticsTime; +} diff --git a/mybatis/src/main/java/com/github/kuangcp/stream/dao/ReportDao.java b/mybatis/src/main/java/com/github/kuangcp/stream/dao/ReportDao.java new file mode 100644 index 00000000..ffad2a2c --- /dev/null +++ b/mybatis/src/main/java/com/github/kuangcp/stream/dao/ReportDao.java @@ -0,0 +1,42 @@ +package com.github.kuangcp.stream.dao; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.github.kuangcp.stream.Report; +import org.apache.ibatis.annotations.Options; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.ResultType; +import org.apache.ibatis.annotations.Select; +import org.apache.ibatis.mapping.ResultSetType; +import org.apache.ibatis.session.ResultHandler; +import org.springframework.stereotype.Repository; + +import java.util.Date; +import java.util.List; + +/** + * @author https://github.com/kuangcp on 2021-07-12 23:52 + */ +@Repository +public interface ReportDao extends BaseMapper { + + @Select("SELECT user_id, b, SUM(c) AS c" + + ", SUM(d) AS d, SUM(e) AS e" + + " FROM report WHERE statistics_time BETWEEN #{start} AND #{end}" + + " AND user_id IN (1,2,3,4,5,6)" + + " GROUP BY b") + @Options(resultSetType = ResultSetType.FORWARD_ONLY, fetchSize = Integer.MIN_VALUE) + @ResultType(Report.class) + void selectAutoList(@Param("start") Date start, + @Param("end") Date end, + ResultHandler handler); + + @Select("SELECT user_id, b, SUM(c) AS c" + + ", SUM(d) AS d, SUM(e) AS e" + + " FROM report WHERE statistics_time BETWEEN #{start} AND #{end}" + + " AND user_id IN (1,2,3,4,5,6)" + + " GROUP BY b LIMIT #{startIdx},#{size}") + List queryByPage(@Param("start") Date start, + @Param("end") Date end, + @Param("startIdx") long startIdx, + @Param("size") long size); +} diff --git a/mybatis/src/main/resources/application.yml b/mybatis/src/main/resources/application.yml new file mode 100644 index 00000000..8bf8a693 --- /dev/null +++ b/mybatis/src/main/resources/application.yml @@ -0,0 +1,6 @@ +spring: + datasource: + url: jdbc:mysql://127.0.0.1:3360/mybatis?autoReconnect=true&useUnicode=true&characterEncoding=utf8&useSSL=false + username: root + password: jiushi + maximum-pool-size: 30 diff --git a/mybatis/src/main/resources/logback.xml b/mybatis/src/main/resources/logback.xml new file mode 100644 index 00000000..cb8ba4cc --- /dev/null +++ b/mybatis/src/main/resources/logback.xml @@ -0,0 +1,79 @@ + + + + + + + + + %d{HH:mm:ss.SSS} %highlight(%-5level) [%thread] %logger{30} %highlight(%M):%yellow(%-3L) %msg%n + + + INFO + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/mybatis/src/test/java/com/github/kuangcp/base/SpringBootTestStarter.java b/mybatis/src/test/java/com/github/kuangcp/base/SpringBootTestStarter.java new file mode 100644 index 00000000..3235e510 --- /dev/null +++ b/mybatis/src/test/java/com/github/kuangcp/base/SpringBootTestStarter.java @@ -0,0 +1,11 @@ +package com.github.kuangcp.base; + +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@SpringBootTest +@RunWith(SpringRunner.class) +public abstract class SpringBootTestStarter { + +} diff --git a/mybatis/src/test/java/com/github/kuangcp/sharding/manual/ConsistentHashingAlgorithmTest.java b/mybatis/src/test/java/com/github/kuangcp/sharding/manual/ConsistentHashingAlgorithmTest.java new file mode 100644 index 00000000..8b474b9b --- /dev/null +++ b/mybatis/src/test/java/com/github/kuangcp/sharding/manual/ConsistentHashingAlgorithmTest.java @@ -0,0 +1,57 @@ +package com.github.kuangcp.sharding.manual; + +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; + +import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; + +/** + * @author https://github.com/kuangcp on 2021-07-11 19:32 + */ +@Slf4j +public class ConsistentHashingAlgorithmTest { + + private Map userMap = new HashMap<>(); + + /** + * 一致性hash算法优势在于 新增删除节点,原始数据迁移较少 + */ + @Test + public void testCompareMoveData() { + for (ShardingAlgorithmEnum func : ShardingAlgorithmEnum.values()) { + log.info("start: func={}", func); + this.userMap = new HashMap<>(); + for (int i = 2; i < 30; i += 3) { + calculatePerIndex(func, i, (int) Math.pow(2, i) * 20); +// calculatePerIndex(func, i, i * 3); + } + } + } + + private void calculatePerIndex(ShardingAlgorithmEnum func, int totalSlice, int totalData) { + Map newUserMap = new HashMap<>(); + for (int i = 0; i < totalData; i++) { + String finalTable = func.getFunc().apply((long) i, totalSlice); +// System.out.println(i + " " + finalTable); + newUserMap.put(i, finalTable); + } + long changCount = userMap.entrySet().stream().filter(v -> { + String newIndex = newUserMap.get(v.getKey()); + return !Objects.equals(newIndex, v.getValue()); + }).count(); + + this.userMap = newUserMap; + Map>> tableMap = userMap.entrySet() + .stream().collect(Collectors.groupingBy(Map.Entry::getValue)); + + AtomicInteger counter = new AtomicInteger(); + tableMap.entrySet().stream() + .sorted(Map.Entry.comparingByKey()).forEach(e -> { + counter.addAndGet(e.getValue().size()); + System.out.println(e.getKey() + ":" + e.getValue().size()); + }); + log.info("{}: count={} changeCount={}", func.name(), counter.get(), changCount); + } +} \ No newline at end of file diff --git a/mybatis/src/test/java/com/github/kuangcp/simple/customer/dao/CustomerDaoSpringBootTest.java b/mybatis/src/test/java/com/github/kuangcp/simple/customer/dao/CustomerDaoSpringBootTest.java new file mode 100644 index 00000000..69c1a02a --- /dev/null +++ b/mybatis/src/test/java/com/github/kuangcp/simple/customer/dao/CustomerDaoSpringBootTest.java @@ -0,0 +1,45 @@ +package com.github.kuangcp.simple.customer.dao; + +import com.github.kuangcp.base.SpringBootTestStarter; +import com.github.kuangcp.sharding.manual.AuthUtil; +import com.github.kuangcp.simple.customer.domain.Customer; +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.List; +import java.util.stream.LongStream; + +@Slf4j +public class CustomerDaoSpringBootTest extends SpringBootTestStarter { + + @Autowired + private CustomerDao customerDao; + @Autowired + private AuthUtil authUtil; + + private void login(Long orgId) { + authUtil.clearAuth(); + authUtil.completeAuth(orgId); + } + + @Test + public void testQuery() { + login(2L); + customerDao.insert(Customer.builder().id(1L).name("myth").build()); + List customers = customerDao.queryByName("myth"); + + customers.forEach(item -> log.info("item={}", item)); + } + + @Test + public void testBulkInsert() { + + LongStream.rangeClosed(1, 1000) + .mapToObj(i -> Customer.builder().id(i) + .id(i) + .nation(3) + .build()) + .forEach(customerDao::insert); + } +} \ No newline at end of file diff --git a/mybatis/src/test/java/com/github/kuangcp/simple/order/dao/OrderDaoSpringBootTest.java b/mybatis/src/test/java/com/github/kuangcp/simple/order/dao/OrderDaoSpringBootTest.java new file mode 100644 index 00000000..faf23b8d --- /dev/null +++ b/mybatis/src/test/java/com/github/kuangcp/simple/order/dao/OrderDaoSpringBootTest.java @@ -0,0 +1,65 @@ +package com.github.kuangcp.simple.order.dao; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.github.kuangcp.base.SpringBootTestStarter; +import com.github.kuangcp.simple.order.domain.Order; +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; + +@Slf4j +public class OrderDaoSpringBootTest extends SpringBootTestStarter { + + @Autowired + private OrderDao orderDao; + + @Test + public void testQuery() { + Order origin = Order.builder() + .num(1) + .detail("detail") + .createTime(LocalDateTime.now()) + .payTime(LocalDateTime.now()) + .build(); + orderDao.insert(origin); + List orders = orderDao.selectList(new QueryWrapper<>()); + orders.forEach(item -> log.info("{}", item)); + } + + @Test + public void testBulkInsert() throws InterruptedException { + + Consumer consumer = start -> { + Order temp = Order.builder().num(44) + .createTime(LocalDateTime.now()).build(); + for (int i = 0; i < 10000; i++) { + long id = (start + i); + temp.setId(id); + temp.setUserId(id % 400); + orderDao.insert(temp); + } + }; + + List threads = new ArrayList<>(); + for (int i = 0; i < 8; i++) { + int finalI = i; + threads.add(new Thread(() -> consumer.accept(500200 + finalI * 20000))); + } + + threads.forEach(v -> { + try { + v.start(); + v.join(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + }); + + Thread.yield(); + } +} \ No newline at end of file diff --git a/mybatis/src/test/java/com/github/kuangcp/simple/order/service/OrderServiceSpringBootTest.java b/mybatis/src/test/java/com/github/kuangcp/simple/order/service/OrderServiceSpringBootTest.java new file mode 100644 index 00000000..b902092d --- /dev/null +++ b/mybatis/src/test/java/com/github/kuangcp/simple/order/service/OrderServiceSpringBootTest.java @@ -0,0 +1,23 @@ +package com.github.kuangcp.simple.order.service; + +import com.github.kuangcp.base.SpringBootTestStarter; +import com.github.kuangcp.simple.order.dto.OrderDTO; +import java.util.List; +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; + +@Slf4j +public class OrderServiceSpringBootTest extends SpringBootTestStarter { + + @Autowired + private OrderService orderService; + + @Test + public void testCompareQueryId() { + List result = orderService.queryByUserIdWithLoop(200L); + log.info(": result={}", result); + result = orderService.queryByUserId(200L); + log.info(": result={}", result); + } +} \ No newline at end of file diff --git a/mybatis/src/test/java/com/github/kuangcp/stream/CursorSessionSpringBootTest.java b/mybatis/src/test/java/com/github/kuangcp/stream/CursorSessionSpringBootTest.java new file mode 100644 index 00000000..3aa235c5 --- /dev/null +++ b/mybatis/src/test/java/com/github/kuangcp/stream/CursorSessionSpringBootTest.java @@ -0,0 +1,118 @@ +package com.github.kuangcp.stream; + +import com.github.kuangcp.base.SpringBootTestStarter; +import com.github.kuangcp.sharding.manual.AuthUtil; +import com.github.kuangcp.simple.customer.dao.CustomerDao; +import com.github.kuangcp.simple.customer.domain.Customer; +import com.github.kuangcp.stream.dao.ReportDao; +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.util.StopWatch; + +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +/** + * @author https://github.com/kuangcp on 2021-07-12 23:29 + */ +@Slf4j +public class CursorSessionSpringBootTest extends SpringBootTestStarter { + + @Autowired + private CustomerDao customerDao; + @Autowired + private AuthUtil authUtil; + @Autowired + private ReportDao reportDao; + + @Test + public void testStream() throws Exception { + authUtil.completeAuth(3L); + List result = new ArrayList<>(); + customerDao.streamQueryAll(ctx -> { + Customer val = ctx.getResultObject(); + result.add(val); + }); + log.info("result={}", result); + } + + @Test + public void testInsertCache() throws Exception { + List strList = IntStream.range(0, 70000) + .mapToObj(v -> UUID.randomUUID().toString()).collect(Collectors.toList()); + Random random = new Random(); + long userFrame = 1; // 构造不同数据段 + for (int i = 1; i < 120_000; i++) { + LocalDateTime now = LocalDateTime.now(); + LocalDateTime time = now.plusDays(-1 * (i / 2_000)).plusMinutes(i % 30).plusSeconds(i % 33).plusNanos(i % 4000); + Date statisticDate = Date.from(time.atZone(ZoneId.systemDefault()).toInstant()); + + Report report = Report.builder() + .userId((long) i % 6 + userFrame) +// .userId((long) i * new Random().nextInt(300)) + .b(strList.get(random.nextInt(70000))) + .c(i % 7) + .d(i % 4) + .e(i % 2) + .statisticsTime(statisticDate) + .build(); + reportDao.insert(report); + } + } + + @Test + public void testPageQueryOne() throws Exception { + StopWatch stopWatch = new StopWatch(); + stopWatch.start("query"); + Date start = Date.from(LocalDateTime.now().plusDays(-500).atZone(ZoneId.systemDefault()).toInstant()); + List reports = reportDao.queryByPage(start, new Date(), 0, 1000); + stopWatch.stop(); + log.info("reports.size()={}", reports.size()); + log.info("={}", stopWatch.prettyPrint()); + } + + // 90s = 30 * 3s + @Test + public void testPageQueryAll() throws Exception { + StopWatch stopWatch = new StopWatch(); + for (int i = 0; i < 10; i++) { + stopWatch.start("query" + i); + Date start = Date.from(LocalDateTime.now().plusDays(-500).atZone(ZoneId.systemDefault()).toInstant()); + List reports = reportDao.queryByPage(start, new Date(), i * 1000, 1000); + stopWatch.stop(); + log.info("reports.size()={}", reports.size()); + } + log.info("={}", stopWatch.prettyPrint()); + } + + // 3s + @Test + public void testStreamQuery() throws Exception { + Date start = Date.from(LocalDateTime.now().plusDays(-500).atZone(ZoneId.systemDefault()).toInstant()); + + List result = new ArrayList<>(); + StopWatch stopWatch = new StopWatch(); + stopWatch.start("query"); + reportDao.selectAutoList(start, new Date(), ctx -> { + Report val = ctx.getResultObject(); +// try { +// TimeUnit.NANOSECONDS.sleep(100); +// } catch (InterruptedException e) { +// e.printStackTrace(); +// } + result.add(val); + }); + stopWatch.stop(); + log.info("result={} {}ms {}", result.size(), stopWatch.getTotalTimeMillis(), stopWatch.prettyPrint()); + +// Path path = Paths.get("/tmp/report.scv"); +// String content = result.stream().map(report -> String.format("%d,%s,%d,%d,%d", report.getUserId(), +// report.getInquiryCode(), report.getInquiryCount(), report.getHaveGoodsCount(), report.getPurchaseCount())) +// .collect(Collectors.joining("\n")); +// Files.write(path, content.getBytes()); + } +} diff --git a/mybatis/src/test/java/com/github/kuangcp/stream/Readme.md b/mybatis/src/test/java/com/github/kuangcp/stream/Readme.md new file mode 100644 index 00000000..503934ea --- /dev/null +++ b/mybatis/src/test/java/com/github/kuangcp/stream/Readme.md @@ -0,0 +1,11 @@ +使用流优化大数据量查询和导出 + +Stream模式 + +弊端: + +查看Mybatis实现原理,是否有连接高频占用和释放风险 + +优势: + +降低SQL查询扫描行数 \ No newline at end of file From cc5b69a6f8d5104e765ea12682ede64c2aec09fc Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Mon, 28 Aug 2023 22:50:34 +0800 Subject: [PATCH 280/476] compile --- flink/{build.gradle => build.gradle.bak} | 0 flink/pom.xml | 37 +++++++++++++ hadoop/build.gradle | 12 ----- hadoop/pom.xml | 35 ++++++++++++ kafka/{build.gradle => build.gradle.bak} | 0 kafka/pom.xml | 41 ++++++++++++++ mybatis/{build.gradle => build.gradle.bak} | 0 mybatis/pom.xml | 63 ++++++++++++++++++++++ pom.xml | 23 ++++++++ 9 files changed, 199 insertions(+), 12 deletions(-) rename flink/{build.gradle => build.gradle.bak} (100%) create mode 100644 flink/pom.xml delete mode 100644 hadoop/build.gradle create mode 100644 hadoop/pom.xml rename kafka/{build.gradle => build.gradle.bak} (100%) create mode 100644 kafka/pom.xml rename mybatis/{build.gradle => build.gradle.bak} (100%) create mode 100644 mybatis/pom.xml diff --git a/flink/build.gradle b/flink/build.gradle.bak similarity index 100% rename from flink/build.gradle rename to flink/build.gradle.bak diff --git a/flink/pom.xml b/flink/pom.xml new file mode 100644 index 00000000..db2fffad --- /dev/null +++ b/flink/pom.xml @@ -0,0 +1,37 @@ + + + + com.github.kuangcp + java-base + 1.0.0 + + + 4.0.0 + + flink + 1.0.0 + + + UTF-8 + UTF-8 + + + + + org.apache.flink + flink-java + + + org.apache.flink + flink-clients_2.12 + + + org.apache.flink + flink-streaming-java_2.12 + + + + + diff --git a/hadoop/build.gradle b/hadoop/build.gradle deleted file mode 100644 index b20508c7..00000000 --- a/hadoop/build.gradle +++ /dev/null @@ -1,12 +0,0 @@ -dependencies { - implementation project(":common-config") - implementation (libs["hadoop-common"]) { - exclude group: 'org.slf4j' - } - implementation (libs["hadoop-client"]){ - exclude group: 'org.slf4j' - } - implementation (libs["hadoop-hdfs"]){ - exclude group: 'org.slf4j' - } -} \ No newline at end of file diff --git a/hadoop/pom.xml b/hadoop/pom.xml new file mode 100644 index 00000000..bb8fd367 --- /dev/null +++ b/hadoop/pom.xml @@ -0,0 +1,35 @@ + + + + com.github.kuangcp + java-base + 1.0.0 + + + 4.0.0 + + hadoop + 1.0.0 + + + UTF-8 + UTF-8 + + + + org.apache.hadoop + hadoop-common + + + org.apache.hadoop + hadoop-client + + + org.apache.hadoop + hadoop-hdfs + + + + diff --git a/kafka/build.gradle b/kafka/build.gradle.bak similarity index 100% rename from kafka/build.gradle rename to kafka/build.gradle.bak diff --git a/kafka/pom.xml b/kafka/pom.xml new file mode 100644 index 00000000..8de34cfc --- /dev/null +++ b/kafka/pom.xml @@ -0,0 +1,41 @@ + + + + com.github.kuangcp + java-base + 1.0.0 + + + 4.0.0 + + kafka + 1.0.0 + + + UTF-8 + UTF-8 + + + + com.github.kuangcp + kcp-tool + + + + org.apache.kafka + kafka-clients + + + + com.fasterxml.jackson.core + jackson-core + + + com.fasterxml.jackson.core + jackson-databind + + + + diff --git a/mybatis/build.gradle b/mybatis/build.gradle.bak similarity index 100% rename from mybatis/build.gradle rename to mybatis/build.gradle.bak diff --git a/mybatis/pom.xml b/mybatis/pom.xml new file mode 100644 index 00000000..d81adfab --- /dev/null +++ b/mybatis/pom.xml @@ -0,0 +1,63 @@ + + + + com.github.kuangcp + java-base + 1.0.0 + + + 4.0.0 + + mybatis + 1.0.0 + + + UTF-8 + UTF-8 + + + + com.github.kuangcp + kcp-tool + + + + com.fasterxml.jackson.core + jackson-core + + + com.fasterxml.jackson.core + jackson-databind + + + + org.mybatis + mybatis + + + com.baomidou + mybatis-plus + + + + org.springframework.boot + spring-boot-starter-test + + + org.springframework.boot + spring-boot-starter-web + + + mysql + mysql-connector-java + + + com.baomidou + mybatis-plus-boot-starter + + + + + diff --git a/pom.xml b/pom.xml index 452fe656..50337f54 100644 --- a/pom.xml +++ b/pom.xml @@ -19,11 +19,15 @@ class collection concurrency + flink generic guava gui + hadoop io java8 + kafka + mybatis netty network pattern @@ -241,6 +245,24 @@ aspectjweaver 1.9.0 + + + org.springframework.boot + spring-boot-starter-test + 2.2.5.RELEASE + + + org.springframework.boot + spring-boot-starter-web + 2.2.5.RELEASE + + + + com.baomidou + mybatis-plus-boot-starter + 3.1.2 + + org.springframework spring-core @@ -266,6 +288,7 @@ spring-orm 5.1.20.RELEASE + org.mybatis mybatis From f62559c8e74f04d0fddd6e55ca00effa803340d5 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Mon, 28 Aug 2023 23:19:59 +0800 Subject: [PATCH 281/476] fix: groovy pom type --- class/pom.xml | 2 -- concurrency/pom.xml | 39 +++++++++++++++++++++++++++++++++------ pom.xml | 22 ++++++++++++++-------- 3 files changed, 47 insertions(+), 16 deletions(-) diff --git a/class/pom.xml b/class/pom.xml index 4f227d3c..7488b815 100644 --- a/class/pom.xml +++ b/class/pom.xml @@ -62,8 +62,6 @@ jackson-dataformat-smile - - org.openjdk.jmh jmh-core diff --git a/concurrency/pom.xml b/concurrency/pom.xml index d94936e4..b6b6d6bf 100644 --- a/concurrency/pom.xml +++ b/concurrency/pom.xml @@ -20,8 +20,8 @@ - io.github.resilience4j - resilience4j-circuitbreaker + com.github.kuangcp + kcp-tool @@ -33,10 +33,11 @@ com.bladejava blade-mvc - - - - + + org.apache.groovy + groovy-all + pom + org.apache.commons commons-lang3 @@ -47,6 +48,32 @@ testng + + + io.github.resilience4j + resilience4j-circuitbreaker + + + io.github.resilience4j + resilience4j-ratelimiter + + + io.github.resilience4j + resilience4j-retry + + + io.github.resilience4j + resilience4j-bulkhead + + + io.github.resilience4j + resilience4j-cache + + + io.github.resilience4j + resilience4j-timelimiter + + diff --git a/pom.xml b/pom.xml index 50337f54..7bc1e6b3 100644 --- a/pom.xml +++ b/pom.xml @@ -112,6 +112,7 @@ org.apache.groovy groovy-all 4.0.14 + pom @@ -450,14 +451,8 @@ - gitee - kuangcp gitee - https://gitee.com/gin9/MavenRepos/raw/master - - - maven-restlet - Public online Restlet repository - https://maven.restlet.talend.com + Apache + https://repo.maven.apache.org/maven2/ public @@ -467,5 +462,16 @@ true + + gitee + kuangcp gitee + https://gitee.com/gin9/MavenRepos/raw/master + + + maven-restlet + Public online Restlet repository + https://maven.restlet.talend.com + + From 52cbdc5937eb0515807c40cef82b8156c3e963f9 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Mon, 28 Aug 2023 23:33:23 +0800 Subject: [PATCH 282/476] rm: oh gradle --- algorithms/build.gradle.bak | 5 -- build.gradle.bak | 49 --------------- class/build.gradle.bak | 18 ------ collection/build.gradle.bak | 8 --- concurrency/build.gradle.bak | 29 --------- dependency.gradle.bak | 115 ----------------------------------- flink/build.gradle.bak | 28 --------- generic/build.gradle.bak | 4 -- guava/build.gradle.bak | 4 -- gui/build.gradle.bak | 51 ---------------- io/build.gradle.bak | 4 -- java8/build.gradle.bak | 4 -- kafka/build.gradle.bak | 7 --- mybatis/build.gradle.bak | 21 ------- netty/build.gradle.bak | 4 -- network/build.gradle.bak | 4 -- pattern/build.gradle.bak | 5 -- question/build.gradle.bak | 12 ---- settings.gradle.bak | 19 ------ spring/build.gradle.bak | 18 ------ test/build.gradle.bak | 3 - web/build.gradle.bak | 8 --- 22 files changed, 420 deletions(-) delete mode 100644 algorithms/build.gradle.bak delete mode 100644 build.gradle.bak delete mode 100644 class/build.gradle.bak delete mode 100644 collection/build.gradle.bak delete mode 100644 concurrency/build.gradle.bak delete mode 100644 dependency.gradle.bak delete mode 100644 flink/build.gradle.bak delete mode 100644 generic/build.gradle.bak delete mode 100644 guava/build.gradle.bak delete mode 100644 gui/build.gradle.bak delete mode 100644 io/build.gradle.bak delete mode 100644 java8/build.gradle.bak delete mode 100644 kafka/build.gradle.bak delete mode 100644 mybatis/build.gradle.bak delete mode 100644 netty/build.gradle.bak delete mode 100644 network/build.gradle.bak delete mode 100644 pattern/build.gradle.bak delete mode 100644 question/build.gradle.bak delete mode 100644 settings.gradle.bak delete mode 100644 spring/build.gradle.bak delete mode 100644 test/build.gradle.bak delete mode 100644 web/build.gradle.bak diff --git a/algorithms/build.gradle.bak b/algorithms/build.gradle.bak deleted file mode 100644 index 36e1be10..00000000 --- a/algorithms/build.gradle.bak +++ /dev/null @@ -1,5 +0,0 @@ -dependencies { - implementation(group: 'com.github.jsonzou', name: 'jmockdata', version: '4.1.2') - implementation(libs['common-codec']) - -} \ No newline at end of file diff --git a/build.gradle.bak b/build.gradle.bak deleted file mode 100644 index aa233324..00000000 --- a/build.gradle.bak +++ /dev/null @@ -1,49 +0,0 @@ -apply from: 'dependency.gradle' - -buildscript { - repositories { - maven { - url "https://plugins.gradle.org/m2/" - } - } -} - -// parent and child project all use this config -subprojects { - apply plugin: 'java' - apply plugin: 'maven-publish' - - group = 'com.github.kuangcp' - - // default build dir is build/ but idea compile dir is out/, i'm dislike this way - // dir out/ just have production/ and test/ dir - buildDir = 'out/build' - - sourceCompatibility = 1.8 - targetCompatibility = 1.8 - - repositories { - mavenLocal() - def kuangcp = "https://gitee.com/gin9/MavenRepos/raw/master" - - maven { - url = kuangcp - } - } - - // All sub-modules add the following dependencies - dependencies { - implementation libs['kcp-tool'] - - annotationProcessor libs['lombok'] - compileOnly libs['lombok'] - testAnnotationProcessor libs['lombok'] - testCompileOnly libs['lombok'] - - implementation libs['logback-classic'] - - testImplementation libs['junit'] - testImplementation libs['hamcrest-core'] - testImplementation libs['hamcrest-lib'] - } -} diff --git a/class/build.gradle.bak b/class/build.gradle.bak deleted file mode 100644 index 1adffe57..00000000 --- a/class/build.gradle.bak +++ /dev/null @@ -1,18 +0,0 @@ -dependencies { - - - implementation libs['kcp-tool'] - - implementation libs['fastjson'] - implementation libs['gson'] - implementation libs['jackson_core'] - implementation libs['jackson_databind'] - implementation libs['jackson_annotations'] - implementation libs['jackson_smile'] - - implementation libs['jmh'] - annotationProcessor libs['jmh-generator-annprocess'] - - implementation libs['common-lang'] - implementation libs['cglib'] -} diff --git a/collection/build.gradle.bak b/collection/build.gradle.bak deleted file mode 100644 index 4b45b865..00000000 --- a/collection/build.gradle.bak +++ /dev/null @@ -1,8 +0,0 @@ -plugins { - id 'java' -} -dependencies { - - implementation libs['kcp-tool'] -// implementation libs['testng'] -} diff --git a/concurrency/build.gradle.bak b/concurrency/build.gradle.bak deleted file mode 100644 index b6ead2ac..00000000 --- a/concurrency/build.gradle.bak +++ /dev/null @@ -1,29 +0,0 @@ -plugins { - id 'groovy' - id 'maven-publish' - id 'application' -} - -application { - mainClass = 'web.Application' -} - -dependencies { - - implementation libs['kcp-tuple'] - - implementation libs['blade-mvc'] - - implementation libs['jedis'] - implementation libs['groovy'] - implementation libs['common-lang'] - - implementation "io.github.resilience4j:resilience4j-circuitbreaker:$ver.resilience4j" - implementation "io.github.resilience4j:resilience4j-ratelimiter:$ver.resilience4j" - implementation "io.github.resilience4j:resilience4j-retry:$ver.resilience4j" - implementation "io.github.resilience4j:resilience4j-bulkhead:$ver.resilience4j" - implementation "io.github.resilience4j:resilience4j-cache:$ver.resilience4j" - implementation "io.github.resilience4j:resilience4j-timelimiter:$ver.resilience4j" - - testImplementation(libs['testng']) -} \ No newline at end of file diff --git a/dependency.gradle.bak b/dependency.gradle.bak deleted file mode 100644 index 9b3b6861..00000000 --- a/dependency.gradle.bak +++ /dev/null @@ -1,115 +0,0 @@ -// when add a new dependency, gradle not download that package -// just when module build.gradle use this dependency, then download and build project - -ext { - ver = [ - logback : '1.2.5' - , jedis : '2.9.0' - , junit : '4.13.2' - , groovy : '3.0.8' - , mail : '1.4.7' - , jackson : '2.9.5' - , hamcrest : '1.3' - , kcp_tool : '1.0.8' - , netty : '4.1.67.Final' - , guava : '30.1.1-jre' - , jmh : '1.33' - , kafka : '2.3.1' - , spring : '5.1.20.RELEASE' - , aspectj : '1.9.0' - , hadoop : '2.7.2' - , flink : '1.8.0' - , resilience4j: '1.7.0' - ] - libs = [ - // mine tool - "kcp-tool" : "com.github.kuangcp:kcp-tool:$ver.kcp_tool" - , "kcp-tuple" : "com.github.kuangcp:kcp-tuple:$ver.kcp_tool" - , "kcp-core" : "com.github.kuangcp:kcp-core:$ver.kcp_tool" - - // tool - , "lombok" : "org.projectlombok:lombok:1.18.2" - , "common-lang" : "org.apache.commons:commons-lang3:3.7" - , "commons-collections4" : "org.apache.commons:commons-collections4:4.4" - , "groovy" : "org.codehaus.groovy:groovy-all:$ver.groovy" - , "cglib" : "cglib:cglib:3.2.9" - , "common-math" : "org.apache.commons:commons-math3:3.6.1" - , "common-codec" : "commons-codec:commons-codec:1.13" - , "guava" : "com.google.guava:guava:$ver.guava" - - // test, junit has removed hamcrest since 4.11 - , "hamcrest-core" : "org.hamcrest:hamcrest-core:$ver.hamcrest" - , "hamcrest-lib" : "org.hamcrest:hamcrest-library:$ver.hamcrest" - , "jmh" : "org.openjdk.jmh:jmh-core:$ver.jmh" - , "jmh-generator-annprocess": "org.openjdk.jmh:jmh-generator-annprocess:$ver.jmh" - - , "junit" : "junit:junit:$ver.junit" - , "mockito-core" : "org.mockito:mockito-core:2.21.0" - , "testng" : "org.testng:testng:6.14.3" - - // log - , "logback-classic" : "ch.qos.logback:logback-classic:$ver.logback" - - // network - , "ok-http" : "com.squareup.okhttp3:okhttp:3.13.1" - , "netty" : "io.netty:netty-all:$ver.netty" - , "blade-mvc" : "com.bladejava:blade-mvc:2.0.15.RELEASE" - - // mail - , "mail" : "javax.mail:mail:$ver.mail" - , "activation" : "javax.activation:activation:1.1.1" - - // db - , "jedis" : "redis.clients:jedis:3.0.1" - , "MySQL" : "mysql:mysql-connector-java:8.0.15" - - // JSON dependency - , "gson" : "com.google.code.gson:gson:2.8.5" - , "fastjson" : "com.alibaba:fastjson:1.2.47" - , "jackson_core" : "com.fasterxml.jackson.core:jackson-core:$ver.jackson" - , "jackson_databind" : "com.fasterxml.jackson.core:jackson-databind:$ver.jackson" - , "jackson_annotations" : "com.fasterxml.jackson.core:jackson-annotations:$ver.jackson" - , "jackson_smile" : "com.fasterxml.jackson.dataformat:jackson-dataformat-smile:$ver.jackson" - - , "aspectjrt" : "org.aspectj:aspectjrt:$ver.aspectj" - , "aspectjweaver" : "org.aspectj:aspectjweaver:$ver.aspectj" - - // Spring - , "spring-core" : "org.springframework:spring-core:$ver.spring" - , "spring-ctx" : "org.springframework:spring-context:$ver.spring" - , "spring-beans" : "org.springframework:spring-beans:$ver.spring" - , "spring-aop" : "org.springframework:spring-aop:$ver.spring" - , "spring-orm" : "org.springframework:spring-orm:$ver.spring" - - // mybatis - , "mybatis" : "org.mybatis:mybatis:3.5.2" - , "mybatis-plus" : "com.baomidou:mybatis-plus:3.1.2" - - // kafka - , "kafka-clients" : "org.apache.kafka:kafka-clients:$ver.kafka" - - // flink - , "flink-java" : "org.apache.flink:flink-java:$ver.flink" - , "flink-clients" : "org.apache.flink:flink-clients_2.12:$ver.flink" - , "flink-streaming-java" : "org.apache.flink:flink-streaming-java_2.12:$ver.flink" - - // hadoop - , "hadoop-common" : "org.apache.hadoop:hadoop-common:$ver.hadoop" - , "hadoop-client" : "org.apache.hadoop:hadoop-client:$ver.hadoop" - , "hadoop-hdfs" : "org.apache.hadoop:hadoop-hdfs:$ver.hadoop" - - // the dependencies with specific version for problem situation - , "cglib-3.2.4" : "cglib:cglib:3.2.4" - , "asm-3.1" : "asm:asm:3.1" - , "asm-5.1" : "org.ow2.asm:asm:5.1" - - // only support JDK10 -// , "quasar-core" : "co.paralleluniverse:quasar-core:0.8.0" -// , "quasar-actors" : "co.paralleluniverse:quasar-actors:0.8.0" -// , "quasar-reactive-streams" : "co.paralleluniverse:quasar-reactive-streams:0.8.0", - - , "quasar-core" : "co.paralleluniverse:quasar-core:0.7.4:jdk8" - , "quasar-actors" : "co.paralleluniverse:quasar-actors:0.7.4:jdk8" - , "quasar-reactive-streams" : "co.paralleluniverse:quasar-reactive-streams:0.7.4:jdk8" - ] -} \ No newline at end of file diff --git a/flink/build.gradle.bak b/flink/build.gradle.bak deleted file mode 100644 index 114821ab..00000000 --- a/flink/build.gradle.bak +++ /dev/null @@ -1,28 +0,0 @@ -version = '1.0.0-SNAPSHOT' - -apply plugin: 'java' -apply plugin: 'application' - -dependencies { - implementation project(":common-config") - implementation libs['flink-java'] - implementation libs['flink-clients'] - implementation libs['flink-streaming-java'] -} - -application { - mainClassName = "com.github.kuangcp.hi.SimpleStatistic" -} - -task uberJar(type: Jar) { - archiveClassifier = 'all-dependency' - - from sourceSets.main.output - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith('jar') }.collect { zipTree(it) } - } - manifest { - attributes 'Main-Class': 'com.github.kuangcp.hi.SimpleStatistic' - } -} diff --git a/generic/build.gradle.bak b/generic/build.gradle.bak deleted file mode 100644 index d91597c1..00000000 --- a/generic/build.gradle.bak +++ /dev/null @@ -1,4 +0,0 @@ -dependencies{ - - implementation rootProject.libs['common-math'] -} \ No newline at end of file diff --git a/guava/build.gradle.bak b/guava/build.gradle.bak deleted file mode 100644 index 8d7b8b56..00000000 --- a/guava/build.gradle.bak +++ /dev/null @@ -1,4 +0,0 @@ -dependencies { - - implementation libs['guava'] -} \ No newline at end of file diff --git a/gui/build.gradle.bak b/gui/build.gradle.bak deleted file mode 100644 index 55be1eb7..00000000 --- a/gui/build.gradle.bak +++ /dev/null @@ -1,51 +0,0 @@ -plugins { - id 'application' -} - -dependencies { - implementation libs['junit'] - implementation libs['kcp-core'] -// implementation libs['quasar-core'] -} - -//configurations { -// quasar -//} -//task runQuasar { -// jvmArgs "-javaagent:${configurations.quasar.iterator().next()}" -//} -//run.dependsOn runQuasar - -//tasks.withType(JavaExec){ -// jvmArgs "-javaagent:${configurations.quasar.iterator().next()}" -//} - -tasks.register('tank') { - dependsOn build - application.mainClass = 'com.github.kuangcp.tank.v3.MainTankGame' - println "Build Tank Game ..." -} - -tasks.register('note') { - dependsOn build - application.mainClass = 'com.github.kuangcp.notepad.Note' - println "Build Note ..." -} - -tasks.register('virus') { - dependsOn build - application.mainClass = 'com.github.kuangcp.virusbroadcast.VirusBroadcast' - println "Build Virus Demo ..." -} - -tasks.register('calc') { - dependsOn build - application.mainClass = 'com.github.kuangcp.caculator.Calculator' - println "Build Calculator ..." -} - -tasks.register('gomoku') { - dependsOn build - application.mainClass = 'com.github.kuangcp.gomoku.Gomoku' - println "Build Gomoku ..." -} \ No newline at end of file diff --git a/io/build.gradle.bak b/io/build.gradle.bak deleted file mode 100644 index d4fdeb0d..00000000 --- a/io/build.gradle.bak +++ /dev/null @@ -1,4 +0,0 @@ -dependencies { - - implementation libs['common-lang'] -} diff --git a/java8/build.gradle.bak b/java8/build.gradle.bak deleted file mode 100644 index 5a11761d..00000000 --- a/java8/build.gradle.bak +++ /dev/null @@ -1,4 +0,0 @@ -dependencies { - - implementation(rootProject.libs['kcp-tool']) -} diff --git a/kafka/build.gradle.bak b/kafka/build.gradle.bak deleted file mode 100644 index b6fd107d..00000000 --- a/kafka/build.gradle.bak +++ /dev/null @@ -1,7 +0,0 @@ -dependencies { - implementation project(":common-config") - implementation libs['kcp-tool'] - implementation libs['kafka-clients'] - implementation libs['jackson_core'] - implementation libs['jackson_databind'] -} \ No newline at end of file diff --git a/mybatis/build.gradle.bak b/mybatis/build.gradle.bak deleted file mode 100644 index 83e23256..00000000 --- a/mybatis/build.gradle.bak +++ /dev/null @@ -1,21 +0,0 @@ -plugins { - id 'org.springframework.boot' version '2.2.5.RELEASE' -} - -apply plugin: 'java' -apply plugin: 'io.spring.dependency-management' - -repositories { - mavenCentral() -} - -dependencies { - implementation project(":common-config") - implementation("org.springframework.boot:spring-boot-starter-web") - testImplementation("org.springframework.boot:spring-boot-starter-test") - implementation("com.baomidou:mybatis-plus-boot-starter:3.1.2") - implementation("mysql:mysql-connector-java:8.0.17") - - implementation libs["mybatis"] - implementation libs["mybatis-plus"] -} diff --git a/netty/build.gradle.bak b/netty/build.gradle.bak deleted file mode 100644 index ad8323d8..00000000 --- a/netty/build.gradle.bak +++ /dev/null @@ -1,4 +0,0 @@ -dependencies { - implementation libs ['netty'] - implementation libs ['testng'] -} \ No newline at end of file diff --git a/network/build.gradle.bak b/network/build.gradle.bak deleted file mode 100644 index ef69ec0b..00000000 --- a/network/build.gradle.bak +++ /dev/null @@ -1,4 +0,0 @@ -dependencies { - - implementation rootProject.libs['ok-http'] -} \ No newline at end of file diff --git a/pattern/build.gradle.bak b/pattern/build.gradle.bak deleted file mode 100644 index 4877effb..00000000 --- a/pattern/build.gradle.bak +++ /dev/null @@ -1,5 +0,0 @@ - -dependencies { - - implementation rootProject.libs['guava'] -} diff --git a/question/build.gradle.bak b/question/build.gradle.bak deleted file mode 100644 index 37ab04ef..00000000 --- a/question/build.gradle.bak +++ /dev/null @@ -1,12 +0,0 @@ -dependencies { - - implementation libs['testng'] - - implementation libs["cglib-3.2.4"] - implementation libs["asm-3.1"] - implementation libs["asm-5.1"] - implementation libs["spring-core"] - implementation libs["common-lang"] - implementation libs["commons-collections4"] - implementation libs["gson"] -} \ No newline at end of file diff --git a/settings.gradle.bak b/settings.gradle.bak deleted file mode 100644 index 3946935c..00000000 --- a/settings.gradle.bak +++ /dev/null @@ -1,19 +0,0 @@ -rootProject.name = 'JavaBase' -include( - 'io' - , 'generic' - , 'collection' - , 'class' - , 'gui' - , 'network' - , 'algorithms' - , 'question' - , 'pattern' - , 'java8' - , 'concurrency' - , 'test' - , 'guava' - , 'netty' - , 'spring' - , 'web' -) diff --git a/spring/build.gradle.bak b/spring/build.gradle.bak deleted file mode 100644 index 3a1d05bb..00000000 --- a/spring/build.gradle.bak +++ /dev/null @@ -1,18 +0,0 @@ -dependencies { - - implementation libs['MySQL'] - implementation libs['cglib'] - - implementation libs['aspectjrt'] - implementation libs['aspectjweaver'] - - implementation libs['spring-core'] - implementation libs['spring-ctx'] - implementation libs['spring-beans'] - implementation libs['spring-aop'] - implementation libs['spring-orm'] - - implementation 'org.hibernate:hibernate-core:5.3.7.Final' - - implementation 'commons-dbcp:commons-dbcp:1.4' -} diff --git a/test/build.gradle.bak b/test/build.gradle.bak deleted file mode 100644 index 0ce6a166..00000000 --- a/test/build.gradle.bak +++ /dev/null @@ -1,3 +0,0 @@ -dependencies { - -} \ No newline at end of file diff --git a/web/build.gradle.bak b/web/build.gradle.bak deleted file mode 100644 index c87a8e6a..00000000 --- a/web/build.gradle.bak +++ /dev/null @@ -1,8 +0,0 @@ -dependencies { - - - implementation group: 'javax.validation', name: 'validation-api', version: '2.0.1.Final' - implementation group: 'org.hibernate.validator', name: 'hibernate-validator', version: '6.2.0.Final' - implementation group: 'javax.el', name: 'javax.el-api', version: '3.0.0' - implementation group: 'org.glassfish.web', name: 'javax.el', version: '2.2.6' -} \ No newline at end of file From e18b3033994db61f700f831f15be244149e62781 Mon Sep 17 00:00:00 2001 From: Kcp Date: Tue, 29 Aug 2023 20:49:05 +0800 Subject: [PATCH 283/476] dep --- pom.xml | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/pom.xml b/pom.xml index 7bc1e6b3..5e9df954 100644 --- a/pom.xml +++ b/pom.xml @@ -11,6 +11,8 @@ UTF-8 UTF-8 1.8 + 2.15.2 + 5.1.20.RELEASE @@ -133,7 +135,7 @@ com.google.guava guava - 30.1.1-jre + 32.0.0-jre org.hamcrest @@ -219,22 +221,22 @@ com.fasterxml.jackson.core jackson-core - 2.9.5 + ${jackson.version} com.fasterxml.jackson.core jackson-databind - 2.9.5 + ${jackson.version} com.fasterxml.jackson.core jackson-annotations - 2.9.5 + ${jackson.version} com.fasterxml.jackson.dataformat jackson-dataformat-smile - 2.9.5 + ${jackson.version} org.aspectj @@ -267,27 +269,27 @@ org.springframework spring-core - 5.1.20.RELEASE + ${spring.version} org.springframework spring-context - 5.1.20.RELEASE + ${spring.version} org.springframework spring-beans - 5.1.20.RELEASE + ${spring.version} org.springframework spring-aop - 5.1.20.RELEASE + ${spring.version} org.springframework spring-orm - 5.1.20.RELEASE + ${spring.version} From cef681302f4d8b9a25f3c4ff698bd2eceac6ecd7 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Tue, 29 Aug 2023 23:46:15 +0800 Subject: [PATCH 284/476] version def --- pom.xml | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/pom.xml b/pom.xml index 5e9df954..63c848a7 100644 --- a/pom.xml +++ b/pom.xml @@ -13,6 +13,9 @@ 1.8 2.15.2 5.1.20.RELEASE + 1.7.0 + 0.8.0 + 1.0.7 @@ -88,17 +91,17 @@ com.github.kuangcp kcp-tool - 1.0.7 + ${kcp-tool.version} com.github.kuangcp kcp-tuple - 1.0.7 + ${kcp-tool.version} com.github.kuangcp kcp-core - 1.0.7 + ${kcp-tool.version} org.apache.commons @@ -381,20 +384,21 @@ asm 5.1 + co.paralleluniverse quasar-core - 0.7.4 + ${quasar.version} co.paralleluniverse quasar-actors - 0.7.4 + ${quasar.version} co.paralleluniverse quasar-reactive-streams - 0.7.4 + ${quasar.version} @@ -406,32 +410,32 @@ io.github.resilience4j resilience4j-circuitbreaker - 1.7.0 + ${resilience4j.version} io.github.resilience4j resilience4j-ratelimiter - 1.7.0 + ${resilience4j.version} io.github.resilience4j resilience4j-retry - 1.7.0 + ${resilience4j.version} io.github.resilience4j resilience4j-bulkhead - 1.7.0 + ${resilience4j.version} io.github.resilience4j resilience4j-cache - 1.7.0 + ${resilience4j.version} io.github.resilience4j resilience4j-timelimiter - 1.7.0 + ${resilience4j.version} From 38eb72de19f2540b18de9ba20de5d8ac98acac03 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Tue, 29 Aug 2023 23:52:34 +0800 Subject: [PATCH 285/476] wip: --- .../java/com/github/kuangcp/future/CompletableFutureTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/java8/src/test/java/com/github/kuangcp/future/CompletableFutureTest.java b/java8/src/test/java/com/github/kuangcp/future/CompletableFutureTest.java index 23b27af9..afca0afb 100644 --- a/java8/src/test/java/com/github/kuangcp/future/CompletableFutureTest.java +++ b/java8/src/test/java/com/github/kuangcp/future/CompletableFutureTest.java @@ -106,4 +106,5 @@ private int func2(int value) { return value + 2; } + // TODO 理解 lettuce 中 Future 的使用 } From e44a924329271f8a1985d4a3b4c12ee0d4dd7b11 Mon Sep 17 00:00:00 2001 From: Kcp Date: Wed, 30 Aug 2023 19:14:59 +0800 Subject: [PATCH 286/476] fmt --- .../github/kuangcp/list/ArrayListDemo.java | 32 ++++---- .../kuangcp/schedule/CreateModel.groovy | 24 +++--- .../github/kuangcp/schedule/ShowSTPE.groovy | 74 +++++++++---------- .../com/github/kuangcp/sync/SyncField.groovy | 53 ++++++------- .../java/guava/ratelimit/RateLimiterTest.java | 10 ++- 5 files changed, 100 insertions(+), 93 deletions(-) diff --git a/concurrency/src/main/java/com/github/kuangcp/list/ArrayListDemo.java b/concurrency/src/main/java/com/github/kuangcp/list/ArrayListDemo.java index ab77e02a..f1d0ca21 100644 --- a/concurrency/src/main/java/com/github/kuangcp/list/ArrayListDemo.java +++ b/concurrency/src/main/java/com/github/kuangcp/list/ArrayListDemo.java @@ -8,25 +8,25 @@ * Created by https://github.com/kuangcp on 17-8-15 上午10:12 * 普通的这个数组对象,也是和那个并发包的类一样的效果,完全是因为锁的原因, * 因为锁的存在,第一个线程和第二个线程的运行是有时效先后的,所以就会出现一个线程和另一个线程的结果是不一样的 - * 如果把锁去掉,就会有异常抛出: - - - * 这个类iterator 和 listIterator 方法返回的的迭代器 都是快速失败的,也就是说 - * 在创建迭代器之后,除非通过迭代器自身的 remove( 或 add 方法 1.8没有了)从结构上 (除了更新元素值的其他操作都是)对列表进行修改, - * 否则在任何时间以任何方式对列表进行修改,迭代器都会抛出 ConcurrentModificationException。 - * 快速失败并不总是可靠的,因为线程的不确定性,快速失败是尽量的做到抛出这个异常 - * - * 注意,这个类的实现不是同步的。使用这个封装就是同步的 - * List list = Collections.synchronizedList(new ArrayList(...)); - * - * 同样的,这里出现别的结果也是因为获得了同一个迭代器, 然后又因为加锁,数据同步了,所以结果是好多个, - * 例如这样的,5没有加入进去 6 却进去了 - * a: null, Element{phone='1'}, Element{phone='6'}, Element{phone='2'}, Element{phone='3'}, - a: + * 如果把锁去掉,就会有异常抛出: + *

+ *

+ * 这个类iterator 和 listIterator 方法返回的的迭代器 都是快速失败的,也就是说 + * 在创建迭代器之后,除非通过迭代器自身的 remove( 或 add 方法 1.8没有了)从结构上 (除了更新元素值的其他操作都是)对列表进行修改, + * 否则在任何时间以任何方式对列表进行修改,迭代器都会抛出 ConcurrentModificationException。 + * 快速失败并不总是可靠的,因为线程的不确定性,快速失败是尽量的做到抛出这个异常 + *

+ * 注意,这个类的实现不是同步的。使用这个封装就是同步的 + * List list = Collections.synchronizedList(new ArrayList(...)); + *

+ * 同样的,这里出现别的结果也是因为获得了同一个迭代器, 然后又因为加锁,数据同步了,所以结果是好多个, + * 例如这样的,5没有加入进去 6 却进去了 + * a: null, Element{phone='1'}, Element{phone='6'}, Element{phone='2'}, Element{phone='3'}, + * a: */ public class ArrayListDemo { - public static void main(String []s){ + public static void main(String[] s) { ArrayList elements = new ArrayList<>(); ReentrantLock lock = new ReentrantLock(); String name = "a"; diff --git a/concurrency/src/main/java/com/github/kuangcp/schedule/CreateModel.groovy b/concurrency/src/main/java/com/github/kuangcp/schedule/CreateModel.groovy index 0eeb3678..da5b3587 100644 --- a/concurrency/src/main/java/com/github/kuangcp/schedule/CreateModel.groovy +++ b/concurrency/src/main/java/com/github/kuangcp/schedule/CreateModel.groovy @@ -18,26 +18,26 @@ list.add(134) Callable callable = new Callable() { - @Override - String call() throws Exception { - return list.toString() - } + @Override + String call() throws Exception { + return list.toString() + } } println("result = " + callable.call()) def executor try { - executor = Executors.newSingleThreadExecutor() - Future future = executor.submit(callable) + executor = Executors.newSingleThreadExecutor() + Future future = executor.submit(callable) - def result = future.get(60, TimeUnit.MILLISECONDS) - println("future: " + result) + def result = future.get(60, TimeUnit.MILLISECONDS) + println("future: " + result) } catch (TimeoutException e) { - println("timeout" + e) + println("timeout" + e) } finally { - if (executor != null) { - executor.shutdown() - } + if (executor != null) { + executor.shutdown() + } } diff --git a/concurrency/src/main/java/com/github/kuangcp/schedule/ShowSTPE.groovy b/concurrency/src/main/java/com/github/kuangcp/schedule/ShowSTPE.groovy index 57ec0298..c421dceb 100644 --- a/concurrency/src/main/java/com/github/kuangcp/schedule/ShowSTPE.groovy +++ b/concurrency/src/main/java/com/github/kuangcp/schedule/ShowSTPE.groovy @@ -10,52 +10,52 @@ import java.util.concurrent.* * 若得到工作单元,则线程就会执行工作单元 */ class ReadService { - // 消息队列 - BlockingQueue lbp = new LinkedBlockingQueue<>() - // 一个延迟的、结果可接受的操作,可将其取消。通常已安排的 future 是用 ScheduledExecutorService 安排任务的结果。 - ScheduledFuture hndl - ScheduledExecutorService stpe = Executors.newScheduledThreadPool(2) + // 消息队列 + BlockingQueue lbp = new LinkedBlockingQueue<>() + // 一个延迟的、结果可接受的操作,可将其取消。通常已安排的 future 是用 ScheduledExecutorService 安排任务的结果。 + ScheduledFuture hndl + ScheduledExecutorService stpe = Executors.newScheduledThreadPool(2) - // 初始化一些信息,同样的调用了这个 线程池,执行了添加信息的工作单元 - void init() { - lbp.add("初始化") - lbp.add("1") - lbp.add("2") - lbp.add("3") - final Runnable Reader = new Runnable() { + // 初始化一些信息,同样的调用了这个 线程池,执行了添加信息的工作单元 + void init() { + lbp.add("初始化") + lbp.add("1") + lbp.add("2") + lbp.add("3") + final Runnable Reader = new Runnable() { - @Override - void run() { - lbp.add(System.currentTimeMillis() + "") - } + @Override + void run() { + lbp.add(System.currentTimeMillis() + "") + } + } + hndl = stpe.scheduleAtFixedRate(Reader, 10, 1000, TimeUnit.MILLISECONDS) } - hndl = stpe.scheduleAtFixedRate(Reader, 10, 1000, TimeUnit.MILLISECONDS) - } - void run() { - final Runnable Reader = new Runnable() { + void run() { + final Runnable Reader = new Runnable() { - @Override - void run() { - String msg = lbp.poll().toString() - if (msg != null) { - println("Receive msg:" + msg) - } + @Override + void run() { + String msg = lbp.poll().toString() + if (msg != null) { + println("Receive msg:" + msg) + } - } + } + } + hndl = stpe.scheduleAtFixedRate(Reader, 10, 500, TimeUnit.MILLISECONDS) } - hndl = stpe.scheduleAtFixedRate(Reader, 10, 500, TimeUnit.MILLISECONDS) - } - void cancel() { - final ScheduledFuture handle = hndl - stpe.schedule(new Runnable() { + void cancel() { + final ScheduledFuture handle = hndl + stpe.schedule(new Runnable() { - public void run() { - handle.cancel(true) - } - }, 10, TimeUnit.MILLISECONDS) - } + public void run() { + handle.cancel(true) + } + }, 10, TimeUnit.MILLISECONDS) + } } // run 和 cancel 是有顺序的,因为ScheduledFuture 对象共享覆盖的原因 diff --git a/concurrency/src/main/java/com/github/kuangcp/sync/SyncField.groovy b/concurrency/src/main/java/com/github/kuangcp/sync/SyncField.groovy index 6b1f2131..22415355 100644 --- a/concurrency/src/main/java/com/github/kuangcp/sync/SyncField.groovy +++ b/concurrency/src/main/java/com/github/kuangcp/sync/SyncField.groovy @@ -5,41 +5,42 @@ import java.util.concurrent.Executors /** * Created by https://github.com/kuangcp - * @author kuangcp* TODO 如何保证多线程并发情况下, 关闭一执行, 后面的操作全都不能做 + * @author kuangcp + * TODO 如何保证多线程并发情况下, 关闭一执行, 后面的操作全都不能做 * 现在做到了只关闭一次, 如何判断是否阻塞 * @date 18-4-17 上午10:16 */ class SyncField { - static void main(String[] s) { - Cache cache = new Cache() - int num = 50 - ExecutorService fixedThreadPool = Executors.newFixedThreadPool(90) - for (int i = 1; i <= num; i++) { - fixedThreadPool.execute(new Runnable() { + static void main(String[] s) { + Cache cache = new Cache() + int num = 50 + ExecutorService fixedThreadPool = Executors.newFixedThreadPool(90) + for (int i = 1; i <= num; i++) { + fixedThreadPool.execute(new Runnable() { - @Override - void run() { - for (; ;) { - if (cache.map.size() == 0 || cache.flag) { - return - } - int result = (int) ((Math.random() * 100) / 10 + 1) - println(result) - if (result < 2) { - println("准备关闭") - cache.close(fixedThreadPool) - } else { - cache.doOther() - } - sleep(500) - } + @Override + void run() { + for (; ;) { + if (cache.map.size() == 0 || cache.flag) { + return + } + int result = (int) ((Math.random() * 100) / 10 + 1) + println(result) + if (result < 2) { + println("准备关闭") + cache.close(fixedThreadPool) + } else { + cache.doOther() + } + sleep(500) + } + } + }) } - }) + fixedThreadPool.shutdown() } - fixedThreadPool.shutdown() - } } diff --git a/guava/src/test/java/guava/ratelimit/RateLimiterTest.java b/guava/src/test/java/guava/ratelimit/RateLimiterTest.java index 7235dace..4238f6bc 100644 --- a/guava/src/test/java/guava/ratelimit/RateLimiterTest.java +++ b/guava/src/test/java/guava/ratelimit/RateLimiterTest.java @@ -18,7 +18,7 @@ public void testLimit1() throws Exception { rateLimiter.acquire(); // SmoothRateLimiter - new Thread(() -> { + Runnable action = () -> { for (int i = 0; i < 1000; i++) { try { TimeUnit.MILLISECONDS.sleep(100); @@ -28,7 +28,13 @@ public void testLimit1() throws Exception { log.error("", e); } } - }).start(); + }; + Thread first = new Thread(action); + first.setName("first"); + first.start(); + Thread second = new Thread(action); + second.setName("second"); + second.start(); TimeUnit.HOURS.sleep(1); } From 713ff04a129939720aa157fc579a9b294237371a Mon Sep 17 00:00:00 2001 From: Kcp Date: Thu, 31 Aug 2023 19:21:00 +0800 Subject: [PATCH 287/476] fmt --- collection/pom.xml | 4 + .../list/ConcurrentModificationTest.java | 206 +++++++++--------- .../kuangcp/list/ListWithArrayTest.java | 85 ++++---- .../github/kuangcp/list/OperationTest.java | 108 ++++----- .../map/ConcurrentModificationTest.java | 172 +++++++-------- .../com/github/kuangcp/map/HashMapTest.java | 137 ++++++------ .../github/kuangcp/map/IterateMapTest.java | 62 +++--- .../kuangcp/map/RandomGetValueTest.java | 22 +- guava/src/test/java/guava/base/AvoidNull.java | 12 - 9 files changed, 403 insertions(+), 405 deletions(-) delete mode 100644 guava/src/test/java/guava/base/AvoidNull.java diff --git a/collection/pom.xml b/collection/pom.xml index 33fe2ba5..dce83557 100644 --- a/collection/pom.xml +++ b/collection/pom.xml @@ -18,6 +18,10 @@ UTF-8 + + com.github.kuangcp + kcp-tool + diff --git a/collection/src/test/java/com/github/kuangcp/list/ConcurrentModificationTest.java b/collection/src/test/java/com/github/kuangcp/list/ConcurrentModificationTest.java index cc37023b..98e3cc6d 100644 --- a/collection/src/test/java/com/github/kuangcp/list/ConcurrentModificationTest.java +++ b/collection/src/test/java/com/github/kuangcp/list/ConcurrentModificationTest.java @@ -3,10 +3,8 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.IsEqual.equalTo; -import java.util.ArrayList; -import java.util.ConcurrentModificationException; -import java.util.Iterator; -import java.util.List; +import java.util.*; + import lombok.extern.slf4j.Slf4j; import org.junit.After; import org.junit.Before; @@ -15,9 +13,9 @@ /** * ConcurrentModificationException 的产生是因为 failFast 策略 * List 发生修改后就会修改 List 的 modCount - * + *

* 当 List 通过迭代器进行迭代时, 每次迭代会将迭代器中的 expectedModCount 和 List 的 modCount 进行比较 如果不一致就抛出异常 快速失败 - * + *

* 所以通过下标进行 remove 不会有异常 * * @author kuangcp on 19-1-16-上午9:07 @@ -25,115 +23,115 @@ @Slf4j public class ConcurrentModificationTest { - // 经测试 ArrayList LinkedList Vector 行为均一致 - private List list = new ArrayList<>(); - - /** - * 错误 - * - * 虽然不会抛出异常, 但是有bug, 因为list的长度随着remove在变小, - * 但是 遍历用的index没有随之变化, 就会出现元素被跳过没有被索引到的情况 - */ - @Test - public void testNormal() { - for (int i = 0; i < list.size(); i++) { - if (list.get(i).equals("del")) { - list.remove(i); - } + // 经测试 ArrayList LinkedList Vector 行为均一致 + private final List list = new ArrayList<>(); + + /** + * 错误 + *

+ * 虽然不会抛出异常, 但是有bug, 因为list的长度随着remove在变小, + * 但是 遍历用的index没有随之变化, 就会出现元素被跳过没有被索引到的情况 + */ + @Test + public void testNormal() { + for (int i = 0; i < list.size(); i++) { + if (list.get(i).equals("del")) { + list.remove(i); + } + } + + assertThat(list.size(), equalTo(6)); } - assertThat(list.size(), equalTo(6)); - } - - /** - * 错误 - * - * 反编译后的代码, forEach 实际上是语法糖, next 方法必然抛出异常 - * - *

-   * Iterator var1 = this.list.iterator();
-   * while(var1.hasNext()) {
-   *   String x = (String)var1.next();
-   *   if (x.equals("del")) {
-   *     this.list.remove(x);
-   *   }
-   * }
-   * 
- */ - @Test(expected = ConcurrentModificationException.class) - public void testForEach() { - for (String x : list) { - if (x.equals("del")) { - // 关键在于这里, 这个remove是使用迭代器遍历list, 找到目标后再复制原数组将对应的对象移除 - // 其中 next() 方法调用了 checkForComodification 方法 - // 由于 remove的调用增加了 modCount 所以在移除了一次后就抛出异常了 - list.remove(x); - log.info("remove one"); - // 如果这里加上 break语句 就没有异常, 这是因为修改了 modCount 但是不会执行到 next 方法 + /** + * 错误 + *

+ * 反编译后的代码, forEach 实际上是语法糖, next 方法必然抛出异常 + * + *

+     * Iterator var1 = this.list.iterator();
+     * while(var1.hasNext()) {
+     *   String x = (String)var1.next();
+     *   if (x.equals("del")) {
+     *     this.list.remove(x);
+     *   }
+     * }
+     * 
+ */ + @Test(expected = ConcurrentModificationException.class) + public void testForEach() { + for (String x : list) { + if (x.equals("del")) { + // 关键在于这里, 这个remove是使用迭代器遍历list, 找到目标后再复制原数组将对应的对象移除 + // 其中 next() 方法调用了 checkForComodification 方法 + // 由于 remove的调用增加了 modCount 所以在移除了一次后就抛出异常了 + list.remove(x); + log.info("remove one"); + // 如果这里加上 break语句 就没有异常, 这是因为修改了 modCount 但是不会执行到 next 方法 // break; - } + } + } } - } - - /** - * 正确 - * - * 但是显然的性能低下, 可读性差 - */ - @Test - public void testNormalCorrect() { - while (true) { - int i; - for (i = 0; i < list.size(); i++) { - if (list.get(i).equals("del")) { - list.remove(i); - break; + + /** + * 正确 + *

+ * 但是显然的性能低下, 可读性差 + */ + @Test + public void testNormalCorrect() { + while (true) { + int i; + for (i = 0; i < list.size(); i++) { + if (list.get(i).equals("del")) { + list.remove(i); + break; + } + } + + // 没有需要移除的元素 + if (i == list.size()) { + break; + } } - } - // 没有需要移除的元素 - if (i == list.size()) { - break; - } + assertThat(list.size(), equalTo(4)); } - assertThat(list.size(), equalTo(4)); - } - - /** - * 正确 - */ - @Test - public void testIterator() { - Iterator it = list.iterator(); - while (it.hasNext()) { - String x = it.next(); - if (x.equals("del")) { - // remove中有 expectedModCount = modCount; 保证了两个值的一致性 避免了 ConcurrentModificationException - it.remove(); - } + /** + * 正确 + */ + @Test + public void testIterator() { + Iterator it = list.iterator(); + while (it.hasNext()) { + String x = it.next(); + if (x.equals("del")) { + // remove中有 expectedModCount = modCount; 保证了两个值的一致性 避免了 ConcurrentModificationException + it.remove(); + } + } + assertThat(list.size(), equalTo(4)); + + // 以上代码块在Java8中可以简写为 list.removeIf(x -> x.equals("del")); } - assertThat(list.size(), equalTo(4)); - // 以上代码块在Java8中可以简写为 list.removeIf(x -> x.equals("del")); - } + @Before + public void before() { + list.add("del"); + for (int i = 0; i < 3; i++) { + list.add(String.valueOf(i)); + list.add("del"); + } - @Before - public void before() { - list.add("del"); - for (int i = 0; i < 3; i++) { - list.add(i + ""); - list.add("del"); + list.add("del"); + list.add("test"); + list.add("del"); } - list.add("del"); - list.add("test"); - list.add("del"); - } - - @After - public void after() { - log.info("result: "); - list.forEach(System.out::println); - } + @After + public void after() { + log.info("result: "); + list.forEach(System.out::println); + } } diff --git a/collection/src/test/java/com/github/kuangcp/list/ListWithArrayTest.java b/collection/src/test/java/com/github/kuangcp/list/ListWithArrayTest.java index a046521f..62d48e4a 100644 --- a/collection/src/test/java/com/github/kuangcp/list/ListWithArrayTest.java +++ b/collection/src/test/java/com/github/kuangcp/list/ListWithArrayTest.java @@ -6,6 +6,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; + import lombok.extern.slf4j.Slf4j; import org.junit.Test; @@ -15,52 +16,52 @@ @Slf4j public class ListWithArrayTest { - /** - * 测试toArray方法,得到的结论就是 无参的 toArray 是复制一份返回一个等大的数组回来 - * - * toArray(T[]) 是将传入的数组作为容器,将调用方这个数组里的数据复制一份到这个数组里去 - * 若传入的数组空间小了就采取无参的做法新建一个 - * 若空间大了 那么会将结果数组最后一个元素之后的元素设置为 null。 - */ - @Test - public void testToArray() { - ArrayList list = new ArrayList<>(); - for (int i = 0; i < 10; i++) { - list.add(i); - } - log.debug("{}", list); - Object[] copy1 = list.toArray(); - log.debug("toArray {}", Arrays.toString(copy1)); + /** + * 测试toArray方法,得到的结论就是 无参的 toArray 是复制一份返回一个等大的数组回来 + *

+ * toArray(T[]) 是将传入的数组作为容器,将调用方这个数组里的数据复制一份到这个数组里去 + * 若传入的数组空间小了就采取无参的做法新建一个 + * 若空间大了 那么会将结果数组最后一个元素之后的元素设置为 null。 + */ + @Test + public void testToArray() { + ArrayList list = new ArrayList<>(); + for (int i = 0; i < 10; i++) { + list.add(i); + } + log.debug("{}", list); + Object[] copy1 = list.toArray(); + log.debug("toArray {}", Arrays.toString(copy1)); - Integer[] target = new Integer[10]; - Object[] copy2 = list.toArray(target); - log.debug("toArray {}", Arrays.toString(copy2)); + Integer[] target = new Integer[10]; + Object[] copy2 = list.toArray(target); + log.debug("toArray {}", Arrays.toString(copy2)); - Integer[] target2 = new Integer[5]; - Object[] copy3 = list.toArray(target2); - log.debug("small than toArray \n{}", Arrays.toString(copy3)); + Integer[] target2 = new Integer[5]; + Object[] copy3 = list.toArray(target2); + log.debug("small than toArray \n{}", Arrays.toString(copy3)); - Integer[] target3 = new Integer[14]; - Object[] copy4 = list.toArray(target3); - log.debug("bigger than toArray \n{}", Arrays.toString(copy4)); - } + Integer[] target3 = new Integer[14]; + Object[] copy4 = list.toArray(target3); + log.debug("bigger than toArray \n{}", Arrays.toString(copy4)); + } - /** - * asList 方法的参数是变长参数,也就是 T[] - */ - @Test - public void testToList() { - // 由于基本类型不能用于泛型, 所以不能按设想的那样将data转为list, T 指代了 int[] (原始类型数组是一个对象) - // 不符合预期 - int[] data = {}; - List list = Arrays.asList(data); - assertThat(list.get(0) instanceof int[], equalTo(true)); + /** + * asList 方法的参数是变长参数,也就是 T[] + */ + @Test + public void testToList() { + // 由于基本类型不能用于泛型, 所以不能按设想的那样将data转为list, T 指代了 int[] (原始类型数组是一个对象) + // 不符合预期 + int[] data = {}; + List list = Arrays.asList(data); + assertThat(list.get(0) instanceof int[], equalTo(true)); - // T 指代了 Integer 符合预期 - Integer[] temp = new Integer[1]; - temp[0] = 3; - List temps = Arrays.asList(temp); - assertThat(temps.get(0) instanceof Integer,equalTo(true)); - } + // T 指代了 Integer 符合预期 + Integer[] temp = new Integer[1]; + temp[0] = 3; + List temps = Arrays.asList(temp); + assertThat(temps.get(0) instanceof Integer, equalTo(true)); + } } diff --git a/collection/src/test/java/com/github/kuangcp/list/OperationTest.java b/collection/src/test/java/com/github/kuangcp/list/OperationTest.java index 8039ddbb..bdf35309 100644 --- a/collection/src/test/java/com/github/kuangcp/list/OperationTest.java +++ b/collection/src/test/java/com/github/kuangcp/list/OperationTest.java @@ -4,8 +4,10 @@ import static org.junit.Assert.assertThat; import com.github.kuangcp.time.GetRunTime; + import java.util.ArrayList; import java.util.List; + import lombok.extern.slf4j.Slf4j; import org.junit.Before; import org.junit.Ignore; @@ -20,76 +22,76 @@ @Slf4j public class OperationTest { - private List one = new ArrayList<>(); - private List two = new ArrayList<>(); + private List one = new ArrayList<>(); + private List two = new ArrayList<>(); - private int num = 100; - private int start = 40; + private int num = 100; + private int start = 40; - @Before - public void initTwoList() { - for (int i = 0; i < num; i++) { - one.add("" + i); - two.add("" + (i + start)); + @Before + public void initTwoList() { + for (int i = 0; i < num; i++) { + one.add("" + i); + two.add("" + (i + start)); + } } - } - // 并集 A + B - A 交 B - @Test - public void testUnion() { - GetRunTime getRunTime = new GetRunTime().startCount(); + // 并集 A + B - A 交 B + @Test + public void testUnion() { + GetRunTime getRunTime = new GetRunTime().startCount(); - one.removeAll(two); - assertThat(one.size(), equalTo(start)); + one.removeAll(two); + assertThat(one.size(), equalTo(start)); - one.addAll(two); - assertThat(one.size(), equalTo(num + start)); + one.addAll(two); + assertThat(one.size(), equalTo(num + start)); - getRunTime.endCount("union "); - } + getRunTime.endCount("union "); + } - // 交集 - @Test - public void testInter() { - GetRunTime getRunTime = new GetRunTime().startCount(); + // 交集 + @Test + public void testInter() { + GetRunTime getRunTime = new GetRunTime().startCount(); - one.retainAll(two); - assertThat(one.size(), equalTo(num - start)); + one.retainAll(two); + assertThat(one.size(), equalTo(num - start)); - getRunTime.endCount("inter "); - } + getRunTime.endCount("inter "); + } - // 差集 (A 并 B) - (A交B) - @Test - public void testDifference() { - GetRunTime getRunTime = new GetRunTime().startCount(); + // 差集 (A 并 B) - (A交B) + @Test + public void testDifference() { + GetRunTime getRunTime = new GetRunTime().startCount(); - one.retainAll(two); - assertThat(one.size(), equalTo(num - start)); + one.retainAll(two); + assertThat(one.size(), equalTo(num - start)); - one.addAll(two); - assertThat(two.size(), equalTo(num)); + one.addAll(two); + assertThat(two.size(), equalTo(num)); - getRunTime.endCount("Difference"); - } + getRunTime.endCount("Difference"); + } - // 补集 A 是 S 的子集, S - A 就是A的补集 - @Test - public void testComplement() { - GetRunTime getRunTime = new GetRunTime().startCount(); + // 补集 A 是 S 的子集, S - A 就是A的补集 + @Test + public void testComplement() { + GetRunTime getRunTime = new GetRunTime().startCount(); - ArrayList three = new ArrayList<>(one); - three.removeAll(two); - assertThat(three.size(), equalTo(start)); - one.removeAll(three); - assertThat(one.size(), equalTo(num - start)); + ArrayList three = new ArrayList<>(one); + three.removeAll(two); + assertThat(three.size(), equalTo(start)); + one.removeAll(three); + assertThat(one.size(), equalTo(num - start)); - // 这里 one 是 two 的 子集 - two.removeAll(one); - // two 是上面的 one 在 原来 two 的补集 - assertThat(two.size(), equalTo(start)); + // 这里 one 是 two 的 子集 + two.removeAll(one); + // two 是上面的 one 在 原来 two 的补集 + assertThat(two.size(), equalTo(start)); - getRunTime.endCount("Complement"); - } + getRunTime.endCount("Complement"); + } } diff --git a/collection/src/test/java/com/github/kuangcp/map/ConcurrentModificationTest.java b/collection/src/test/java/com/github/kuangcp/map/ConcurrentModificationTest.java index ad716cfb..33d8e2d8 100644 --- a/collection/src/test/java/com/github/kuangcp/map/ConcurrentModificationTest.java +++ b/collection/src/test/java/com/github/kuangcp/map/ConcurrentModificationTest.java @@ -3,6 +3,7 @@ import java.util.HashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; + import lombok.extern.slf4j.Slf4j; import org.junit.Test; @@ -14,95 +15,94 @@ @Slf4j public class ConcurrentModificationTest { - /** - * 异常时而发生时而不发生, 因为不像List, Map是无序的, HashMap中的实现是在需要做修改操作前缓存 modCount, 修改后再比对缓存值是否 - * TODO ? - */ + /** + * 异常时而发生时而不发生, 因为不像List, Map是无序的, HashMap中的实现是在需要做修改操作前缓存 modCount, 修改后再比对缓存值是否 + */ // @Test(expected = ConcurrentModificationException.class) - @Test - public void testHashMap() { - Map map = new HashMap<>(); - -// log.info("foreach"); -// testReadAndModifyMapByForEach(map); - - log.info("lambda"); - readAndModifyMapByLambda(map); - } - - // TODO 使用了ConcurrentHash 后避免了异常, 但是每次迭代的, 是最新的数据么 - @Test - public void testConcurrentHashMap() { - Map map = new ConcurrentHashMap<>(); - - log.info("foreach"); - testReadAndModifyMapByForEach(map); - - log.info("lambda"); - readAndModifyMapByLambda(map); - } - - /** - * 一条线程增加 主线程 forEach 读取 - */ - private void testReadAndModifyMapByForEach(Map map) { - new Thread(() -> addItemToMap(map)).start(); - - for (int i = 0; i < 100; i++) { - log.info("read total: i={} size={}", i, map.size()); - try { - Thread.sleep(100); - } catch (InterruptedException e) { - e.printStackTrace(); - } - - for (String value : map.values()) { - String temp = value.replace("1", "ddd"); - } + @Test + public void testHashMap() { + Map map = new HashMap<>(); + + log.info("foreach"); + testReadAndModifyMapByForEach(map); + + log.info("lambda"); + readAndModifyMapByLambda(map); } - } - - /** - * 一条线程增加 主线程通过 Lambda 表达式读取 - */ - private void readAndModifyMapByLambda(Map map) { - new Thread(() -> addItemToMap(map)).start(); - new Thread(() -> removeItemFromMap(map)).start(); - - for (int i = 0; i < 300; i++) { - log.info("read total: i={} size={}", i, map.size()); - try { - Thread.sleep(30); - } catch (InterruptedException e) { - e.printStackTrace(); - } - map.values().forEach(value -> { - String temp = value.replace("1", "ddd"); - }); + + // TODO 使用了ConcurrentHash 后避免了异常, 但是每次迭代的, 是最新的数据么 + @Test + public void testConcurrentHashMap() { + Map map = new ConcurrentHashMap<>(); + + log.info("foreach"); + testReadAndModifyMapByForEach(map); + + log.info("lambda"); + readAndModifyMapByLambda(map); } - } - - private void addItemToMap(Map map) { - for (int i = 0; i < 300; i++) { - log.info("addItemToMap: i={}", i); - try { - Thread.sleep(30); - } catch (InterruptedException e) { - e.printStackTrace(); - } - map.put("t " + i, "1"); + + /** + * 一条线程增加 主线程 forEach 读取 + */ + private void testReadAndModifyMapByForEach(Map map) { + new Thread(() -> addItemToMap(map)).start(); + + for (int i = 0; i < 100; i++) { + log.info("read total: i={} size={}", i, map.size()); + try { + Thread.sleep(100); + } catch (InterruptedException e) { + log.error("", e); + } + + for (String value : map.values()) { + String temp = value.replace("1", "ddd"); + } + } + } + + /** + * 一条线程增加 主线程通过 Lambda 表达式读取 + */ + private void readAndModifyMapByLambda(Map map) { + new Thread(() -> addItemToMap(map)).start(); + new Thread(() -> removeItemFromMap(map)).start(); + + for (int i = 0; i < 300; i++) { + log.info("read total: i={} size={}", i, map.size()); + try { + Thread.sleep(30); + } catch (InterruptedException e) { + log.error("", e); + } + map.values().forEach(value -> { + String temp = value.replace("1", "ddd"); + }); + } } - } - - private void removeItemFromMap(Map map) { - for (int i = 0; i < 300; i++) { - log.info("addItemToMap: i={}", i); - try { - Thread.sleep(30); - } catch (InterruptedException e) { - e.printStackTrace(); - } - map.remove("t " + i); + + private void addItemToMap(Map map) { + for (int i = 0; i < 300; i++) { + log.info("addItemToMap: i={}", i); + try { + Thread.sleep(30); + } catch (InterruptedException e) { + log.error("", e); + } + map.put("t " + i, "1"); + } + } + + private void removeItemFromMap(Map map) { + for (int i = 0; i < 300; i++) { + log.info("addItemToMap: i={}", i); + try { + Thread.sleep(30); + } catch (InterruptedException e) { + log.error("", e); + } + map.remove("t " + i); + } } - } } diff --git a/collection/src/test/java/com/github/kuangcp/map/HashMapTest.java b/collection/src/test/java/com/github/kuangcp/map/HashMapTest.java index 9f8cc3b6..83021da8 100644 --- a/collection/src/test/java/com/github/kuangcp/map/HashMapTest.java +++ b/collection/src/test/java/com/github/kuangcp/map/HashMapTest.java @@ -7,6 +7,7 @@ import java.util.HashSet; import java.util.Map; import java.util.Objects; + import lombok.AllArgsConstructor; import lombok.Builder; import lombok.NoArgsConstructor; @@ -19,80 +20,80 @@ @Slf4j public class HashMapTest { - @Builder - @NoArgsConstructor - @AllArgsConstructor - static class Banana { + @Builder + @NoArgsConstructor + @AllArgsConstructor + static class Banana { - private String tag; + private String tag; - @Override - public int hashCode() { - return 1; - } + @Override + public int hashCode() { + return 1; + } - @Override - public String toString() { - return tag; + @Override + public String toString() { + return tag; + } } - } - - /** - * 由于hashCode都是1, 就形成了 table[1] 这个单链表, 但是不影响正常使用 - */ - @Test - public void testSameHashCode() { - Map map = new HashMap<>(); - for (int i = 0; i < 10; i++) { - map.put(new Banana(), 1); + + /** + * 由于hashCode都是1, 就形成了 table[1] 这个单链表, 但是不影响正常使用 + */ + @Test + public void testSameHashCode() { + Map map = new HashMap<>(); + for (int i = 0; i < 10; i++) { + map.put(new Banana(), 1); + } + + Banana banana = Banana.builder().tag("banana").build(); + map.put(banana, 12); + map.remove(new Banana()); + + System.out.println("size=" + map.size()); + map.keySet().forEach(System.out::println); } - Banana banana = Banana.builder().tag("banana").build(); - map.put(banana, 12); - map.remove(new Banana()); - - System.out.println("size=" + map.size()); - map.keySet().forEach(System.out::println); - } - - /** - * 注意 keySet 返回的是 Map 中的存储结构, 如果改变 keySet, 就会影响到原有的Map - */ - @Test - public void testKeySet() { - Map dict1 = new HashMap<>(); - Map dict2 = new HashMap<>(); - - dict1.put("1", "5"); - dict1.put("2", "5"); - dict1.put("3", "5"); - - dict2.put("4", "5"); - dict2.put("3", "5"); - dict2.put("2", "5"); - - log.info("new set"); - - HashSet common = new HashSet<>(dict1.keySet()); - common.retainAll(dict2.keySet()); - assertThat(common.size(), equalTo(2)); - assertThat(dict1.size(), equalTo(3)); - assertThat(dict2.size(), equalTo(3)); - showMap(dict1); - - log.info("origin set"); - dict1.keySet().retainAll(dict2.keySet()); - assertThat(dict1.size(), equalTo(2)); - assertThat(dict2.size(), equalTo(3)); - - showMap(dict1); - } - - private void showMap(Map map) { - if (Objects.isNull(map)) { - return; + /** + * 注意 keySet 返回的是 Map 中的存储结构, 如果改变 keySet, 就会影响到原有的Map + */ + @Test + public void testKeySet() { + Map dict1 = new HashMap<>(); + Map dict2 = new HashMap<>(); + + dict1.put("1", "5"); + dict1.put("2", "5"); + dict1.put("3", "5"); + + dict2.put("4", "5"); + dict2.put("3", "5"); + dict2.put("2", "5"); + + log.info("new set"); + + HashSet common = new HashSet<>(dict1.keySet()); + common.retainAll(dict2.keySet()); + assertThat(common.size(), equalTo(2)); + assertThat(dict1.size(), equalTo(3)); + assertThat(dict2.size(), equalTo(3)); + showMap(dict1); + + log.info("origin set"); + dict1.keySet().retainAll(dict2.keySet()); + assertThat(dict1.size(), equalTo(2)); + assertThat(dict2.size(), equalTo(3)); + + showMap(dict1); } - map.forEach((k, v) -> log.info("k={} v={}", k, v)); - } + private void showMap(Map map) { + if (Objects.isNull(map)) { + return; + } + + map.forEach((k, v) -> log.info("k={} v={}", k, v)); + } } diff --git a/collection/src/test/java/com/github/kuangcp/map/IterateMapTest.java b/collection/src/test/java/com/github/kuangcp/map/IterateMapTest.java index 67ec3e84..caa8be1c 100644 --- a/collection/src/test/java/com/github/kuangcp/map/IterateMapTest.java +++ b/collection/src/test/java/com/github/kuangcp/map/IterateMapTest.java @@ -1,8 +1,10 @@ package com.github.kuangcp.map; import com.github.kuangcp.mock.MockMap; + import java.util.Iterator; import java.util.Map; + import lombok.extern.slf4j.Slf4j; import org.junit.Test; @@ -13,38 +15,38 @@ @Slf4j public class IterateMapTest { - private static Map map = MockMap.mock(6, String.class, String.class); + private static Map map = MockMap.mock(6, String.class, String.class); - /** - * 通过Map.keySet()遍历key和value,二次取值 - */ - @Test - public void loopByKeySet() { - for (String key : map.keySet()) { - log.debug("{}:{}", key, map.get(key)); + /** + * 通过Map.keySet()遍历key和value,二次取值 + */ + @Test + public void loopByKeySet() { + for (String key : map.keySet()) { + log.debug("{}:{}", key, map.get(key)); + } } - } - - /** - * 通过Map.entrySet()遍历key和value(推荐使用) - */ - @Test - public void loopByEntrySet() { - for (Map.Entry entry : map.entrySet()) { - log.debug("{}:{}", entry.getKey(), entry.getValue()); + + /** + * 通过Map.entrySet()遍历key和value(推荐使用) + */ + @Test + public void loopByEntrySet() { + for (Map.Entry entry : map.entrySet()) { + log.debug("{}:{}", entry.getKey(), entry.getValue()); + } } - } - - /** - * 通过Map.entrySet()使用iterator()遍历key和value - * idea 都会提出警告, 说明了这种方式不是很好的遍历方式 - */ - @Test - public void loopByIterator() { - Iterator> iterator = map.entrySet().iterator(); - while (iterator.hasNext()) { - Map.Entry entry = iterator.next(); - log.debug("{}:{}", entry.getKey(), entry.getValue()); + + /** + * 通过Map.entrySet()使用iterator()遍历key和value + * idea 都会提出警告, 说明了这种方式不是很好的遍历方式 + */ + @Test + public void loopByIterator() { + Iterator> iterator = map.entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry entry = iterator.next(); + log.debug("{}:{}", entry.getKey(), entry.getValue()); + } } - } } diff --git a/collection/src/test/java/com/github/kuangcp/map/RandomGetValueTest.java b/collection/src/test/java/com/github/kuangcp/map/RandomGetValueTest.java index fb2d14e9..681f0365 100644 --- a/collection/src/test/java/com/github/kuangcp/map/RandomGetValueTest.java +++ b/collection/src/test/java/com/github/kuangcp/map/RandomGetValueTest.java @@ -4,8 +4,10 @@ import static org.hamcrest.MatcherAssert.assertThat; import com.github.kuangcp.mock.MockMap; + import java.util.Map; import java.util.concurrent.ThreadLocalRandom; + import lombok.extern.slf4j.Slf4j; import org.junit.Test; @@ -16,16 +18,16 @@ @Slf4j public class RandomGetValueTest { - private Map data = MockMap.mock(5, 100, 10); + private Map data = MockMap.mock(5, 100, 10); - @Test - public void testRandomValue() { - Integer[] keys = new Integer[data.size()]; - data.keySet().toArray(keys); - int index = ThreadLocalRandom.current().nextInt(keys.length); - Integer result = data.get(keys[index]); - log.debug("random: result={}", result); + @Test + public void testRandomValue() { + Integer[] keys = new Integer[data.size()]; + data.keySet().toArray(keys); + int index = ThreadLocalRandom.current().nextInt(keys.length); + Integer result = data.get(keys[index]); + log.debug("random: result={}", result); - assertThat(data.values(), hasItem(result)); - } + assertThat(data.values(), hasItem(result)); + } } diff --git a/guava/src/test/java/guava/base/AvoidNull.java b/guava/src/test/java/guava/base/AvoidNull.java deleted file mode 100644 index 04bb96bd..00000000 --- a/guava/src/test/java/guava/base/AvoidNull.java +++ /dev/null @@ -1,12 +0,0 @@ -package guava.base; - -/** - * Created by https://github.com/kuangcp - * 避免Null http://ifeve.com/google-guava-using-and-avoiding-null/ - * 这个就很像Java8里面的Optional了, 接口的设计几乎一致 - * @author kuangcp - * @date 18-3-15 下午6:03 - */ -public class AvoidNull { - -} From ee2ba1cc7640a59fd4ce946674af53a9d9b396f9 Mon Sep 17 00:00:00 2001 From: kuangcp Date: Thu, 31 Aug 2023 17:14:25 +0800 Subject: [PATCH 288/476] copy --- algorithms/pom.xml | 4 + .../kuangcp/parser/expression/Expression.java | 437 ++++++++++++++++++ .../parser/expression/ExpressionWord.java | 29 ++ .../parser/expression/ExpressionTest.java | 80 ++++ 4 files changed, 550 insertions(+) create mode 100644 algorithms/src/main/java/com/github/kuangcp/parser/expression/Expression.java create mode 100644 algorithms/src/main/java/com/github/kuangcp/parser/expression/ExpressionWord.java create mode 100644 algorithms/src/test/java/com/github/kuangcp/parser/expression/ExpressionTest.java diff --git a/algorithms/pom.xml b/algorithms/pom.xml index f511be5b..07692ac4 100644 --- a/algorithms/pom.xml +++ b/algorithms/pom.xml @@ -18,6 +18,10 @@ UTF-8 + + com.github.kuangcp + kcp-tool + com.github.jsonzou jmockdata diff --git a/algorithms/src/main/java/com/github/kuangcp/parser/expression/Expression.java b/algorithms/src/main/java/com/github/kuangcp/parser/expression/Expression.java new file mode 100644 index 00000000..f7a1f192 --- /dev/null +++ b/algorithms/src/main/java/com/github/kuangcp/parser/expression/Expression.java @@ -0,0 +1,437 @@ +package com.github.kuangcp.parser.expression; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; +import java.util.Stack; + +/** + * https://github.com/simplewz/Expression + *

+ * 四则运算表达式解析 + *

+ * 2023-06-09 18:30 + */ +public class Expression { + + private String expression; //算术表达式字符串 + private final List expressionWord; //存储表达式词法分析所得的单词与该单词所对应的种别编码的集合 + private int index; //读取词法分析器的字符游标 + private int errorCode = 0; //表达式错误编码 + private int sym; //单词种别编码 + + public Expression(String expression) { + String temp = expression.replace("{", ""); + temp = temp.replace("}", ""); + + this.expression = temp; + expressionWord = wordAnalysis(this.expression); + } + + /** + * 词法分析器:用于扫描表达式并处理得到该表达式的词库 + * + * @param expressionStr 表达式字符串 + */ + public static List wordAnalysis(String expressionStr) { + String expStrNoSpace = remove(expressionStr, ' '); //去除目标表达式中的空格字符 + char[] exChar = expStrNoSpace.toCharArray(); + List expressionWord = new ArrayList<>(); + for (int i = 0; i < exChar.length; i++) { + //如果是操作符或是括号,直接识别出来 + if (exChar[i] == '+' || exChar[i] == '-' || exChar[i] == '*' || exChar[i] == '/' || exChar[i] == '(' || exChar[i] == ')') { + StringBuilder sb = new StringBuilder(); + sb.append(exChar[i]); + switch (exChar[i]) { + case '+': + expressionWord.add(new ExpressionWord(sb.toString(), 1)); + break; + case '-': + expressionWord.add(new ExpressionWord(sb.toString(), 2)); + break; + case '*': + expressionWord.add(new ExpressionWord(sb.toString(), 3)); + break; + case '/': + expressionWord.add(new ExpressionWord(sb.toString(), 4)); + break; + case '(': + expressionWord.add(new ExpressionWord(sb.toString(), 6)); + break; + case ')': + expressionWord.add(new ExpressionWord(sb.toString(), 7)); + break; + default: + break; + } + } else if (('a' <= exChar[i] && exChar[i] <= 'z') || ('A' <= exChar[i] && exChar[i] <= 'Z') || exChar[i] == '_') { + StringBuilder sb = new StringBuilder(); + while (('a' <= exChar[i] && exChar[i] <= 'z') || ('A' <= exChar[i] && exChar[i] <= 'Z') || exChar[i] == '_' || ('0' <= exChar[i] && exChar[i] <= '9')) { + if (i == exChar.length - 1) { + sb.append(exChar[i]); + break; + } else { + sb.append(exChar[i]); + i++; + } + } + if (exChar[i] == '.') { + ++i; + if (('a' <= exChar[i] && exChar[i] <= 'z') || ('A' <= exChar[i] && exChar[i] <= 'Z') || exChar[i] == '_' || ('0' <= exChar[i] && exChar[i] <= '9')) { + sb.append('.'); + while (('a' <= exChar[i] && exChar[i] <= 'z') || ('A' <= exChar[i] && exChar[i] <= 'Z') || exChar[i] == '_' || ('0' <= exChar[i] && exChar[i] <= '9')) { + if (i == exChar.length - 1) { + sb.append(exChar[i]); + break; + } else { + sb.append(exChar[i]); + i++; + } + } + } else { + throw new RuntimeException("词法错误!"); + } + } + expressionWord.add(new ExpressionWord(sb.toString(), 5)); + if (i == exChar.length - 1) { + switch (exChar[i]) { + case '+': + expressionWord.add(new ExpressionWord("+", 1)); + break; + case '-': + expressionWord.add(new ExpressionWord("-", 2)); + break; + case '*': + expressionWord.add(new ExpressionWord("*", 3)); + break; + case '/': + expressionWord.add(new ExpressionWord("/", 4)); + break; + case '(': + expressionWord.add(new ExpressionWord("(", 6)); + break; + case ')': + expressionWord.add(new ExpressionWord(")", 7)); + break; + default: + break; + } + break; + } else { + i--; + } + + } else if ('0' <= exChar[i] && exChar[i] <= '9') { + StringBuilder sb = new StringBuilder(); + while ('0' <= exChar[i] && exChar[i] <= '9') { + if (i == exChar.length - 1) { + sb.append(exChar[i]); + break; + } else { + sb.append(exChar[i]); + i++; + } + } + + if (exChar[i] == '.') { + ++i; + if (exChar[i] >= '0' && exChar[i] <= '9') { + sb.append('.'); + while (exChar[i] >= '0' && exChar[i] <= '9') { + if (i == exChar.length - 1) { + sb.append(exChar[i]); + break; + } else { + sb.append(exChar[i]); + i++; + } + } + } else { + throw new RuntimeException("词法错误!"); + } + } + expressionWord.add(new ExpressionWord(sb.toString(), 5)); + if (i == exChar.length - 1) { + switch (exChar[i]) { + case '+': + expressionWord.add(new ExpressionWord("+", 1)); + break; + case '-': + expressionWord.add(new ExpressionWord("-", 2)); + break; + case '*': + expressionWord.add(new ExpressionWord("*", 3)); + break; + case '/': + expressionWord.add(new ExpressionWord("/", 4)); + break; + case '(': + expressionWord.add(new ExpressionWord("(", 6)); + break; + case ')': + expressionWord.add(new ExpressionWord(")", 7)); + break; + default: + break; + } + break; + } else { + i--; + } + } else { + throw new RuntimeException("词法错误!"); + } + } + return expressionWord; + } + + private static String remove(String srcStr, char ch) { + StringBuilder sbBuilder = new StringBuilder(); + for (int i = 0; i < srcStr.length(); i++) { + char currentChar = srcStr.charAt(i); + if (currentChar != ch) { + sbBuilder.append(currentChar); + } + } + return sbBuilder.toString(); + } + + public boolean isRightExpression() { + errorCode = 0; + sym = 0; + index = 0; + //上述三条语句在进行表达式的合法检测之前还原表达式的初始状态:原因表达式调用一次判断之后不会还原上述三个参数 + next(); + E(); + return sym == 0 && errorCode == 0; + } + + private void next() { + if (index < expressionWord.size()) { + sym = expressionWord.get(index).code; + index++; + } else { + sym = 0; + } + } + + /** + * 算数表达式的文法规则G(E): + * E -> E+T | E-T | T + * T ->T*F | T/F | F + * F -> (E) | d + */ + private void E() { + T(); + E1(); + } + + private void E1() { + if (sym == 1) { + next(); + T(); + E1(); + } else if (sym == 2) { + next(); + T(); + E1(); + } else if (sym != 7 && sym != 0) { + errorCode = -1; + } + } + + private void T() { + F(); + T1(); + } + + private void F() { + if (sym == 5) { + next(); + } else if (sym == 6) { + next(); + E(); + if (sym == 7) { + next(); + } else { + errorCode = -1; + } + } else { + errorCode = -1; + } + } + + private void T1() { + if (sym == 3) { + next(); + F(); + T1(); + } else if (sym == 4) { + next(); + F(); + T1(); + } else if (sym != 0 && sym != 1 && sym != 2 && sym != 7) { + errorCode = -1; + } + } + + public String getExpression() { + return expression; + } + + public void setExpression(String expression) { + this.expression = expression; + } + + /** + * 只提供get方法,返回Expression表达式对应的词法分析器识别的结果 + * + * @return + */ + public List getWord() { + return expressionWord; + } + + /** + * 只提供get方法,用于返回表达式是否有误的编码 0为没有错 -1为表达式有误 + */ + public int getErrorCode() { + return errorCode; + } + + /** + * 只提供get方法,用于返回表达式词法分析器中识别出的单词的种别编码 + * + * @return + */ + public int getSym() { + return sym; + } + + @Override + public String toString() { + return "Experssion [experssion=" + expression + ", word=" + expressionWord + ", index=" + index + ", errorCode=" + + errorCode + ", sym=" + sym + "]"; + } + + /* + * 根据种别编码获取运算符的优先级 + */ + private static int piror(int sym) { + switch (sym) { + case 1: + case 2: + return 1; // + - 对应的运算级别为 1 + case 3: + case 4: + return 2; // * / 对应的运算级别为 2 + default: + return 0; // 其余的运算级别为 0 + } + } + + /* + * 根据种别编码判断是否是操作符 + */ + private static boolean isOperation(int sym) { + switch (sym) { + case 1: // + + case 2: // - + case 3: // * + case 4: // / + return true; + default: + return false; + } + } + + private static boolean isNumber(int sym) { + if (sym == 5) { //单词种别编码为5时表明该单词是一个操作数,返回true + return true; + } + return false; + } + + + /** + * 中缀表达式转后缀表达式 + * + * @return + */ + public static List getPostfix(Expression expression) { + List midfix = expression.getWord(); //获取中缀表达式的经过词法分析器分析的结果集 + List result = new ArrayList<>(); //存储后缀表达式结果的List集合 + Stack operationStack = new Stack<>(); //操作符栈 + + if (expression.isRightExpression()) {//如果是正确的表达式则将执行将中缀表达式转换为后缀表达式的操作 + for (int i = 0; i < midfix.size(); i++) { + ExpressionWord currentExpressionWord = midfix.get(i); //获取当前单词 + if (isOperation(currentExpressionWord.code)) { + //如果操作符栈不为空且操作符栈的栈顶元素是操作符并且操作符栈顶的运算级别高于当前操作符的运算级别 + while (!operationStack.isEmpty() && isOperation(operationStack.peek().code) && piror(operationStack.peek().code) >= piror(currentExpressionWord.code)) { + result.add(operationStack.peek()); //将操作符栈顶元素添加到后缀表达式List中 + operationStack.pop(); //操作符栈顶元素出栈 + } + operationStack.push(currentExpressionWord); //当前操作符入栈 + } else if (currentExpressionWord.code.equals(6)) { //如果当前单词是左括号 无条件入栈 + operationStack.push(currentExpressionWord); + } else if (currentExpressionWord.code.equals(7)) {//当前单词是右括号 将操作符栈中的左括号以前的操作符出栈 + while (!operationStack.peek().code.equals(6)) { //如果栈顶操作符不是左括号 + result.add(operationStack.peek()); //栈顶操作符加入后缀表达式List中 + operationStack.pop(); //栈顶操作符出栈 + } + operationStack.pop(); //当前操作符入栈 + } else { + result.add(currentExpressionWord); //当前单词不是操作符,即数字直接加入到后缀表达式的List中 + } + } + //中缀表达式链表元素遍历完毕,需要将栈中元素依次出栈加入到后缀表达式List中 + while (!operationStack.isEmpty()) { + result.add(operationStack.peek()); + operationStack.pop(); + } + } else {//不是正确的表达式,给出提示信息 + System.out.println("表达式有误!"); + } + return result; + } + + /** + * 计算表达式的运算结果 + */ + public static BigDecimal expCalculate(Expression expression) { + List postfix = getPostfix(expression); //获取表达式的后缀表达式 + Stack result = new Stack<>(); //操作数栈 + if (postfix != null && postfix.size() > 0) { + for (int i = 0; i < postfix.size(); i++) { + ExpressionWord currentExpressionWord = postfix.get(i); + if (isNumber(currentExpressionWord.code)) { + result.push(new BigDecimal(currentExpressionWord.word)); + } else if (isOperation(currentExpressionWord.code)) { + BigDecimal first = result.peek(); + result.pop(); + BigDecimal second = result.peek(); + result.pop(); + switch (currentExpressionWord.code) { + case 1: + result.push(second.add(first)); + break; + case 2: + result.push(second.subtract(first)); + break; + case 3: + result.push(second.multiply(first)); + break; + case 4: + result.push(second.divide(first, 4, BigDecimal.ROUND_HALF_UP)); + break; + default: + break; + } + } + } + } + return result.peek(); + } +} + diff --git a/algorithms/src/main/java/com/github/kuangcp/parser/expression/ExpressionWord.java b/algorithms/src/main/java/com/github/kuangcp/parser/expression/ExpressionWord.java new file mode 100644 index 00000000..277a5d50 --- /dev/null +++ b/algorithms/src/main/java/com/github/kuangcp/parser/expression/ExpressionWord.java @@ -0,0 +1,29 @@ +package com.github.kuangcp.parser.expression; + +/** + * 2023-06-09 18:35 + */ +public class ExpressionWord { + + String word; //单词部分 + + Integer code; //单词种别编码 + + @Override + public String toString() { + return "(" + word + "," + code + ")"; + } + + public ExpressionWord(String word, Integer code) { + this.word = word; + this.code = code; + } + + public String getWord() { + return word; + } + + public Integer getCode() { + return code; + } +} diff --git a/algorithms/src/test/java/com/github/kuangcp/parser/expression/ExpressionTest.java b/algorithms/src/test/java/com/github/kuangcp/parser/expression/ExpressionTest.java new file mode 100644 index 00000000..6225d66c --- /dev/null +++ b/algorithms/src/test/java/com/github/kuangcp/parser/expression/ExpressionTest.java @@ -0,0 +1,80 @@ +package com.github.kuangcp.parser.expression; + +import org.junit.Test; + +import java.util.*; + +/** + * 2023-08-31 17:09 + */ +public class ExpressionTest { + + @Test + public void testInvalid() throws Exception { + Expression e1 = new Expression("a_qq.b+5b+6cu+f"); + Expression e2 = new Expression("((12*(2.50-1.05" + ")" + "-10)+90)"); + Expression e3 = new Expression("3 * 5 / (" + " 12 + 11 " + ")"); + + List words1 = Expression.wordAnalysis(e1.getExpression()); + System.out.println(words1); + System.out.println(e1.isRightExpression()); + System.out.println(e1); + + List words2 = Expression.wordAnalysis(e2.getExpression()); + //e2.setWord(words2); + System.out.println(words2); + System.out.println(e2.isRightExpression()); + System.out.println(Expression.expCalculate(e2)); + System.out.println(e2); + + List words3 = Expression.wordAnalysis(e3.getExpression()); + //e3.setWord(words3); + System.out.println(words3); + System.out.println(e3); + System.out.println(e3.isRightExpression()); + System.out.println(Expression.expCalculate(e3)); + + + Expression e4 = new Expression("tc_zd_plan.xztdmj+tc_zd_plan.cltdmj"); + System.out.println(e4); + System.out.println(e4.getWord()); + //System.out.println(expCalculate(e4)); + System.out.println(e4.isRightExpression()); + System.out.println(e4); + } + + + @org.junit.Test + public void testFormula() throws Exception { + String formula = "12/14"; + + Expression expression = new Expression(formula); + List words = expression.getWord(); + + System.out.println(expression.isRightExpression()); + if (words.size() != 3) { + System.out.println("NOT"); + return; + } + + Map nameMap = new HashMap<>(); + nameMap.put("12", "SUM(`放大销售额 `)"); + nameMap.put("14", "SUM(`放大销售量 `)"); + String result = ""; + String sec = nameMap.get(words.get(2).word); + if (Objects.equals("/", words.get(1).word)) { + result = String.format("IF(%s = 0 OR %s IS NULL, NULL, %s / materialize(%s))", + sec, sec, nameMap.get(words.get(0).word), sec); + } else { + result = String.format("%s%s%s", nameMap.get(words.get(0).word), words.get(1).word, sec); + } + + Integer round = 2; + if (Objects.nonNull(round)) { + result = String.format("ROUND(%s,%d)", result, round); + } + System.out.println(result); + } + + +} From 3d664ece1f24b0c4c0c99ed371408d4182eb0220 Mon Sep 17 00:00:00 2001 From: kuangcp Date: Thu, 31 Aug 2023 22:21:24 +0800 Subject: [PATCH 289/476] fix: shit compile? --- algorithms/pom.xml | 54 +++++++++++++++ algorithms/src/main/antlr4/Expr.g4 | 25 +++++++ .../kuangcp/parser/expression/Expression.java | 68 +++++++++---------- .../github/kuangcp/antlr4/ExpressionTest.java | 25 +++++++ .../java/com/github/kuangcp/antlr4/Readme.md | 1 + pom.xml | 7 +- 6 files changed, 142 insertions(+), 38 deletions(-) create mode 100644 algorithms/src/main/antlr4/Expr.g4 create mode 100644 algorithms/src/test/java/com/github/kuangcp/antlr4/ExpressionTest.java create mode 100644 algorithms/src/test/java/com/github/kuangcp/antlr4/Readme.md diff --git a/algorithms/pom.xml b/algorithms/pom.xml index 07692ac4..8fccb3cc 100644 --- a/algorithms/pom.xml +++ b/algorithms/pom.xml @@ -16,6 +16,9 @@ UTF-8 UTF-8 + + 4.7.1 + 3.2.0 @@ -32,6 +35,57 @@ commons-codec + + org.antlr + antlr4-runtime + ${antlr.version} + + + + + + org.antlr + antlr4-maven-plugin + 4.7.1 + + + true + true + + -package + antlr4 + + + + + + antlr4 + + + + + + org.codehaus.mojo + build-helper-maven-plugin + ${mojo.version} + + + generate-sources + + add-source + + + + ${basedir}/target/generated-sources/antlr4 + + + + + + + + + diff --git a/algorithms/src/main/antlr4/Expr.g4 b/algorithms/src/main/antlr4/Expr.g4 new file mode 100644 index 00000000..d3dbebb3 --- /dev/null +++ b/algorithms/src/main/antlr4/Expr.g4 @@ -0,0 +1,25 @@ + +grammar Expr; + +prog : stat+; + +stat: expr NEWLINE # printExpr + | ID '=' expr NEWLINE # assign + | NEWLINE # blank + ; + +expr: expr op=('*'|'/') expr # MulDiv +| expr op=('+'|'-') expr # AddSub +| INT # int +| ID # id +| '(' expr ')' # parens +; + +MUL : '*' ; // assigns token name to '*' used above in grammar +DIV : '/' ; +ADD : '+' ; +SUB : '-' ; +ID : [a-zA-Z]+ ; +INT : [0-9]+ ; +NEWLINE:'\r'? '\n' ; +WS : [ \t]+ -> skip; \ No newline at end of file diff --git a/algorithms/src/main/java/com/github/kuangcp/parser/expression/Expression.java b/algorithms/src/main/java/com/github/kuangcp/parser/expression/Expression.java index f7a1f192..eaf4fb1c 100644 --- a/algorithms/src/main/java/com/github/kuangcp/parser/expression/Expression.java +++ b/algorithms/src/main/java/com/github/kuangcp/parser/expression/Expression.java @@ -6,7 +6,7 @@ import java.util.Stack; /** - * https://github.com/simplewz/Expression + * Github: simplewz/Expression *

* 四则运算表达式解析 *

@@ -66,28 +66,12 @@ public static List wordAnalysis(String expressionStr) { } } else if (('a' <= exChar[i] && exChar[i] <= 'z') || ('A' <= exChar[i] && exChar[i] <= 'Z') || exChar[i] == '_') { StringBuilder sb = new StringBuilder(); - while (('a' <= exChar[i] && exChar[i] <= 'z') || ('A' <= exChar[i] && exChar[i] <= 'Z') || exChar[i] == '_' || ('0' <= exChar[i] && exChar[i] <= '9')) { - if (i == exChar.length - 1) { - sb.append(exChar[i]); - break; - } else { - sb.append(exChar[i]); - i++; - } - } + i = getI(exChar, i, sb); if (exChar[i] == '.') { ++i; if (('a' <= exChar[i] && exChar[i] <= 'z') || ('A' <= exChar[i] && exChar[i] <= 'Z') || exChar[i] == '_' || ('0' <= exChar[i] && exChar[i] <= '9')) { sb.append('.'); - while (('a' <= exChar[i] && exChar[i] <= 'z') || ('A' <= exChar[i] && exChar[i] <= 'Z') || exChar[i] == '_' || ('0' <= exChar[i] && exChar[i] <= '9')) { - if (i == exChar.length - 1) { - sb.append(exChar[i]); - break; - } else { - sb.append(exChar[i]); - i++; - } - } + i = getI(exChar, i, sb); } else { throw new RuntimeException("词法错误!"); } @@ -123,29 +107,13 @@ public static List wordAnalysis(String expressionStr) { } else if ('0' <= exChar[i] && exChar[i] <= '9') { StringBuilder sb = new StringBuilder(); - while ('0' <= exChar[i] && exChar[i] <= '9') { - if (i == exChar.length - 1) { - sb.append(exChar[i]); - break; - } else { - sb.append(exChar[i]); - i++; - } - } + i = getA(exChar, i, sb, '0' <= exChar[i]); if (exChar[i] == '.') { ++i; if (exChar[i] >= '0' && exChar[i] <= '9') { sb.append('.'); - while (exChar[i] >= '0' && exChar[i] <= '9') { - if (i == exChar.length - 1) { - sb.append(exChar[i]); - break; - } else { - sb.append(exChar[i]); - i++; - } - } + i = getA(exChar, i, sb, exChar[i] >= '0'); } else { throw new RuntimeException("词法错误!"); } @@ -185,6 +153,32 @@ public static List wordAnalysis(String expressionStr) { return expressionWord; } + private static int getA(char[] exChar, int i, StringBuilder sb, boolean b) { + while (b && exChar[i] <= '9') { + if (i == exChar.length - 1) { + sb.append(exChar[i]); + break; + } else { + sb.append(exChar[i]); + i++; + } + } + return i; + } + + private static int getI(char[] exChar, int i, StringBuilder sb) { + while (('a' <= exChar[i] && exChar[i] <= 'z') || ('A' <= exChar[i] && exChar[i] <= 'Z') || exChar[i] == '_' || ('0' <= exChar[i] && exChar[i] <= '9')) { + if (i == exChar.length - 1) { + sb.append(exChar[i]); + break; + } else { + sb.append(exChar[i]); + i++; + } + } + return i; + } + private static String remove(String srcStr, char ch) { StringBuilder sbBuilder = new StringBuilder(); for (int i = 0; i < srcStr.length(); i++) { diff --git a/algorithms/src/test/java/com/github/kuangcp/antlr4/ExpressionTest.java b/algorithms/src/test/java/com/github/kuangcp/antlr4/ExpressionTest.java new file mode 100644 index 00000000..cc40f210 --- /dev/null +++ b/algorithms/src/test/java/com/github/kuangcp/antlr4/ExpressionTest.java @@ -0,0 +1,25 @@ +package com.github.kuangcp.antlr4; + +import org.antlr.v4.runtime.CharStreams; +import org.antlr.v4.runtime.CommonTokenStream; +import org.antlr.v4.runtime.tree.ParseTreeWalker; +import org.junit.Test; + +/** + * @author kuangchengping@sinohealth.cn + * 2023-08-31 21:23 + */ +public class ExpressionTest { + + @Test + public void testFirst() throws Exception { + String str = "a/b"; + + antlr4.ExprLexer serverLogLexer = new antlr4.ExprLexer(CharStreams.fromString(str)); + CommonTokenStream tokens = new CommonTokenStream(serverLogLexer); + antlr4.ExprParser logParser = new antlr4.ExprParser(tokens); + ParseTreeWalker walker = new ParseTreeWalker(); + antlr4.ExprListener logWalker = new antlr4.ExprListener(); + walker.walk(logWalker, logParser.expr()); + } +} diff --git a/algorithms/src/test/java/com/github/kuangcp/antlr4/Readme.md b/algorithms/src/test/java/com/github/kuangcp/antlr4/Readme.md new file mode 100644 index 00000000..e02fdece --- /dev/null +++ b/algorithms/src/test/java/com/github/kuangcp/antlr4/Readme.md @@ -0,0 +1 @@ +https://www.baeldung.com/java-antlr \ No newline at end of file diff --git a/pom.xml b/pom.xml index 63c848a7..532a986c 100644 --- a/pom.xml +++ b/pom.xml @@ -442,10 +442,15 @@ + + org.apache.maven.plugins + maven-resources-plugin + 3.0.2 + org.apache.maven.plugins maven-compiler-plugin - 3.1 + 3.8.0 ${java.version} ${java.version} From 7fccc4edcb959bb55a9a211adf6d4cb042781093 Mon Sep 17 00:00:00 2001 From: Kcp Date: Fri, 1 Sep 2023 11:22:53 +0800 Subject: [PATCH 290/476] compile antlr4 --- algorithms/pom.xml | 8 ++++---- .../src/main/antlr4/{ => expression}/Expr.g4 | 0 .../kuangcp/antlr4/expression/CustomListener.java | 14 ++++++++++++++ .../com/github/kuangcp/antlr4/ExpressionTest.java | 10 +++++++--- 4 files changed, 25 insertions(+), 7 deletions(-) rename algorithms/src/main/antlr4/{ => expression}/Expr.g4 (100%) create mode 100644 algorithms/src/main/java/com/github/kuangcp/antlr4/expression/CustomListener.java diff --git a/algorithms/pom.xml b/algorithms/pom.xml index 8fccb3cc..68b549e6 100644 --- a/algorithms/pom.xml +++ b/algorithms/pom.xml @@ -53,10 +53,10 @@ true true - - -package - antlr4 - + + + + diff --git a/algorithms/src/main/antlr4/Expr.g4 b/algorithms/src/main/antlr4/expression/Expr.g4 similarity index 100% rename from algorithms/src/main/antlr4/Expr.g4 rename to algorithms/src/main/antlr4/expression/Expr.g4 diff --git a/algorithms/src/main/java/com/github/kuangcp/antlr4/expression/CustomListener.java b/algorithms/src/main/java/com/github/kuangcp/antlr4/expression/CustomListener.java new file mode 100644 index 00000000..9d388ae9 --- /dev/null +++ b/algorithms/src/main/java/com/github/kuangcp/antlr4/expression/CustomListener.java @@ -0,0 +1,14 @@ +package com.github.kuangcp.antlr4.expression; + +import expression.ExprBaseListener; +import expression.ExprListener; +import org.antlr.v4.runtime.ParserRuleContext; + +public class CustomListener extends ExprBaseListener { + + + @Override + public void enterEveryRule(ParserRuleContext ctx) { + super.enterEveryRule(ctx); + } +} diff --git a/algorithms/src/test/java/com/github/kuangcp/antlr4/ExpressionTest.java b/algorithms/src/test/java/com/github/kuangcp/antlr4/ExpressionTest.java index cc40f210..ec4e9477 100644 --- a/algorithms/src/test/java/com/github/kuangcp/antlr4/ExpressionTest.java +++ b/algorithms/src/test/java/com/github/kuangcp/antlr4/ExpressionTest.java @@ -1,5 +1,9 @@ package com.github.kuangcp.antlr4; +import com.github.kuangcp.antlr4.expression.CustomListener; +import expression.ExprLexer; +import expression.ExprListener; +import expression.ExprParser; import org.antlr.v4.runtime.CharStreams; import org.antlr.v4.runtime.CommonTokenStream; import org.antlr.v4.runtime.tree.ParseTreeWalker; @@ -15,11 +19,11 @@ public class ExpressionTest { public void testFirst() throws Exception { String str = "a/b"; - antlr4.ExprLexer serverLogLexer = new antlr4.ExprLexer(CharStreams.fromString(str)); + ExprLexer serverLogLexer = new ExprLexer(CharStreams.fromString(str)); CommonTokenStream tokens = new CommonTokenStream(serverLogLexer); - antlr4.ExprParser logParser = new antlr4.ExprParser(tokens); + ExprParser logParser = new ExprParser(tokens); ParseTreeWalker walker = new ParseTreeWalker(); - antlr4.ExprListener logWalker = new antlr4.ExprListener(); + ExprListener logWalker = new CustomListener(); walker.walk(logWalker, logParser.expr()); } } From 50e0e79fafd583222fb5a9599b463827021e15f9 Mon Sep 17 00:00:00 2001 From: kuangcp Date: Fri, 1 Sep 2023 14:46:44 +0800 Subject: [PATCH 291/476] first expr --- algorithms/pom.xml | 6 -- algorithms/src/main/antlr4/expression/Expr.g4 | 36 +++++------- .../src/main/antlr4/expression/ExprParser.g4 | 0 .../antlr4/expression/CustomListener.java | 4 +- .../antlr4/expression/EvalVisitor.java | 57 +++++++++++++++++++ .../{ => expression}/ExpressionTest.java | 25 +++++++- 6 files changed, 96 insertions(+), 32 deletions(-) create mode 100644 algorithms/src/main/antlr4/expression/ExprParser.g4 create mode 100644 algorithms/src/main/java/com/github/kuangcp/antlr4/expression/EvalVisitor.java rename algorithms/src/test/java/com/github/kuangcp/antlr4/{ => expression}/ExpressionTest.java (53%) diff --git a/algorithms/pom.xml b/algorithms/pom.xml index 68b549e6..17c2f0f0 100644 --- a/algorithms/pom.xml +++ b/algorithms/pom.xml @@ -42,7 +42,6 @@ - @@ -50,13 +49,8 @@ antlr4-maven-plugin 4.7.1 - true true - - - - diff --git a/algorithms/src/main/antlr4/expression/Expr.g4 b/algorithms/src/main/antlr4/expression/Expr.g4 index d3dbebb3..10a8004b 100644 --- a/algorithms/src/main/antlr4/expression/Expr.g4 +++ b/algorithms/src/main/antlr4/expression/Expr.g4 @@ -1,25 +1,15 @@ - grammar Expr; - -prog : stat+; - -stat: expr NEWLINE # printExpr - | ID '=' expr NEWLINE # assign - | NEWLINE # blank +prog: expr NEWLINE ; +expr: expr op=('*'|'/') expr # MulDiv + | expr op=('+'|'-') expr # AddSub + | INT # int + | '(' expr ')' # parens ; - -expr: expr op=('*'|'/') expr # MulDiv -| expr op=('+'|'-') expr # AddSub -| INT # int -| ID # id -| '(' expr ')' # parens -; - -MUL : '*' ; // assigns token name to '*' used above in grammar -DIV : '/' ; -ADD : '+' ; -SUB : '-' ; -ID : [a-zA-Z]+ ; -INT : [0-9]+ ; -NEWLINE:'\r'? '\n' ; -WS : [ \t]+ -> skip; \ No newline at end of file +NEWLINE : [\r\n]+ ; +INT : [0-9]+ ; + +/*创建常量方便引入*/ +MUL: '*'; +DIV: '/'; +Add: '+'; +SUB: '-'; diff --git a/algorithms/src/main/antlr4/expression/ExprParser.g4 b/algorithms/src/main/antlr4/expression/ExprParser.g4 new file mode 100644 index 00000000..e69de29b diff --git a/algorithms/src/main/java/com/github/kuangcp/antlr4/expression/CustomListener.java b/algorithms/src/main/java/com/github/kuangcp/antlr4/expression/CustomListener.java index 9d388ae9..071c172f 100644 --- a/algorithms/src/main/java/com/github/kuangcp/antlr4/expression/CustomListener.java +++ b/algorithms/src/main/java/com/github/kuangcp/antlr4/expression/CustomListener.java @@ -2,13 +2,15 @@ import expression.ExprBaseListener; import expression.ExprListener; +import lombok.extern.slf4j.Slf4j; import org.antlr.v4.runtime.ParserRuleContext; +@Slf4j public class CustomListener extends ExprBaseListener { - @Override public void enterEveryRule(ParserRuleContext ctx) { + log.info("ctx={}", ctx); super.enterEveryRule(ctx); } } diff --git a/algorithms/src/main/java/com/github/kuangcp/antlr4/expression/EvalVisitor.java b/algorithms/src/main/java/com/github/kuangcp/antlr4/expression/EvalVisitor.java new file mode 100644 index 00000000..f7071337 --- /dev/null +++ b/algorithms/src/main/java/com/github/kuangcp/antlr4/expression/EvalVisitor.java @@ -0,0 +1,57 @@ +package com.github.kuangcp.antlr4.expression; + +import expression.ExprBaseVisitor; +import expression.ExprParser; +import lombok.extern.slf4j.Slf4j; + +/** + * @author kuangchengping@sinohealth.cn + * 2023-09-01 13:46 + */ +@Slf4j +public class EvalVisitor extends ExprBaseVisitor { + + @Override + public Integer visitProg(ExprParser.ProgContext ctx) { + Integer value = visit(ctx.expr()); + System.out.println(value); + return value; + } + + + /* expr ('*'|'/') expr */ + @Override + public Integer visitMulDiv(ExprParser.MulDivContext ctx) { + int left = visit(ctx.expr(0)); + int right = visit(ctx.expr(1)); + if (ctx.op.getType() == ExprParser.MUL) { + return left * right; + } + return left / right; + } + + /* expr ('+'|'-') expr */ + @Override + public Integer visitAddSub(ExprParser.AddSubContext ctx) { + int left = visit(ctx.expr(0)); + int right = visit(ctx.expr(1)); + if (ctx.op.getType() == ExprParser.Add) { + return left + right; + } + return left - right; + } + + + /* INT */ + @Override + public Integer visitInt(ExprParser.IntContext ctx) { + return Integer.valueOf(ctx.INT().getText()); + } + + + /* '(' expr ')' */ + @Override + public Integer visitParens(ExprParser.ParensContext ctx) { + return visit(ctx.expr()); + } +} diff --git a/algorithms/src/test/java/com/github/kuangcp/antlr4/ExpressionTest.java b/algorithms/src/test/java/com/github/kuangcp/antlr4/expression/ExpressionTest.java similarity index 53% rename from algorithms/src/test/java/com/github/kuangcp/antlr4/ExpressionTest.java rename to algorithms/src/test/java/com/github/kuangcp/antlr4/expression/ExpressionTest.java index ec4e9477..c0059a12 100644 --- a/algorithms/src/test/java/com/github/kuangcp/antlr4/ExpressionTest.java +++ b/algorithms/src/test/java/com/github/kuangcp/antlr4/expression/ExpressionTest.java @@ -1,9 +1,9 @@ -package com.github.kuangcp.antlr4; +package com.github.kuangcp.antlr4.expression; -import com.github.kuangcp.antlr4.expression.CustomListener; import expression.ExprLexer; import expression.ExprListener; import expression.ExprParser; +import lombok.extern.slf4j.Slf4j; import org.antlr.v4.runtime.CharStreams; import org.antlr.v4.runtime.CommonTokenStream; import org.antlr.v4.runtime.tree.ParseTreeWalker; @@ -13,6 +13,7 @@ * @author kuangchengping@sinohealth.cn * 2023-08-31 21:23 */ +@Slf4j public class ExpressionTest { @Test @@ -24,6 +25,26 @@ public void testFirst() throws Exception { ExprParser logParser = new ExprParser(tokens); ParseTreeWalker walker = new ParseTreeWalker(); ExprListener logWalker = new CustomListener(); + +// logWalker.add + walker.walk(logWalker, logParser.expr()); } + + // https://juejin.cn/post/6844903701472083982 + @Test + public void testEval() throws Exception { + try { + ExprLexer lexer = new ExprLexer(CharStreams.fromString("1 + 3\n")); + CommonTokenStream tokens = new CommonTokenStream(lexer); + ExprParser parser = new ExprParser(tokens); + ExprParser.ProgContext tree = parser.prog(); + EvalVisitor eval = new EvalVisitor(); + Integer result = eval.visit(tree); + log.info("result={}", result); + } catch (Exception e) { + log.error("", e); + } + + } } From 7461bbd0a3d1f72727a2e5614236af88ed653a1b Mon Sep 17 00:00:00 2001 From: kuangcp Date: Tue, 5 Sep 2023 19:08:51 +0800 Subject: [PATCH 292/476] crpto: aes first use --- class/src/main/java/security/AES.java | 150 ----------- class/src/main/java/security/aes/AES.java | 111 ++++++++ .../main/java/security/aes/AesException.java | 58 ++++ class/src/main/java/security/aes/AesUtil.java | 147 ++++++++++ .../src/main/java/security/aes/ByteGroup.java | 26 ++ .../main/java/security/aes/PKCS7Encoder.java | 62 +++++ class/src/main/java/security/aes/SHA1.java | 56 ++++ .../main/java/security/aes/WXBizMsgCrypt.java | 251 ++++++++++++++++++ class/src/test/java/security/AESTest.java | 69 ----- class/src/test/java/security/aes/AESTest.java | 34 +++ .../java/security/aes/WXBizMsgCryptTest.java | 123 +++++++++ 11 files changed, 868 insertions(+), 219 deletions(-) delete mode 100644 class/src/main/java/security/AES.java create mode 100644 class/src/main/java/security/aes/AES.java create mode 100644 class/src/main/java/security/aes/AesException.java create mode 100644 class/src/main/java/security/aes/AesUtil.java create mode 100644 class/src/main/java/security/aes/ByteGroup.java create mode 100644 class/src/main/java/security/aes/PKCS7Encoder.java create mode 100644 class/src/main/java/security/aes/SHA1.java create mode 100644 class/src/main/java/security/aes/WXBizMsgCrypt.java delete mode 100644 class/src/test/java/security/AESTest.java create mode 100644 class/src/test/java/security/aes/AESTest.java create mode 100644 class/src/test/java/security/aes/WXBizMsgCryptTest.java diff --git a/class/src/main/java/security/AES.java b/class/src/main/java/security/AES.java deleted file mode 100644 index d9f35993..00000000 --- a/class/src/main/java/security/AES.java +++ /dev/null @@ -1,150 +0,0 @@ -package security; - -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; -import sun.misc.BASE64Decoder; - -import javax.crypto.Cipher; -import javax.crypto.KeyGenerator; -import javax.crypto.spec.SecretKeySpec; -import java.math.BigInteger; -import java.nio.charset.StandardCharsets; -import java.util.Base64; - -@Slf4j -public class AES { - - //密钥 (需要前端和后端保持一致) - private static final String KEY = "qwertyuiqwertyui"; - //算法 - private static final String ALGORITHMSTR = "AES/CTR/PKCS5Padding"; - - /** - * aes解密 - * - * @param encrypt 内容 - */ - public static String aesDecrypt(String encrypt) { - try { - return aesDecrypt(encrypt, KEY); - } catch (Exception e) { - log.error("", e); - return ""; - } - } - - /** - * aes加密 - */ - public static String aesEncrypt(String content) { - try { - return aesEncrypt(content, KEY); - } catch (Exception e) { - log.error("", e); - return ""; - } - } - - /** - * 将byte[]转为各种进制的字符串 - * - * @param bytes byte[] - * @param radix 可以转换进制的范围,从Character.MIN_RADIX到Character.MAX_RADIX,超出范围后变为10进制 - * @return 转换后的字符串 - */ - public static String binary(byte[] bytes, int radix) { - return new BigInteger(1, bytes).toString(radix);// 这里的1代表正数 - } - - /** - * base 64 encode - * - * @param bytes 待编码的byte[] - * @return 编码后的base 64 code - */ - public static String base64Encode(byte[] bytes) { - return Base64.getEncoder().encodeToString(bytes); - } - - /** - * base 64 decode - * - * @param base64Code 待解码的base 64 code - * @return 解码后的byte[] - */ - public static byte[] base64Decode(String base64Code) throws Exception { - return StringUtils.isEmpty(base64Code) ? null : new BASE64Decoder().decodeBuffer(base64Code); - } - - - /** - * AES加密 - * - * @param content 待加密的内容 - * @param encryptKey 加密密钥 - * @return 加密后的byte[] - */ - public static byte[] aesEncryptToBytes(String content, String encryptKey) throws Exception { - KeyGenerator kgen = KeyGenerator.getInstance("AES"); - kgen.init(128); - Cipher cipher = Cipher.getInstance(ALGORITHMSTR); - cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(encryptKey.getBytes(), "AES")); - - return cipher.doFinal(content.getBytes(StandardCharsets.UTF_8)); - } - - - /** - * AES加密为base 64 code - * - * @param content 待加密的内容 - * @param encryptKey 加密密钥 - * @return 加密后的base 64 code - */ - public static String aesEncrypt(String content, String encryptKey) throws Exception { - return base64Encode(aesEncryptToBytes(content, encryptKey)); - } - - /** - * AES解密 - * - * @param encryptBytes 待解密的byte[] - * @param decryptKey 解密密钥 - * @return 解密后的String - */ - public static String aesDecryptByBytes(byte[] encryptBytes, String decryptKey) throws Exception { - KeyGenerator kgen = KeyGenerator.getInstance("AES"); - kgen.init(128); - - Cipher cipher = Cipher.getInstance(ALGORITHMSTR); - cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(decryptKey.getBytes(), "AES")); - byte[] decryptBytes = cipher.doFinal(encryptBytes); - return new String(decryptBytes); - } - - - /** - * 将base 64 code AES解密 - * - * @param encryptStr 待解密的base 64 code - * @param decryptKey 解密密钥 - * @return 解密后的string - */ - public static String aesDecrypt(String encryptStr, String decryptKey) throws Exception { - return StringUtils.isEmpty(encryptStr) ? null - : aesDecryptByBytes(base64Decode(encryptStr), decryptKey); - } - - /** - * 测试 - */ - public static void main(String[] args) throws Exception { - String content = "123"; - System.out.println("加密前:" + content); - System.out.println("加密密钥和解密密钥:" + KEY); - String encrypt = aesEncrypt(content, KEY); - System.out.println("加密后:" + encrypt); - String decrypt = aesDecrypt(encrypt, KEY); - System.out.println("解密后:" + decrypt); - } -} \ No newline at end of file diff --git a/class/src/main/java/security/aes/AES.java b/class/src/main/java/security/aes/AES.java new file mode 100644 index 00000000..25ced58d --- /dev/null +++ b/class/src/main/java/security/aes/AES.java @@ -0,0 +1,111 @@ +package security.aes; + +import lombok.extern.slf4j.Slf4j; + +import javax.crypto.Cipher; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.Base64; + +@Slf4j +public class AES { + + static Charset CHARSET = StandardCharsets.UTF_8; + + static byte[] getNetworkBytesOrder(int sourceNumber) { + byte[] orderBytes = new byte[4]; + orderBytes[3] = (byte) (sourceNumber & 0xFF); + orderBytes[2] = (byte) (sourceNumber >> 8 & 0xFF); + orderBytes[1] = (byte) (sourceNumber >> 16 & 0xFF); + orderBytes[0] = (byte) (sourceNumber >> 24 & 0xFF); + return orderBytes; + } + static int recoverNetworkBytesOrder(byte[] orderBytes) { + int sourceNumber = 0; + for (int i = 0; i < 4; i++) { + sourceNumber <<= 8; + sourceNumber |= orderBytes[i] & 0xff; + } + return sourceNumber; + } + + /** + * 对明文进行加密. + * + * @param text 需要加密的明文 + * @return 加密后base64编码的字符串 + * @throws AesException aes加密失败 + */ + static String encrypt(byte[] aesKey, String text) throws AesException { + ByteGroup byteCollector = new ByteGroup(); + byte[] textBytes = text.getBytes(CHARSET); + // randomStr + networkBytesOrder + text + appid + byteCollector.addBytes(textBytes); + // ... + pad: 使用自定义的填充方式对明文进行补位填充 + byte[] padBytes = PKCS7Encoder.encode(byteCollector.size()); + byteCollector.addBytes(padBytes); + + // 获得最终的字节流, 未加密 + byte[] unencrypted = byteCollector.toBytes(); + + try { + // 设置加密模式为AES的CBC模式 + Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding"); + SecretKeySpec keySpec = new SecretKeySpec(aesKey, "AES"); + IvParameterSpec iv = new IvParameterSpec(aesKey, 0, 16); + cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv); + + // 加密 + byte[] encrypted = cipher.doFinal(unencrypted); + + // 使用BASE64对加密后的字符串进行编码 + return Base64.getEncoder().encodeToString(encrypted); + } catch (Exception e) { + log.error("", e); + throw new AesException(AesException.EncryptAESError); + } + } + + /** + * 对密文进行解密. + * + * @param text 需要解密的密文 + * @return 解密得到的明文 + * @throws AesException aes解密失败 + */ + static String decrypt(byte[] aesKey, String text) throws AesException { + byte[] original; + try { + // 设置解密模式为AES的CBC模式 + Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding"); + SecretKeySpec key_spec = new SecretKeySpec(aesKey, "AES"); + IvParameterSpec iv = new IvParameterSpec(Arrays.copyOfRange(aesKey, 0, 16)); + cipher.init(Cipher.DECRYPT_MODE, key_spec, iv); + + // 使用BASE64对密文进行解码 + byte[] encrypted = Base64.getDecoder().decode(text); + + // 解密 + original = cipher.doFinal(encrypted); + } catch (Exception e) { + log.error("", e); + throw new AesException(AesException.DecryptAESError); + } + + String xmlContent; + try { + // 去除补位字符 + byte[] bytes = PKCS7Encoder.decode(original); + xmlContent = new String(bytes, CHARSET); + } catch (Exception e) { + log.error("", e); + throw new AesException(AesException.IllegalBuffer); + } + + return xmlContent; + } + +} \ No newline at end of file diff --git a/class/src/main/java/security/aes/AesException.java b/class/src/main/java/security/aes/AesException.java new file mode 100644 index 00000000..5bf46337 --- /dev/null +++ b/class/src/main/java/security/aes/AesException.java @@ -0,0 +1,58 @@ +package security.aes; + +public class AesException extends Exception { + + public final static int OK = 0; + public final static int ValidateSignatureError = -40001; + public final static int ParseXmlError = -40002; + public final static int ComputeSignatureError = -40003; + public final static int IllegalAesKey = -40004; + public final static int ValidateAppidError = -40005; + public final static int EncryptAESError = -40006; + public final static int DecryptAESError = -40007; + public final static int IllegalBuffer = -40008; + //public final static int EncodeBase64Error = -40009; + //public final static int DecodeBase64Error = -40010; + //public final static int GenReturnXmlError = -40011; + + private final int code; + + private static String getMessage(int code) { + switch (code) { + case ValidateSignatureError: + return "签名验证错误"; + case ParseXmlError: + return "xml解析失败"; + case ComputeSignatureError: + return "sha加密生成签名失败"; + case IllegalAesKey: + return "SymmetricKey非法"; + case ValidateAppidError: + return "appid校验失败"; + case EncryptAESError: + return "aes加密失败"; + case DecryptAESError: + return "aes解密失败"; + case IllegalBuffer: + return "解密后得到的buffer非法"; +// case EncodeBase64Error: +// return "base64加密错误"; +// case DecodeBase64Error: +// return "base64解密错误"; +// case GenReturnXmlError: +// return "xml生成失败"; + default: + return null; // cannot be + } + } + + public int getCode() { + return code; + } + + AesException(int code) { + super(getMessage(code)); + this.code = code; + } + +} diff --git a/class/src/main/java/security/aes/AesUtil.java b/class/src/main/java/security/aes/AesUtil.java new file mode 100644 index 00000000..36ae51f8 --- /dev/null +++ b/class/src/main/java/security/aes/AesUtil.java @@ -0,0 +1,147 @@ +package security.aes; + +import javax.crypto.*; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.PBEKeySpec; +import javax.crypto.spec.SecretKeySpec; +import java.io.*; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.KeySpec; +import java.util.Base64; + +/** + * @author kuangchengping@sinohealth.cn + * 2023-09-05 19:02 + */ +public class AesUtil { + public static String encrypt(String algorithm, String input, SecretKey key, IvParameterSpec iv) + throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, + InvalidKeyException, BadPaddingException, IllegalBlockSizeException { + Cipher cipher = Cipher.getInstance(algorithm); + cipher.init(Cipher.ENCRYPT_MODE, key, iv); + byte[] cipherText = cipher.doFinal(input.getBytes()); + return Base64.getEncoder() + .encodeToString(cipherText); + } + + public static String decrypt(String algorithm, String cipherText, SecretKey key, IvParameterSpec iv) + throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, + InvalidKeyException, BadPaddingException, IllegalBlockSizeException { + Cipher cipher = Cipher.getInstance(algorithm); + cipher.init(Cipher.DECRYPT_MODE, key, iv); + byte[] plainText = cipher.doFinal(Base64.getDecoder() + .decode(cipherText)); + return new String(plainText); + } + + public static SecretKey generateKey(int n) throws NoSuchAlgorithmException { + KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); + keyGenerator.init(n); + SecretKey key = keyGenerator.generateKey(); + return key; + } + + public static SecretKey getKeyFromPassword(String password, String salt) + throws NoSuchAlgorithmException, InvalidKeySpecException { + SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256"); + KeySpec spec = new PBEKeySpec(password.toCharArray(), salt.getBytes(), 65536, 256); + SecretKey secret = new SecretKeySpec(factory.generateSecret(spec) + .getEncoded(), "AES"); + return secret; + } + + public static IvParameterSpec generateIv() { + byte[] iv = new byte[16]; + new SecureRandom().nextBytes(iv); + return new IvParameterSpec(iv); + } + + public static void encryptFile(String algorithm, SecretKey key, IvParameterSpec iv, + File inputFile, File outputFile) throws IOException, NoSuchPaddingException, + NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, + BadPaddingException, IllegalBlockSizeException { + Cipher cipher = Cipher.getInstance(algorithm); + cipher.init(Cipher.ENCRYPT_MODE, key, iv); + FileInputStream inputStream = new FileInputStream(inputFile); + FileOutputStream outputStream = new FileOutputStream(outputFile); + byte[] buffer = new byte[64]; + int bytesRead; + while ((bytesRead = inputStream.read(buffer)) != -1) { + byte[] output = cipher.update(buffer, 0, bytesRead); + if (output != null) { + outputStream.write(output); + } + } + byte[] outputBytes = cipher.doFinal(); + if (outputBytes != null) { + outputStream.write(outputBytes); + } + inputStream.close(); + outputStream.close(); + } + + public static void decryptFile(String algorithm, SecretKey key, IvParameterSpec iv, + File encryptedFile, File decryptedFile) throws IOException, NoSuchPaddingException, + NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, + BadPaddingException, IllegalBlockSizeException { + Cipher cipher = Cipher.getInstance(algorithm); + cipher.init(Cipher.DECRYPT_MODE, key, iv); + FileInputStream inputStream = new FileInputStream(encryptedFile); + FileOutputStream outputStream = new FileOutputStream(decryptedFile); + byte[] buffer = new byte[64]; + int bytesRead; + while ((bytesRead = inputStream.read(buffer)) != -1) { + byte[] output = cipher.update(buffer, 0, bytesRead); + if (output != null) { + outputStream.write(output); + } + } + byte[] output = cipher.doFinal(); + if (output != null) { + outputStream.write(output); + } + inputStream.close(); + outputStream.close(); + } + + public static SealedObject encryptObject(String algorithm, Serializable object, SecretKey key, + IvParameterSpec iv) throws NoSuchPaddingException, NoSuchAlgorithmException, + InvalidAlgorithmParameterException, InvalidKeyException, IOException, IllegalBlockSizeException { + Cipher cipher = Cipher.getInstance(algorithm); + cipher.init(Cipher.ENCRYPT_MODE, key, iv); + SealedObject sealedObject = new SealedObject(object, cipher); + return sealedObject; + } + + public static Serializable decryptObject(String algorithm, SealedObject sealedObject, SecretKey key, + IvParameterSpec iv) throws NoSuchPaddingException, NoSuchAlgorithmException, + InvalidAlgorithmParameterException, InvalidKeyException, ClassNotFoundException, + BadPaddingException, IllegalBlockSizeException, IOException { + Cipher cipher = Cipher.getInstance(algorithm); + cipher.init(Cipher.DECRYPT_MODE, key, iv); + Serializable unsealObject = (Serializable) sealedObject.getObject(cipher); + return unsealObject; + } + + public static String encryptPasswordBased(String plainText, SecretKey key, IvParameterSpec iv) + throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, + InvalidKeyException, BadPaddingException, IllegalBlockSizeException { + Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); + cipher.init(Cipher.ENCRYPT_MODE, key, iv); + return Base64.getEncoder() + .encodeToString(cipher.doFinal(plainText.getBytes())); + } + + public static String decryptPasswordBased(String cipherText, SecretKey key, IvParameterSpec iv) + throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, + InvalidKeyException, BadPaddingException, IllegalBlockSizeException { + Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING"); + cipher.init(Cipher.DECRYPT_MODE, key, iv); + return new String(cipher.doFinal(Base64.getDecoder() + .decode(cipherText))); + } +} diff --git a/class/src/main/java/security/aes/ByteGroup.java b/class/src/main/java/security/aes/ByteGroup.java new file mode 100644 index 00000000..f54880b2 --- /dev/null +++ b/class/src/main/java/security/aes/ByteGroup.java @@ -0,0 +1,26 @@ +package security.aes; + +import java.util.ArrayList; + +class ByteGroup { + ArrayList byteContainer = new ArrayList<>(); + + public byte[] toBytes() { + byte[] bytes = new byte[byteContainer.size()]; + for (int i = 0; i < byteContainer.size(); i++) { + bytes[i] = byteContainer.get(i); + } + return bytes; + } + + public ByteGroup addBytes(byte[] bytes) { + for (byte b : bytes) { + byteContainer.add(b); + } + return this; + } + + public int size() { + return byteContainer.size(); + } +} diff --git a/class/src/main/java/security/aes/PKCS7Encoder.java b/class/src/main/java/security/aes/PKCS7Encoder.java new file mode 100644 index 00000000..fb87b251 --- /dev/null +++ b/class/src/main/java/security/aes/PKCS7Encoder.java @@ -0,0 +1,62 @@ +package security.aes; + +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; + +/** + * 提供基于PKCS7算法的加解密接口. + */ +class PKCS7Encoder { + + static Charset CHARSET = StandardCharsets.UTF_8; + + static int BLOCK_SIZE = 32; + + /** + * 获得对明文进行补位填充的字节. + * + * @param count 需要进行填充补位操作的明文字节个数 + * @return 补齐用的字节数组 + */ + static byte[] encode(int count) { + // 计算需要填充的位数 + int amountToPad = BLOCK_SIZE - (count % BLOCK_SIZE); + if (amountToPad == 0) { + amountToPad = BLOCK_SIZE; + } + // 获得补位所用的字符 + char padChr = chr(amountToPad); + StringBuilder tmp = new StringBuilder(); + for (int index = 0; index < amountToPad; index++) { + tmp.append(padChr); + } + return tmp.toString().getBytes(CHARSET); + } + + /** + * 删除解密后明文的补位字符 + * + * @param decrypted 解密后的明文 + * @return 删除补位字符后的明文 + */ + static byte[] decode(byte[] decrypted) { + int pad = decrypted[decrypted.length - 1]; + if (pad < 1 || pad > 32) { + pad = 0; + } + return Arrays.copyOfRange(decrypted, 0, decrypted.length - pad); + } + + /** + * 将数字转化成ASCII码对应的字符,用于对明文进行补码 + * + * @param a 需要转化的数字 + * @return 转化得到的字符 + */ + static char chr(int a) { + byte target = (byte) (a & 0xFF); + return (char) target; + } + +} diff --git a/class/src/main/java/security/aes/SHA1.java b/class/src/main/java/security/aes/SHA1.java new file mode 100644 index 00000000..fa03f487 --- /dev/null +++ b/class/src/main/java/security/aes/SHA1.java @@ -0,0 +1,56 @@ +package security.aes; + +import lombok.extern.slf4j.Slf4j; + +import java.security.MessageDigest; +import java.util.Arrays; + +/** + * SHA1 class + *

+ * 计算公众平台的消息签名接口. + */ +@Slf4j +public class SHA1 { + + /** + * 用SHA1算法生成安全签名 + * + * @param token 票据 + * @param timestamp 时间戳 + * @param nonce 随机字符串 + * @param encrypt 密文 + * @return 安全签名 + * @throws AesException + */ + public static String getSHA1(String token, String timestamp, String nonce, String encrypt) throws AesException { + try { + String[] array = new String[]{token, timestamp, nonce, encrypt}; + StringBuilder sb = new StringBuilder(); + // 字符串排序 + Arrays.sort(array); + for (int i = 0; i < 4; i++) { + sb.append(array[i]); + } + String str = sb.toString(); + // SHA1签名生成 + MessageDigest md = MessageDigest.getInstance("SHA-1"); + md.update(str.getBytes()); + byte[] digest = md.digest(); + + StringBuilder hexstr = new StringBuilder(); + String shaHex = ""; + for (byte b : digest) { + shaHex = Integer.toHexString(b & 0xFF); + if (shaHex.length() < 2) { + hexstr.append(0); + } + hexstr.append(shaHex); + } + return hexstr.toString(); + } catch (Exception e) { + log.error("", e); + throw new AesException(AesException.ComputeSignatureError); + } + } +} diff --git a/class/src/main/java/security/aes/WXBizMsgCrypt.java b/class/src/main/java/security/aes/WXBizMsgCrypt.java new file mode 100644 index 00000000..b90c2024 --- /dev/null +++ b/class/src/main/java/security/aes/WXBizMsgCrypt.java @@ -0,0 +1,251 @@ +package security.aes; + +import lombok.extern.slf4j.Slf4j; + +import javax.crypto.Cipher; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.Base64; +import java.util.Random; + +/** + * 提供接收和推送给公众平台消息的加解密接口(UTF8编码的字符串). + *

    + *
  1. 第三方回复加密消息给公众平台
  2. + *
  3. 第三方收到公众平台发送的消息,验证消息的安全性,并对消息进行解密。
  4. + *
+ * 说明:异常java.security.InvalidKeyException:illegal Key Size的解决方案 + *
    + *
  1. 在官方网站下载JCE无限制权限策略文件(JDK7的下载地址: + * http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html
  2. + *
  3. 下载后解压,可以看到local_policy.jar和US_export_policy.jar以及readme.txt
  4. + *
  5. 如果安装了JRE,将两个jar文件放到%JRE_HOME%\lib\security目录下覆盖原来的文件
  6. + *
  7. 如果安装了JDK,将两个jar文件放到%JDK_HOME%\jre\lib\security目录下覆盖原来文件
  8. + *
+ */ +@Slf4j +public class WXBizMsgCrypt { + static Charset CHARSET = StandardCharsets.UTF_8; + byte[] aesKey; + String token; + String appId; + + /** + * 构造函数 + * + * @param token 公众平台上,开发者设置的token + * @param encodingAesKey 公众平台上,开发者设置的EncodingAESKey + * @param appId 公众平台appid + * @throws AesException 执行失败,请查看该异常的错误码和具体的错误信息 + */ + public WXBizMsgCrypt(String token, String encodingAesKey, String appId) throws AesException { + if (encodingAesKey.length() != 43) { + throw new AesException(AesException.IllegalAesKey); + } + + this.token = token; + this.appId = appId; + aesKey = Base64.getDecoder().decode(encodingAesKey + "="); + } + + // 生成4个字节的网络字节序 + byte[] getNetworkBytesOrder(int sourceNumber) { + byte[] orderBytes = new byte[4]; + orderBytes[3] = (byte) (sourceNumber & 0xFF); + orderBytes[2] = (byte) (sourceNumber >> 8 & 0xFF); + orderBytes[1] = (byte) (sourceNumber >> 16 & 0xFF); + orderBytes[0] = (byte) (sourceNumber >> 24 & 0xFF); + return orderBytes; + } + + // 还原4个字节的网络字节序 + int recoverNetworkBytesOrder(byte[] orderBytes) { + int sourceNumber = 0; + for (int i = 0; i < 4; i++) { + sourceNumber <<= 8; + sourceNumber |= orderBytes[i] & 0xff; + } + return sourceNumber; + } + + // 随机生成16位字符串 + String getRandomStr() { + String base = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + Random random = new Random(); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < 16; i++) { + int number = random.nextInt(base.length()); + sb.append(base.charAt(number)); + } + return sb.toString(); + } + + /** + * 对明文进行加密. + * + * @param text 需要加密的明文 + * @return 加密后base64编码的字符串 + * @throws AesException aes加密失败 + */ + String encrypt(String randomStr, String text) throws AesException { + ByteGroup byteCollector = new ByteGroup(); + byte[] randomStrBytes = randomStr.getBytes(CHARSET); + byte[] textBytes = text.getBytes(CHARSET); + byte[] networkBytesOrder = getNetworkBytesOrder(textBytes.length); + byte[] appidBytes = appId.getBytes(CHARSET); + + // randomStr + networkBytesOrder + text + appid + byteCollector.addBytes(randomStrBytes); + byteCollector.addBytes(networkBytesOrder); + byteCollector.addBytes(textBytes); + byteCollector.addBytes(appidBytes); + + // ... + pad: 使用自定义的填充方式对明文进行补位填充 + byte[] padBytes = PKCS7Encoder.encode(byteCollector.size()); + byteCollector.addBytes(padBytes); + + // 获得最终的字节流, 未加密 + byte[] unencrypted = byteCollector.toBytes(); + + try { + // 设置加密模式为AES的CBC模式 + Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding"); + SecretKeySpec keySpec = new SecretKeySpec(aesKey, "AES"); + IvParameterSpec iv = new IvParameterSpec(aesKey, 0, 16); + cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv); + + // 加密 + byte[] encrypted = cipher.doFinal(unencrypted); + + // 使用BASE64对加密后的字符串进行编码 + return Base64.getEncoder().encodeToString(encrypted); + } catch (Exception e) { + log.error("", e); + throw new AesException(AesException.EncryptAESError); + } + } + + /** + * 对密文进行解密. + * + * @param text 需要解密的密文 + * @return 解密得到的明文 + * @throws AesException aes解密失败 + */ + String decrypt(String text) throws AesException { + byte[] original; + try { + // 设置解密模式为AES的CBC模式 + Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding"); + SecretKeySpec key_spec = new SecretKeySpec(aesKey, "AES"); + IvParameterSpec iv = new IvParameterSpec(Arrays.copyOfRange(aesKey, 0, 16)); + cipher.init(Cipher.DECRYPT_MODE, key_spec, iv); + + // 使用BASE64对密文进行解码 + byte[] encrypted = Base64.getDecoder().decode(text); + + // 解密 + original = cipher.doFinal(encrypted); + } catch (Exception e) { + log.error("", e); + throw new AesException(AesException.DecryptAESError); + } + + String xmlContent, from_appid; + try { + // 去除补位字符 + byte[] bytes = PKCS7Encoder.decode(original); + + // 分离16位随机字符串,网络字节序和AppId + byte[] networkOrder = Arrays.copyOfRange(bytes, 16, 20); + + int xmlLength = recoverNetworkBytesOrder(networkOrder); + + xmlContent = new String(Arrays.copyOfRange(bytes, 20, 20 + xmlLength), CHARSET); + from_appid = new String(Arrays.copyOfRange(bytes, 20 + xmlLength, bytes.length), + CHARSET); + } catch (Exception e) { + log.error("", e); + throw new AesException(AesException.IllegalBuffer); + } + + // appid不相同的情况 + if (!from_appid.equals(appId)) { + throw new AesException(AesException.ValidateAppidError); + } + return xmlContent; + + } + + /** + * 将公众平台回复用户的消息加密打包. + *
    + *
  1. 对要发送的消息进行AES-CBC加密
  2. + *
  3. 生成安全签名
  4. + *
  5. 将消息密文和安全签名打包成xml格式
  6. + *
+ * + * @param replyMsg 公众平台待回复用户的消息,xml格式的字符串 + * @param timeStamp 时间戳,可以自己生成,也可以用URL参数的timestamp + * @param nonce 随机串,可以自己生成,也可以用URL参数的nonce + * @return 加密后的可以直接回复用户的密文,包括msg_signature, timestamp, nonce, encrypt的xml格式的字符串 + * @throws AesException 执行失败,请查看该异常的错误码和具体的错误信息 + */ + public String encryptMsg(String replyMsg) throws AesException { + //加密 + return encrypt(getRandomStr(), replyMsg); + } + + /** + * 检验消息的真实性,并且获取解密后的明文. + *
    + *
  1. 利用收到的密文生成安全签名,进行签名验证
  2. + *
  3. 若验证通过,则提取xml中的加密消息
  4. + *
  5. 对消息进行解密
  6. + *
+ * + * @param msgSignature 签名串,对应URL参数的msg_signature + * @param timeStamp 时间戳,对应URL参数的timestamp + * @param nonce 随机串,对应URL参数的nonce + * @param postData 密文,对应POST请求的数据 + * @return 解密后的原文 + * @throws AesException 执行失败,请查看该异常的错误码和具体的错误信息 + */ + public String decryptMsg(String msgSignature, String timeStamp, String nonce, String postData) + throws AesException { + + // 验证安全签名 + String signature = SHA1.getSHA1(token, timeStamp, nonce, postData); + + if (!signature.equals(msgSignature)) { + throw new AesException(AesException.ValidateSignatureError); + } + + // 解密 + return decrypt(postData); + } + + /** + * 验证URL + * + * @param msgSignature 签名串,对应URL参数的msg_signature + * @param timeStamp 时间戳,对应URL参数的timestamp + * @param nonce 随机串,对应URL参数的nonce + * @param echoStr 随机串,对应URL参数的echostr + * @return 解密之后的echostr + * @throws AesException 执行失败,请查看该异常的错误码和具体的错误信息 + */ + public String verifyUrl(String msgSignature, String timeStamp, String nonce, String echoStr) + throws AesException { + String signature = SHA1.getSHA1(token, timeStamp, nonce, ""); + + if (!signature.equals(msgSignature)) { + throw new AesException(AesException.ValidateSignatureError); + } + + return echoStr; + } +} \ No newline at end of file diff --git a/class/src/test/java/security/AESTest.java b/class/src/test/java/security/AESTest.java deleted file mode 100644 index 565f4a0f..00000000 --- a/class/src/test/java/security/AESTest.java +++ /dev/null @@ -1,69 +0,0 @@ -//package security; -// -//import static org.hamcrest.Matchers.equalTo; -//import static org.junit.Assert.assertThat; -// -//import java.nio.charset.StandardCharsets; -//import java.security.Key; -//import java.security.Provider; -//import java.security.Security; -//import javax.crypto.Cipher; -//import javax.crypto.spec.IvParameterSpec; -//import javax.crypto.spec.SecretKeySpec; -//import org.apache.commons.lang3.StringUtils; -//import org.apache.kafka.common.utils.ByteUtils; -//import org.junit.Test; -// -///** -// * @author https://github.com/kuangcp on 2020-11-01 21:35 -// */ -//public class AESTest { -// -// @Test -// public void testEncrypt() throws Exception { -// final Provider[] providers = Security.getProviders(); -// for (Provider provider : providers) { -// System.out.println(provider); -// } -// String key = "qwertyuiqwertyuo"; -// final String text = "123456"; -// String result = AES.aesEncrypt(text, key); -// System.out.println(result); -// String origin = AES.aesDecrypt(result, "qwertyuiqwertoki"); -// assertThat(origin, equalTo(text)); -// } -// -// /** -// * DES解密:CTR操作模式 * * @param cipherText:密文 * @param key:密钥 * @param iv:初始计数器 * -// * -// * @return 原文 -// */ -// public static String decryptCTR(String cipherText, String key, String counter) { -// try { -// // 获取解密密钥 -// byte[] keyBytes = getKey(key); -// Key keySpec = new SecretKeySpec(keyBytes, ALGORITHM); -// // 获取初始矢量 -// byte[] ivBytes = counter.getBytes(StandardCharsets.UTF_8); -// IvParameterSpec ivSpec = new IvParameterSpec(ivBytes); -// // 获取Cipher实例 -// Cipher cipher = Cipher.getInstance("DES/CTR/PKCS5Padding"); -// // 初始化Cipher实例,设置执行模式,解密密钥和初始计数器 -// cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec); -// // 解密 -// -// byte[] cipherTextBytes = hex2byte(cipherText); -// byte[] plainTextBytes = cipher.doFinal(cipherTextBytes); -// // 返回明文 -// return new String(plainTextBytes, StandardCharsets.UTF_8); -// } catch (Exception e) { -// System.out.println("CTR解密异常"); -// e.printStackTrace(); -// } -// return null; -// } -// -// @Test -// public void testdd() throws Exception { -// -// } \ No newline at end of file diff --git a/class/src/test/java/security/aes/AESTest.java b/class/src/test/java/security/aes/AESTest.java new file mode 100644 index 00000000..adba21ed --- /dev/null +++ b/class/src/test/java/security/aes/AESTest.java @@ -0,0 +1,34 @@ +package security.aes; + +import org.junit.Test; + +import java.nio.charset.StandardCharsets; +import java.security.Provider; +import java.security.Security; + +import static org.hamcrest.Matchers.equalTo; +import static org.junit.Assert.assertThat; + +/** + * @author https://github.com/kuangcp on 2020-11-01 21:35 + */ +public class AESTest { + + @Test + public void testEncrypt() throws Exception { +// byte[] aesKey = Base64.getDecoder().decode("abcdefghijklmnopqrstuvwxyz0123456789ABCDEFG="); + byte[] aesKey = "46EBA22EF5204DD5B110A1F730513965".getBytes(StandardCharsets.UTF_8); + + final Provider[] providers = Security.getProviders(); + for (Provider provider : providers) { + System.out.println(provider); + } + final String text = "123456"; + String target = AES.encrypt(aesKey, text); + + System.out.println("密文: " + target); + + String origin = AES.decrypt(aesKey, target); + assertThat(origin, equalTo(text)); + } +} diff --git a/class/src/test/java/security/aes/WXBizMsgCryptTest.java b/class/src/test/java/security/aes/WXBizMsgCryptTest.java new file mode 100644 index 00000000..9a2e5976 --- /dev/null +++ b/class/src/test/java/security/aes/WXBizMsgCryptTest.java @@ -0,0 +1,123 @@ +package security.aes; + +import lombok.extern.slf4j.Slf4j; +import org.junit.*; +import org.xml.sax.SAXException; + +import javax.xml.parsers.ParserConfigurationException; +import java.io.IOException; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +@Slf4j +public class WXBizMsgCryptTest { + String encodingAesKey = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFG"; + String token = "pamtest"; + String timestamp = "1409304348"; + String nonce = "xxxxxx"; + String appId = "wxb11529c136998cb6"; + String replyMsg = "我是中文abcd123"; + String xmlFormat = ""; + String afterAesEncrypt = "jn1L23DB+6ELqJ+6bruv21Y6MD7KeIfP82D6gU39rmkgczbWwt5+3bnyg5K55bgVtVzd832WzZGMhkP72vVOfg=="; + String randomStr = "aaaabbbbccccdddd"; + + String replyMsg2 = "1407743423"; + String afterAesEncrypt2 = "jn1L23DB+6ELqJ+6bruv23M2GmYfkv0xBh2h+XTBOKVKcgDFHle6gqcZ1cZrk3e1qjPQ1F4RsLWzQRG9udbKWesxlkupqcEcW7ZQweImX9+wLMa0GaUzpkycA8+IamDBxn5loLgZpnS7fVAbExOkK5DYHBmv5tptA9tklE/fTIILHR8HLXa5nQvFb3tYPKAlHF3rtTeayNf0QuM+UW/wM9enGIDIJHF7CLHiDNAYxr+r+OrJCmPQyTy8cVWlu9iSvOHPT/77bZqJucQHQ04sq7KZI27OcqpQNSto2OdHCoTccjggX5Z9Mma0nMJBU+jLKJ38YB1fBIz+vBzsYjrTmFQ44YfeEuZ+xRTQwr92vhA9OxchWVINGC50qE/6lmkwWTwGX9wtQpsJKhP+oS7rvTY8+VdzETdfakjkwQ5/Xka042OlUb1/slTwo4RscuQ+RdxSGvDahxAJ6+EAjLt9d8igHngxIbf6YyqqROxuxqIeIch3CssH/LqRs+iAcILvApYZckqmA7FNERspKA5f8GoJ9sv8xmGvZ9Yrf57cExWtnX8aCMMaBropU/1k+hKP5LVdzbWCG0hGwx/dQudYR/eXp3P0XxjlFiy+9DMlaFExWUZQDajPkdPrEeOwofJb"; + + @BeforeClass + public static void setUpBeforeClass() throws Exception { + } + + @AfterClass + public static void tearDownAfterClass() throws Exception { + } + + @Before + public void setUp() throws Exception { + + } + + @After + public void tearDown() throws Exception { + } + + @Test + public void testNormal() throws ParserConfigurationException, SAXException, IOException { + try { + WXBizMsgCrypt pc = new WXBizMsgCrypt(token, encodingAesKey, appId); + String afterEncrpt = pc.encryptMsg(replyMsg); + + //生成签名 + String signature = SHA1.getSHA1(token, timestamp, nonce, afterEncrpt); + + // 第三方收到公众号平台发送的消息 + String afterDecrpt = pc.decryptMsg(signature, timestamp, nonce, afterEncrpt); + assertEquals(replyMsg, afterDecrpt); + } catch (AesException e) { + fail("正常流程,怎么就抛出异常了??????"); + } + } + + @Test + public void testAesEncrypt() { + try { + WXBizMsgCrypt pc = new WXBizMsgCrypt(token, encodingAesKey, appId); + assertEquals(afterAesEncrypt, pc.encrypt(randomStr, replyMsg)); + } catch (AesException e) { + log.error("", e); + fail("no异常"); + } + } + + @Test + public void testAesEncrypt2() { + try { + WXBizMsgCrypt pc = new WXBizMsgCrypt(token, encodingAesKey, appId); + assertEquals(afterAesEncrypt2, pc.encrypt(randomStr, replyMsg2)); + + } catch (AesException e) { + log.error("", e); + fail("no异常"); + } + } + + @Test + public void testIllegalAesKey() { + try { + new WXBizMsgCrypt(token, "abcde", appId); + } catch (AesException e) { + assertEquals(AesException.IllegalAesKey, e.getCode()); + return; + } + fail("错误流程不抛出异常???"); + } + + @Test + public void testValidateSignatureError() throws ParserConfigurationException, SAXException, + IOException { + try { + WXBizMsgCrypt pc = new WXBizMsgCrypt(token, encodingAesKey, appId); + String afterEncrpt = pc.encryptMsg(replyMsg); + + String fromXML = String.format(xmlFormat, afterEncrpt); + pc.decryptMsg("12345", timestamp, nonce, fromXML); // 这里签名错误 + } catch (AesException e) { + assertEquals(AesException.ValidateSignatureError, e.getCode()); + return; + } + fail("错误流程不抛出异常???"); + } + + @Test + public void testVerifyUrl() throws AesException { + WXBizMsgCrypt wxcpt = new WXBizMsgCrypt("QDG6eK", + "jWmYm7qr5nMoAUwZRjGtBxmz3KA1tkAj3ykkR6q2B2C", "wx5823bf96d3bd56c7"); + String verifyMsgSig = "5c45ff5e21c57e6ad56bac8758b79b1d9ac89fd3"; + String timeStamp = "1409659589"; + String nonce = "263014780"; + String echoStr = "P9nAzCzyDtyTWESHep1vC5X9xho/qYX3Zpb4yKa9SKld1DsH3Iyt3tP3zNdtp+4RPcs8TgAE7OaBO+FZXvnaqQ=="; + wxcpt.verifyUrl(verifyMsgSig, timeStamp, nonce, echoStr); + // 只要不抛出异常就好 + } +} From bca0e3e8bc60b9e826852441f2ad16e777208963 Mon Sep 17 00:00:00 2001 From: kuangcp Date: Thu, 7 Sep 2023 22:17:25 +0800 Subject: [PATCH 293/476] fix: aes compile --- .../aes/{AesUtil.java => AESUtil.java} | 48 ++++----- class/src/main/java/security/aes/Student.java | 40 ++++++++ .../main/java/security/aes/{ => wx}/AES.java | 2 +- .../security/aes/{ => wx}/AesException.java | 2 +- .../java/security/aes/{ => wx}/ByteGroup.java | 2 +- .../security/aes/{ => wx}/PKCS7Encoder.java | 2 +- .../main/java/security/aes/{ => wx}/SHA1.java | 2 +- .../security/aes/{ => wx}/WXBizMsgCrypt.java | 2 +- .../java/security/aes/AESUtilUnitTest.java | 98 +++++++++++++++++++ .../java/security/aes/{ => wx}/AESTest.java | 4 +- .../aes/{ => wx}/WXBizMsgCryptTest.java | 5 +- .../cipher/AvailableCiphersUnitTest.java | 35 +++++++ .../java/security/des/TripleDESUnitTest.java | 91 +++++++++++++++++ .../test/java/security/rsa/RsaUnitTest.java | 93 ++++++++++++++++++ class/src/test/resources/origin.txt | 2 + network/pom.xml | 9 +- .../kuangcp/runable/TuLingRobotTest.java | 46 ++++----- pattern/pom.xml | 5 +- pom.xml | 2 +- 19 files changed, 432 insertions(+), 58 deletions(-) rename class/src/main/java/security/aes/{AesUtil.java => AESUtil.java} (85%) create mode 100644 class/src/main/java/security/aes/Student.java rename class/src/main/java/security/aes/{ => wx}/AES.java (99%) rename class/src/main/java/security/aes/{ => wx}/AesException.java (95%) rename class/src/main/java/security/aes/{ => wx}/ByteGroup.java (91%) rename class/src/main/java/security/aes/{ => wx}/PKCS7Encoder.java (94%) rename class/src/main/java/security/aes/{ => wx}/SHA1.java (95%) rename class/src/main/java/security/aes/{ => wx}/WXBizMsgCrypt.java (97%) create mode 100644 class/src/test/java/security/aes/AESUtilUnitTest.java rename class/src/test/java/security/aes/{ => wx}/AESTest.java (92%) rename class/src/test/java/security/aes/{ => wx}/WXBizMsgCryptTest.java (95%) create mode 100644 class/src/test/java/security/cipher/AvailableCiphersUnitTest.java create mode 100644 class/src/test/java/security/des/TripleDESUnitTest.java create mode 100644 class/src/test/java/security/rsa/RsaUnitTest.java create mode 100644 class/src/test/resources/origin.txt diff --git a/class/src/main/java/security/aes/AesUtil.java b/class/src/main/java/security/aes/AESUtil.java similarity index 85% rename from class/src/main/java/security/aes/AesUtil.java rename to class/src/main/java/security/aes/AESUtil.java index 36ae51f8..60349a47 100644 --- a/class/src/main/java/security/aes/AesUtil.java +++ b/class/src/main/java/security/aes/AESUtil.java @@ -1,10 +1,21 @@ package security.aes; -import javax.crypto.*; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.SecretKey; +import javax.crypto.BadPaddingException; +import javax.crypto.KeyGenerator; +import javax.crypto.SecretKeyFactory; +import javax.crypto.SealedObject; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.PBEKeySpec; import javax.crypto.spec.SecretKeySpec; -import java.io.*; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.Serializable; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; @@ -13,19 +24,15 @@ import java.security.spec.KeySpec; import java.util.Base64; -/** - * @author kuangchengping@sinohealth.cn - * 2023-09-05 19:02 - */ -public class AesUtil { +public class AESUtil { + public static String encrypt(String algorithm, String input, SecretKey key, IvParameterSpec iv) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException { Cipher cipher = Cipher.getInstance(algorithm); cipher.init(Cipher.ENCRYPT_MODE, key, iv); byte[] cipherText = cipher.doFinal(input.getBytes()); - return Base64.getEncoder() - .encodeToString(cipherText); + return Base64.getEncoder().encodeToString(cipherText); } public static String decrypt(String algorithm, String cipherText, SecretKey key, IvParameterSpec iv) @@ -33,25 +40,21 @@ public static String decrypt(String algorithm, String cipherText, SecretKey key, InvalidKeyException, BadPaddingException, IllegalBlockSizeException { Cipher cipher = Cipher.getInstance(algorithm); cipher.init(Cipher.DECRYPT_MODE, key, iv); - byte[] plainText = cipher.doFinal(Base64.getDecoder() - .decode(cipherText)); + byte[] plainText = cipher.doFinal(Base64.getDecoder().decode(cipherText)); return new String(plainText); } public static SecretKey generateKey(int n) throws NoSuchAlgorithmException { KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); keyGenerator.init(n); - SecretKey key = keyGenerator.generateKey(); - return key; + return keyGenerator.generateKey(); } public static SecretKey getKeyFromPassword(String password, String salt) throws NoSuchAlgorithmException, InvalidKeySpecException { SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256"); KeySpec spec = new PBEKeySpec(password.toCharArray(), salt.getBytes(), 65536, 256); - SecretKey secret = new SecretKeySpec(factory.generateSecret(spec) - .getEncoded(), "AES"); - return secret; + return new SecretKeySpec(factory.generateSecret(spec).getEncoded(), "AES"); } public static IvParameterSpec generateIv() { @@ -113,8 +116,7 @@ public static SealedObject encryptObject(String algorithm, Serializable object, InvalidAlgorithmParameterException, InvalidKeyException, IOException, IllegalBlockSizeException { Cipher cipher = Cipher.getInstance(algorithm); cipher.init(Cipher.ENCRYPT_MODE, key, iv); - SealedObject sealedObject = new SealedObject(object, cipher); - return sealedObject; + return new SealedObject(object, cipher); } public static Serializable decryptObject(String algorithm, SealedObject sealedObject, SecretKey key, @@ -123,8 +125,7 @@ public static Serializable decryptObject(String algorithm, SealedObject sealedOb BadPaddingException, IllegalBlockSizeException, IOException { Cipher cipher = Cipher.getInstance(algorithm); cipher.init(Cipher.DECRYPT_MODE, key, iv); - Serializable unsealObject = (Serializable) sealedObject.getObject(cipher); - return unsealObject; + return (Serializable) sealedObject.getObject(cipher); } public static String encryptPasswordBased(String plainText, SecretKey key, IvParameterSpec iv) @@ -132,8 +133,7 @@ public static String encryptPasswordBased(String plainText, SecretKey key, IvPar InvalidKeyException, BadPaddingException, IllegalBlockSizeException { Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); cipher.init(Cipher.ENCRYPT_MODE, key, iv); - return Base64.getEncoder() - .encodeToString(cipher.doFinal(plainText.getBytes())); + return Base64.getEncoder().encodeToString(cipher.doFinal(plainText.getBytes())); } public static String decryptPasswordBased(String cipherText, SecretKey key, IvParameterSpec iv) @@ -141,7 +141,7 @@ public static String decryptPasswordBased(String cipherText, SecretKey key, IvPa InvalidKeyException, BadPaddingException, IllegalBlockSizeException { Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING"); cipher.init(Cipher.DECRYPT_MODE, key, iv); - return new String(cipher.doFinal(Base64.getDecoder() - .decode(cipherText))); + return new String(cipher.doFinal(Base64.getDecoder().decode(cipherText))); } + } diff --git a/class/src/main/java/security/aes/Student.java b/class/src/main/java/security/aes/Student.java new file mode 100644 index 00000000..9b4c97cc --- /dev/null +++ b/class/src/main/java/security/aes/Student.java @@ -0,0 +1,40 @@ +package security.aes; + +import java.io.Serializable; +import java.util.Objects; + +public class Student implements Serializable { + private String name; + private int age; + + public Student(String name, int age) { + this.name = name; + this.age = age; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + Student student = (Student) o; + return age == student.age && Objects.equals(name, student.name); + } +} diff --git a/class/src/main/java/security/aes/AES.java b/class/src/main/java/security/aes/wx/AES.java similarity index 99% rename from class/src/main/java/security/aes/AES.java rename to class/src/main/java/security/aes/wx/AES.java index 25ced58d..48bb5f64 100644 --- a/class/src/main/java/security/aes/AES.java +++ b/class/src/main/java/security/aes/wx/AES.java @@ -1,4 +1,4 @@ -package security.aes; +package security.aes.wx; import lombok.extern.slf4j.Slf4j; diff --git a/class/src/main/java/security/aes/AesException.java b/class/src/main/java/security/aes/wx/AesException.java similarity index 95% rename from class/src/main/java/security/aes/AesException.java rename to class/src/main/java/security/aes/wx/AesException.java index 5bf46337..38387b82 100644 --- a/class/src/main/java/security/aes/AesException.java +++ b/class/src/main/java/security/aes/wx/AesException.java @@ -1,4 +1,4 @@ -package security.aes; +package security.aes.wx; public class AesException extends Exception { diff --git a/class/src/main/java/security/aes/ByteGroup.java b/class/src/main/java/security/aes/wx/ByteGroup.java similarity index 91% rename from class/src/main/java/security/aes/ByteGroup.java rename to class/src/main/java/security/aes/wx/ByteGroup.java index f54880b2..6a1c05b0 100644 --- a/class/src/main/java/security/aes/ByteGroup.java +++ b/class/src/main/java/security/aes/wx/ByteGroup.java @@ -1,4 +1,4 @@ -package security.aes; +package security.aes.wx; import java.util.ArrayList; diff --git a/class/src/main/java/security/aes/PKCS7Encoder.java b/class/src/main/java/security/aes/wx/PKCS7Encoder.java similarity index 94% rename from class/src/main/java/security/aes/PKCS7Encoder.java rename to class/src/main/java/security/aes/wx/PKCS7Encoder.java index fb87b251..0c5f06e3 100644 --- a/class/src/main/java/security/aes/PKCS7Encoder.java +++ b/class/src/main/java/security/aes/wx/PKCS7Encoder.java @@ -1,4 +1,4 @@ -package security.aes; +package security.aes.wx; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; diff --git a/class/src/main/java/security/aes/SHA1.java b/class/src/main/java/security/aes/wx/SHA1.java similarity index 95% rename from class/src/main/java/security/aes/SHA1.java rename to class/src/main/java/security/aes/wx/SHA1.java index fa03f487..14776ba1 100644 --- a/class/src/main/java/security/aes/SHA1.java +++ b/class/src/main/java/security/aes/wx/SHA1.java @@ -1,4 +1,4 @@ -package security.aes; +package security.aes.wx; import lombok.extern.slf4j.Slf4j; diff --git a/class/src/main/java/security/aes/WXBizMsgCrypt.java b/class/src/main/java/security/aes/wx/WXBizMsgCrypt.java similarity index 97% rename from class/src/main/java/security/aes/WXBizMsgCrypt.java rename to class/src/main/java/security/aes/wx/WXBizMsgCrypt.java index b90c2024..b76b9a15 100644 --- a/class/src/main/java/security/aes/WXBizMsgCrypt.java +++ b/class/src/main/java/security/aes/wx/WXBizMsgCrypt.java @@ -1,4 +1,4 @@ -package security.aes; +package security.aes.wx; import lombok.extern.slf4j.Slf4j; diff --git a/class/src/test/java/security/aes/AESUtilUnitTest.java b/class/src/test/java/security/aes/AESUtilUnitTest.java new file mode 100644 index 00000000..0f2e81cf --- /dev/null +++ b/class/src/test/java/security/aes/AESUtilUnitTest.java @@ -0,0 +1,98 @@ +package security.aes; + + +import org.junit.Assert; +import org.junit.Test; + +import javax.crypto.*; +import javax.crypto.spec.IvParameterSpec; +import java.io.File; +import java.io.IOException; +import java.nio.file.Paths; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.spec.InvalidKeySpecException; + +public class AESUtilUnitTest { + + + @Test + public void testStringEncrypt() + throws NoSuchAlgorithmException, IllegalBlockSizeException, InvalidKeyException, + BadPaddingException, InvalidAlgorithmParameterException, NoSuchPaddingException { + // given + String input = "baeldung"; + SecretKey key = AESUtil.generateKey(128); + IvParameterSpec ivParameterSpec = AESUtil.generateIv(); + String algorithm = "AES/CBC/PKCS5Padding"; + + // when + String cipherText = AESUtil.encrypt(algorithm, input, key, ivParameterSpec); + String plainText = AESUtil.decrypt(algorithm, cipherText, key, ivParameterSpec); + + // then + Assert.assertEquals(input, plainText); + } + + @Test + public void testGivenFile_whenEncrypt_thenSuccess() + throws NoSuchAlgorithmException, IOException, IllegalBlockSizeException, InvalidKeyException, + BadPaddingException, InvalidAlgorithmParameterException, NoSuchPaddingException { + // given + SecretKey key = AESUtil.generateKey(128); + String algorithm = "AES/CBC/PKCS5Padding"; + IvParameterSpec ivParameterSpec = AESUtil.generateIv(); + File inputFile = Paths.get("src/test/resources/origin.txt") + .toFile(); + File encryptedFile = new File("baeldung.encrypted"); + File decryptedFile = new File("document.decrypted"); + + // when + AESUtil.encryptFile(algorithm, key, ivParameterSpec, inputFile, encryptedFile); + AESUtil.decryptFile(algorithm, key, ivParameterSpec, encryptedFile, decryptedFile); + + // then +// assertThat(inputFile).hasSameTextualContentAs(decryptedFile); + encryptedFile.deleteOnExit(); + decryptedFile.deleteOnExit(); + } + + @Test + public void givenObject_whenEncrypt_thenSuccess() + throws NoSuchAlgorithmException, IllegalBlockSizeException, InvalidKeyException, + InvalidAlgorithmParameterException, NoSuchPaddingException, IOException, BadPaddingException, + ClassNotFoundException { + // given + Student student = new Student("Baeldung", 20); + SecretKey key = AESUtil.generateKey(128); + IvParameterSpec ivParameterSpec = AESUtil.generateIv(); + String algorithm = "AES/CBC/PKCS5Padding"; + + // when + SealedObject sealedObject = AESUtil.encryptObject(algorithm, student, key, ivParameterSpec); + Student object = (Student) AESUtil.decryptObject(algorithm, sealedObject, key, ivParameterSpec); + + // then + Assert.assertEquals(student, object); + } + + @Test + public void givenPassword_whenEncrypt_thenSuccess() + throws InvalidKeySpecException, NoSuchAlgorithmException, IllegalBlockSizeException, + InvalidKeyException, BadPaddingException, InvalidAlgorithmParameterException, NoSuchPaddingException { + // given + String plainText = "www.baeldung.com"; + String password = "baeldung"; + String salt = "12345678"; + IvParameterSpec ivParameterSpec = AESUtil.generateIv(); + SecretKey key = AESUtil.getKeyFromPassword(password, salt); + + // when + String cipherText = AESUtil.encryptPasswordBased(plainText, key, ivParameterSpec); + String decryptedCipherText = AESUtil.decryptPasswordBased(cipherText, key, ivParameterSpec); + + // then + Assert.assertEquals(plainText, decryptedCipherText); + } +} diff --git a/class/src/test/java/security/aes/AESTest.java b/class/src/test/java/security/aes/wx/AESTest.java similarity index 92% rename from class/src/test/java/security/aes/AESTest.java rename to class/src/test/java/security/aes/wx/AESTest.java index adba21ed..170fe9bc 100644 --- a/class/src/test/java/security/aes/AESTest.java +++ b/class/src/test/java/security/aes/wx/AESTest.java @@ -1,4 +1,4 @@ -package security.aes; +package security.aes.wx; import org.junit.Test; @@ -24,7 +24,7 @@ public void testEncrypt() throws Exception { System.out.println(provider); } final String text = "123456"; - String target = AES.encrypt(aesKey, text); + String target = AES.encrypt(aesKey, text); System.out.println("密文: " + target); diff --git a/class/src/test/java/security/aes/WXBizMsgCryptTest.java b/class/src/test/java/security/aes/wx/WXBizMsgCryptTest.java similarity index 95% rename from class/src/test/java/security/aes/WXBizMsgCryptTest.java rename to class/src/test/java/security/aes/wx/WXBizMsgCryptTest.java index 9a2e5976..75d87462 100644 --- a/class/src/test/java/security/aes/WXBizMsgCryptTest.java +++ b/class/src/test/java/security/aes/wx/WXBizMsgCryptTest.java @@ -1,8 +1,11 @@ -package security.aes; +package security.aes.wx; import lombok.extern.slf4j.Slf4j; import org.junit.*; import org.xml.sax.SAXException; +import security.aes.wx.AesException; +import security.aes.wx.SHA1; +import security.aes.wx.WXBizMsgCrypt; import javax.xml.parsers.ParserConfigurationException; import java.io.IOException; diff --git a/class/src/test/java/security/cipher/AvailableCiphersUnitTest.java b/class/src/test/java/security/cipher/AvailableCiphersUnitTest.java new file mode 100644 index 00000000..7b7750f0 --- /dev/null +++ b/class/src/test/java/security/cipher/AvailableCiphersUnitTest.java @@ -0,0 +1,35 @@ +package security.cipher; + +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.security.Provider; +import java.security.Security; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +public class AvailableCiphersUnitTest { + private final Logger logger = LoggerFactory.getLogger(AvailableCiphersUnitTest.class); + + @Test + public void whenGetServices_thenGetAllCipherAlgorithms() { + for (Provider provider : Security.getProviders()) { + for (Provider.Service service : provider.getServices()) { + logger.debug(service.getAlgorithm()); + } + } + } + + @Test + public void whenGetServicesWithFilter_thenGetAllCompatibleCipherAlgorithms() { + List algorithms = Arrays.stream(Security.getProviders()) + .flatMap(provider -> provider.getServices().stream()) + .filter(service -> "Cipher".equals(service.getType())) + .map(Provider.Service::getAlgorithm) + .collect(Collectors.toList()); + + algorithms.forEach(logger::debug); + } +} diff --git a/class/src/test/java/security/des/TripleDESUnitTest.java b/class/src/test/java/security/des/TripleDESUnitTest.java new file mode 100644 index 00000000..cc10a101 --- /dev/null +++ b/class/src/test/java/security/des/TripleDESUnitTest.java @@ -0,0 +1,91 @@ +package security.des; + + +import org.junit.Assert; +import org.junit.Test; + +import javax.crypto.Cipher; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; + +public class TripleDESUnitTest { + + @Test + public void given3DesKey_whenEncryptAndDecryptString_thenCompareResults() throws Exception { + byte[] secretKey = "9mng65v8jf4lxn93nabf981m".getBytes(); + byte[] iv = "a76nb5h9".getBytes(); + + String secretMessage = "Baeldung secret message"; + + SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey, "DESede"); + IvParameterSpec ivSpec = new IvParameterSpec(iv); + Cipher encryptCipher = Cipher.getInstance("DESede/CBC/PKCS5Padding"); + encryptCipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivSpec); + byte[] secretMessagesBytes = secretMessage.getBytes(StandardCharsets.UTF_8); + byte[] encryptedMessageBytes = encryptCipher.doFinal(secretMessagesBytes); + + Cipher decryptCipher = Cipher.getInstance("DESede/CBC/PKCS5Padding"); + decryptCipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivSpec); + byte[] decryptedMessageBytes = decryptCipher.doFinal(encryptedMessageBytes); + String decryptedMessage = new String(decryptedMessageBytes, StandardCharsets.UTF_8); + + Assert.assertEquals(secretMessage, decryptedMessage); + } + + @Test + public void given3DesKey_whenEncryptAndDecryptFile_thenCompareResults() throws Exception { + byte[] secretKey = "9mng65v8jf4lxn93nabf981m".getBytes(); + byte[] iv = "a76nb5h9".getBytes(); + + SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey, "DESede"); + IvParameterSpec ivSpec = new IvParameterSpec(iv); + + String originalContent = "some secret message"; + Path tempFile = Files.createTempFile("temp", "txt"); + writeString(tempFile, originalContent); + + byte[] fileBytes = Files.readAllBytes(tempFile); + Cipher encryptCipher = Cipher.getInstance("DESede/CBC/PKCS5Padding"); + encryptCipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivSpec); + byte[] encryptedFileBytes = encryptCipher.doFinal(fileBytes); + try (FileOutputStream stream = new FileOutputStream(tempFile.toFile())) { + stream.write(encryptedFileBytes); + } + + encryptedFileBytes = Files.readAllBytes(tempFile); + Cipher decryptCipher = Cipher.getInstance("DESede/CBC/PKCS5Padding"); + decryptCipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivSpec); + byte[] decryptedFileBytes = decryptCipher.doFinal(encryptedFileBytes); + try (FileOutputStream stream = new FileOutputStream(tempFile.toFile())) { + stream.write(decryptedFileBytes); + } + + String fileContent = readString(tempFile); + + Assert.assertEquals(originalContent, fileContent); + } + + private void writeString(Path path, String content) throws Exception { + try (BufferedWriter writer = Files.newBufferedWriter(path)) { + writer.write(content); + } + } + + private String readString(Path path) throws Exception { + StringBuilder resultStringBuilder = new StringBuilder(); + try (BufferedReader br = new BufferedReader(new FileReader(path.toFile()))) { + String line; + while ((line = br.readLine()) != null) { + resultStringBuilder.append(line); + } + } + return resultStringBuilder.toString(); + } +} diff --git a/class/src/test/java/security/rsa/RsaUnitTest.java b/class/src/test/java/security/rsa/RsaUnitTest.java new file mode 100644 index 00000000..2df01186 --- /dev/null +++ b/class/src/test/java/security/rsa/RsaUnitTest.java @@ -0,0 +1,93 @@ +package security.rsa; + + +import org.junit.Assert; +import org.junit.Test; + +import javax.crypto.Cipher; +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.PrivateKey; +import java.security.PublicKey; + +public class RsaUnitTest { + + @Test + public void givenRsaKeyPair_whenEncryptAndDecryptString_thenCompareResults() throws Exception { + KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA"); + generator.initialize(2048); + KeyPair pair = generator.generateKeyPair(); + PrivateKey privateKey = pair.getPrivate(); + PublicKey publicKey = pair.getPublic(); + + String secretMessage = "Baeldung secret message"; + Cipher encryptCipher = Cipher.getInstance("RSA"); + encryptCipher.init(Cipher.ENCRYPT_MODE, publicKey); + byte[] secretMessageBytes = secretMessage.getBytes(StandardCharsets.UTF_8); + byte[] encryptedMessageBytes = encryptCipher.doFinal(secretMessageBytes); + + Cipher decryptCipher = Cipher.getInstance("RSA"); + decryptCipher.init(Cipher.DECRYPT_MODE, privateKey); + byte[] decryptedMessageBytes = decryptCipher.doFinal(encryptedMessageBytes); + String decryptedMessage = new String(decryptedMessageBytes, StandardCharsets.UTF_8); + + Assert.assertEquals(secretMessage, decryptedMessage); + } + + @Test + public void givenRsaKeyPair_whenEncryptAndDecryptFile_thenCompareResults() throws Exception { + KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA"); + generator.initialize(2048); + KeyPair pair = generator.generateKeyPair(); + PrivateKey privateKey = pair.getPrivate(); + PublicKey publicKey = pair.getPublic(); + + String originalContent = "some secret message"; + Path tempFile = Files.createTempFile("temp", "txt"); + writeString(tempFile, originalContent); + + byte[] fileBytes = Files.readAllBytes(tempFile); + Cipher encryptCipher = Cipher.getInstance("RSA"); + encryptCipher.init(Cipher.ENCRYPT_MODE, publicKey); + byte[] encryptedFileBytes = encryptCipher.doFinal(fileBytes); + try (FileOutputStream stream = new FileOutputStream(tempFile.toFile())) { + stream.write(encryptedFileBytes); + } + + encryptedFileBytes = Files.readAllBytes(tempFile); + Cipher decryptCipher = Cipher.getInstance("RSA"); + decryptCipher.init(Cipher.DECRYPT_MODE, privateKey); + byte[] decryptedFileBytes = decryptCipher.doFinal(encryptedFileBytes); + try (FileOutputStream stream = new FileOutputStream(tempFile.toFile())) { + stream.write(decryptedFileBytes); + } + + String fileContent = readString(tempFile); + + Assert.assertEquals(originalContent, fileContent); + } + + private void writeString(Path path, String content) throws Exception { + try (BufferedWriter writer = Files.newBufferedWriter(path)) { + writer.write(content); + } + } + + private String readString(Path path) throws Exception { + StringBuilder resultStringBuilder = new StringBuilder(); + try (BufferedReader br = new BufferedReader(new FileReader(path.toFile()))) { + String line; + while ((line = br.readLine()) != null) { + resultStringBuilder.append(line); + } + } + return resultStringBuilder.toString(); + } +} diff --git a/class/src/test/resources/origin.txt b/class/src/test/resources/origin.txt new file mode 100644 index 00000000..dc6988d2 --- /dev/null +++ b/class/src/test/resources/origin.txt @@ -0,0 +1,2 @@ +Hello Baeldung +This is AES file encryption sample \ No newline at end of file diff --git a/network/pom.xml b/network/pom.xml index 6b8a87c4..ff014ed4 100644 --- a/network/pom.xml +++ b/network/pom.xml @@ -18,7 +18,14 @@ UTF-8 - + + com.github.kuangcp + kcp-tool + + + com.squareup.okhttp3 + okhttp + diff --git a/network/src/test/java/com/github/kuangcp/runable/TuLingRobotTest.java b/network/src/test/java/com/github/kuangcp/runable/TuLingRobotTest.java index 0b79c2d7..be676c93 100644 --- a/network/src/test/java/com/github/kuangcp/runable/TuLingRobotTest.java +++ b/network/src/test/java/com/github/kuangcp/runable/TuLingRobotTest.java @@ -1,9 +1,11 @@ package com.github.kuangcp.runable; import com.github.kuangcp.time.GetRunTime; + import java.io.IOException; import java.net.URLEncoder; import java.util.Optional; + import lombok.extern.slf4j.Slf4j; import okhttp3.OkHttpClient; import okhttp3.Request; @@ -12,7 +14,7 @@ /** * created by https://gitee.com/gin9 - * + *

* 调用图灵机器人平台接口 适合使用js来做 * http://www.java2s.com/Code/Jar/h/Downloadhttpclient423jar.htm * @@ -21,31 +23,31 @@ @Slf4j public class TuLingRobotTest { - private final static OkHttpClient client = new OkHttpClient(); + private final static OkHttpClient client = new OkHttpClient(); - static Optional get(String url) throws IOException { - Request request = new Request.Builder() - .url(url).build(); + static Optional get(String url) throws IOException { + Request request = new Request.Builder() + .url(url).build(); - try (Response response = client.newCall(request).execute()) { - if (response.body() != null) { - return Optional.of(response.body().string()); - } - return Optional.empty(); + try (Response response = client.newCall(request).execute()) { + if (response.body() != null) { + return Optional.of(response.body().string()); + } + return Optional.empty(); + } } - } - @Test - public void testGet() throws IOException { - GetRunTime getRunTime = new GetRunTime().startCount(); - String baseURL = "http://www.tuling123.com/openapi/api?key=c8d9f9fd7a4f46609686020354745f25&info="; - String input = "翻译 appropriate 详情"; - String INFO = URLEncoder.encode(input, "utf-8"); + @Test + public void testGet() throws IOException { + GetRunTime getRunTime = new GetRunTime().startCount(); + String baseURL = "http://www.tuling123.com/openapi/api?key=c8d9f9fd7a4f46609686020354745f25&info="; + String input = "翻译 appropriate 详情"; + String INFO = URLEncoder.encode(input, "utf-8"); - Optional result = get(baseURL + INFO); - assert result.isPresent(); + Optional result = get(baseURL + INFO); + assert result.isPresent(); - System.out.println(result.get()); - getRunTime.endCountOneLine(""); - } + System.out.println(result.get()); + getRunTime.endCountOneLine(""); + } } diff --git a/pattern/pom.xml b/pattern/pom.xml index fc43b262..c0e6ef69 100644 --- a/pattern/pom.xml +++ b/pattern/pom.xml @@ -18,7 +18,10 @@ UTF-8 - + + com.google.guava + guava + diff --git a/pom.xml b/pom.xml index 532a986c..a3e34c9c 100644 --- a/pom.xml +++ b/pom.xml @@ -179,7 +179,7 @@ com.squareup.okhttp3 okhttp - 3.13.1 + 4.11.0 io.netty From 64c3024646f5a95d6100157f0fd299915b7cccac Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Sun, 10 Sep 2023 14:54:43 +0800 Subject: [PATCH 294/476] readme --- README.md | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index fa064a70..feb08dec 100644 --- a/README.md +++ b/README.md @@ -17,12 +17,9 @@ | [泛型](/generic) | | [Hadoop](/hadoop) | | [IO](/io) | | | -[图形化](/gui) | [实际问题](/question) +[Java Gui](/gui) | [业务问题](/question) -************************ -> 使用了Gradle作为构建工具, 将知识点分为了多个子项目, 然后将所有的依赖项放在了 `dependency.gradle`中 -> 其他的子项目引用全局定义的依赖, 实现了依赖的统一管理 ************************ @@ -41,4 +38,4 @@ https://maven.restlet.talend.com maven-restlet -``` \ No newline at end of file +``` From 3df7b2d3055b822ebea1daa0223a2244d40a34d9 Mon Sep 17 00:00:00 2001 From: Kcp Date: Mon, 11 Sep 2023 09:50:31 +0800 Subject: [PATCH 295/476] expr: visitor with replace expression --- .../antlr4/expression/TokenVisitor.java | 53 ++++++++++++ .../antlr4/expression/ExpressionTest.java | 12 ++- java8/pom.xml | 5 ++ .../function/sort/BaseTypeSortTest.java | 50 ++++++------ .../kuangcp/function/sort/CustomSortTest.java | 34 ++++---- .../kuangcp/function/sort/DateSortTest.java | 81 ++++++++++--------- .../kuangcp/future/CompletableFutureTest.java | 2 +- 7 files changed, 152 insertions(+), 85 deletions(-) create mode 100644 algorithms/src/main/java/com/github/kuangcp/antlr4/expression/TokenVisitor.java diff --git a/algorithms/src/main/java/com/github/kuangcp/antlr4/expression/TokenVisitor.java b/algorithms/src/main/java/com/github/kuangcp/antlr4/expression/TokenVisitor.java new file mode 100644 index 00000000..475b33ec --- /dev/null +++ b/algorithms/src/main/java/com/github/kuangcp/antlr4/expression/TokenVisitor.java @@ -0,0 +1,53 @@ +package com.github.kuangcp.antlr4.expression; + +import expression.ExprBaseVisitor; +import expression.ExprParser; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class TokenVisitor extends ExprBaseVisitor { + + @Override + public String visitProg(ExprParser.ProgContext ctx) { + String value = visit(ctx.expr()); + System.out.println(value); + return value; + } + + + /* expr ('*'|'/') expr */ + @Override + public String visitMulDiv(ExprParser.MulDivContext ctx) { + String left = visit(ctx.expr(0)); + String right = visit(ctx.expr(1)); + if (ctx.op.getType() == ExprParser.MUL) { + return left + "*" + right; + } + return left + " / round(" + right + ", 2)"; + } + + /* expr ('+'|'-') expr */ + @Override + public String visitAddSub(ExprParser.AddSubContext ctx) { + String left = visit(ctx.expr(0)); + String right = visit(ctx.expr(1)); + if (ctx.op.getType() == ExprParser.Add) { + return left + "+" + right; + } + return left + "-" + right; + } + + + /* INT */ + @Override + public String visitInt(ExprParser.IntContext ctx) { + return ctx.INT().getText(); + } + + + /* '(' expr ')' */ + @Override + public String visitParens(ExprParser.ParensContext ctx) { + return visit(ctx.expr()); + } +} diff --git a/algorithms/src/test/java/com/github/kuangcp/antlr4/expression/ExpressionTest.java b/algorithms/src/test/java/com/github/kuangcp/antlr4/expression/ExpressionTest.java index c0059a12..4c181fe7 100644 --- a/algorithms/src/test/java/com/github/kuangcp/antlr4/expression/ExpressionTest.java +++ b/algorithms/src/test/java/com/github/kuangcp/antlr4/expression/ExpressionTest.java @@ -35,7 +35,7 @@ public void testFirst() throws Exception { @Test public void testEval() throws Exception { try { - ExprLexer lexer = new ExprLexer(CharStreams.fromString("1 + 3\n")); + ExprLexer lexer = new ExprLexer(CharStreams.fromString("(1+3)*124+(9/3 + 4)\n")); CommonTokenStream tokens = new CommonTokenStream(lexer); ExprParser parser = new ExprParser(tokens); ExprParser.ProgContext tree = parser.prog(); @@ -45,6 +45,16 @@ public void testEval() throws Exception { } catch (Exception e) { log.error("", e); } + } + @Test + public void testTokenHandle() throws Exception { + ExprLexer lexer = new ExprLexer(CharStreams.fromString("9/3*(6+7)\n")); + CommonTokenStream tokens = new CommonTokenStream(lexer); + ExprParser parser = new ExprParser(tokens); + ExprParser.ProgContext tree = parser.prog(); + TokenVisitor eval = new TokenVisitor(); + String result = eval.visit(tree); + log.info("result={}", result); } } diff --git a/java8/pom.xml b/java8/pom.xml index 9d3718f6..64741431 100644 --- a/java8/pom.xml +++ b/java8/pom.xml @@ -19,6 +19,11 @@ + + com.github.kuangcp + kcp-tool + + diff --git a/java8/src/test/java/com/github/kuangcp/function/sort/BaseTypeSortTest.java b/java8/src/test/java/com/github/kuangcp/function/sort/BaseTypeSortTest.java index 683866b4..843abb63 100644 --- a/java8/src/test/java/com/github/kuangcp/function/sort/BaseTypeSortTest.java +++ b/java8/src/test/java/com/github/kuangcp/function/sort/BaseTypeSortTest.java @@ -1,44 +1,42 @@ package com.github.kuangcp.function.sort; - import com.github.kuangcp.time.GetRunTime; -import java.util.Arrays; +import org.junit.Test; + import java.util.Comparator; import java.util.List; import java.util.concurrent.ThreadLocalRandom; import java.util.stream.Collectors; import java.util.stream.IntStream; -import org.junit.Test; /** * @author kuangcp on 18-8-17-下午12:00 * base data type use functional Comparator to sort - * + *

* Double Float is the same way */ public class BaseTypeSortTest { - @Test - public void testInt() { - List lists = IntStream.rangeClosed(1, 10000) - .map(i -> ThreadLocalRandom.current().nextInt(10000)).boxed() - .collect(Collectors.toList()); - - GetRunTime getRunTime = new GetRunTime().startCount(); - lists.sort(Comparator.comparingInt(Integer::intValue)); - getRunTime.endCount("int 排序完成"); - } - - // TODO 一起执行时 为什么这个后执行的方法要快, 单独执行就不会有这样的情况 - @Test - public void testLong() { - List lists = IntStream.rangeClosed(1, 10000) - .mapToLong(i -> ThreadLocalRandom.current().nextLong(10000)).boxed() - .collect(Collectors.toList()); - - GetRunTime getRunTime = new GetRunTime().startCount(); - lists.sort(Comparator.comparingLong(Long::longValue)); - getRunTime.endCount("long 排序完成"); - } + @Test + public void testInt() { + List lists = IntStream.rangeClosed(1, 10000) + .map(i -> ThreadLocalRandom.current().nextInt(10000)).boxed() + .collect(Collectors.toList()); + + GetRunTime getRunTime = new GetRunTime().startCount(); + lists.sort(Comparator.comparingInt(Integer::intValue)); + getRunTime.endCount("int 排序完成"); + } + + @Test + public void testLong() { + List lists = IntStream.rangeClosed(1, 10000) + .mapToLong(i -> ThreadLocalRandom.current().nextLong(10000)).boxed() + .collect(Collectors.toList()); + + GetRunTime getRunTime = new GetRunTime().startCount(); + lists.sort(Comparator.comparingLong(Long::longValue)); + getRunTime.endCount("long 排序完成"); + } } diff --git a/java8/src/test/java/com/github/kuangcp/function/sort/CustomSortTest.java b/java8/src/test/java/com/github/kuangcp/function/sort/CustomSortTest.java index d9dabedf..9e78f7b3 100644 --- a/java8/src/test/java/com/github/kuangcp/function/sort/CustomSortTest.java +++ b/java8/src/test/java/com/github/kuangcp/function/sort/CustomSortTest.java @@ -1,31 +1,31 @@ package com.github.kuangcp.function.sort; -import static org.hamcrest.CoreMatchers.equalTo; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; +import org.junit.Test; import java.util.Arrays; import java.util.List; import java.util.Optional; -import org.junit.Test; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.junit.Assert.assertThat; /** * @author kuangcp on 3/8/19-2:25 PM */ public class CustomSortTest { - @Test - public void testString() { - List list = Arrays.asList("map1", "ma2p3", "map23"); - Optional maxOpt = list.stream().max((a, b) -> { - String[] mapA = a.split("map"); - String[] mapB = b.split("map"); - if (mapA.length > 1 && mapB.length > 1) { - return Integer.compare(Integer.parseInt(mapA[1]), Integer.parseInt(mapB[1])); - } - return 0; - }); + @Test + public void testString() { + List list = Arrays.asList("map1", "ma2p3", "map23"); + Optional maxOpt = list.stream().max((a, b) -> { + String[] mapA = a.split("map"); + String[] mapB = b.split("map"); + if (mapA.length > 1 && mapB.length > 1) { + return Integer.compare(Integer.parseInt(mapA[1]), Integer.parseInt(mapB[1])); + } + return 0; + }); - assertThat(maxOpt.get(), equalTo("map23")); - } + assertThat(maxOpt.get(), equalTo("map23")); + } } diff --git a/java8/src/test/java/com/github/kuangcp/function/sort/DateSortTest.java b/java8/src/test/java/com/github/kuangcp/function/sort/DateSortTest.java index e9e5e08c..b0388bad 100644 --- a/java8/src/test/java/com/github/kuangcp/function/sort/DateSortTest.java +++ b/java8/src/test/java/com/github/kuangcp/function/sort/DateSortTest.java @@ -1,57 +1,58 @@ package com.github.kuangcp.function.sort; +import org.junit.Test; + import java.time.LocalDateTime; import java.util.Comparator; import java.util.LinkedList; import java.util.List; import java.util.stream.Collectors; import java.util.stream.IntStream; -import org.junit.Test; /** * @author kuangcp on 18-8-27-上午11:35 */ public class DateSortTest { - @Test - public void testLocalDateTime() { - List datetimeList = new LinkedList<>(); - for (int i = 0; i < 10; i++) { - LocalDateTime days = LocalDateTime.now().plusDays(i); - datetimeList.add(days); + @Test + public void testLocalDateTime() { + List datetimeList = new LinkedList<>(); + for (int i = 0; i < 10; i++) { + LocalDateTime days = LocalDateTime.now().plusDays(i); + datetimeList.add(days); + } + + datetimeList.sort((a, b) -> { + if (a.equals(b)) { + return 0; + } + if (a.isAfter(b)) { + return 1; + } else { + return -1; + } + }); + + datetimeList.forEach(System.out::println); } - datetimeList.sort((a, b) -> { - if (a.equals(b)) { - return 0; - } - if (a.isAfter(b)) { - return 1; - } else { - return -1; - } - }); - - datetimeList.forEach(System.out::println); - } - - @Test - public void testSimple() { - IntStream.rangeClosed(1, 10) - .mapToObj(i -> LocalDateTime.now().plusDays(i)) - .sorted((a, b) -> a.compareTo(b) * -1) - .forEach(System.out::println); - - List data = IntStream.rangeClosed(1, 10) - .mapToObj(i -> LocalDateTime.now().plusDays(i)) - .sorted((a, b) -> a.compareTo(b) * -1) - .collect(Collectors.toList()); - data.forEach(System.out::println); - } - - @Test - public void testReversed() { - IntStream.rangeClosed(1, 10).mapToObj(i -> LocalDateTime.now().plusDays(i)) - .sorted(Comparator.reverseOrder()).forEach(System.out::println); - } + @Test + public void testSimple() { + IntStream.rangeClosed(1, 10) + .mapToObj(i -> LocalDateTime.now().plusDays(i)) + .sorted((a, b) -> a.compareTo(b) * -1) + .forEach(System.out::println); + + List data = IntStream.rangeClosed(1, 10) + .mapToObj(i -> LocalDateTime.now().plusDays(i)) + .sorted((a, b) -> a.compareTo(b) * -1) + .collect(Collectors.toList()); + data.forEach(System.out::println); + } + + @Test + public void testReversed() { + IntStream.rangeClosed(1, 10).mapToObj(i -> LocalDateTime.now().plusDays(i)) + .sorted(Comparator.reverseOrder()).forEach(System.out::println); + } } diff --git a/java8/src/test/java/com/github/kuangcp/future/CompletableFutureTest.java b/java8/src/test/java/com/github/kuangcp/future/CompletableFutureTest.java index afca0afb..0d2f5490 100644 --- a/java8/src/test/java/com/github/kuangcp/future/CompletableFutureTest.java +++ b/java8/src/test/java/com/github/kuangcp/future/CompletableFutureTest.java @@ -86,7 +86,7 @@ public void testFutureAndCombine() throws Exception { // 只会在两个函数都完成计算后进行 避免get() 产生阻塞,并发的损失和死锁问题 // CompletableFuture 和 结合器 一起使用 - CompletableFuture resultFuture = func1Future.thenCombine(func2Future, (a, b) -> a + b); + CompletableFuture resultFuture = func1Future.thenCombine(func2Future, Integer::sum); executorService.submit(() -> func1Future.complete(func1(num))); executorService.submit(() -> func2Future.complete(func2(num))); From 0ef9c32fdd4c09a6bd49b272ac140b0d8c78c6fe Mon Sep 17 00:00:00 2001 From: kuangcp Date: Mon, 25 Sep 2023 13:31:49 +0800 Subject: [PATCH 296/476] fmt --- class/src/main/java/security/Readme.md | 1 + .../kuangcp/forkjoin/ElementSorter.groovy | 97 +++++++++---------- .../kuangcp/forkjoin/ForkJoinEasyDemo.groovy | 21 ++-- 3 files changed, 61 insertions(+), 58 deletions(-) create mode 100644 class/src/main/java/security/Readme.md diff --git a/class/src/main/java/security/Readme.md b/class/src/main/java/security/Readme.md new file mode 100644 index 00000000..198a4ebd --- /dev/null +++ b/class/src/main/java/security/Readme.md @@ -0,0 +1 @@ +> [密码学](https://gitee.com/gin9/Memo/blob/master/Algorithm/Cryptography.md) diff --git a/concurrency/src/main/java/com/github/kuangcp/forkjoin/ElementSorter.groovy b/concurrency/src/main/java/com/github/kuangcp/forkjoin/ElementSorter.groovy index b438f3a4..69dc2b7f 100644 --- a/concurrency/src/main/java/com/github/kuangcp/forkjoin/ElementSorter.groovy +++ b/concurrency/src/main/java/com/github/kuangcp/forkjoin/ElementSorter.groovy @@ -9,63 +9,62 @@ import java.util.concurrent.RecursiveAction // 继承这个类才可以看成是一个task class ElementSorter extends RecursiveAction { - // 这是一个标记量,当排序元素低于这个数量就用 Arrays.sort() 排序,大于就用归并 - private static final int SMALL_ENOUGH = 10 - private final Element[] elements - private final int start, end - private final Element[] result + // 这是一个标记量,当排序元素低于这个数量就用 Arrays.sort() 排序,大于就用归并 + private static final int SMALL_ENOUGH = 10 + private final Element[] elements + private final int start, end + private final Element[] result - ElementSorter(Element[] elements) { - this(elements, 0, elements.length) - } - - ElementSorter(Element[] elements, int start, int end) { - this.elements = elements - this.start = start - this.end = end - this.result = new Element[elements.length] - } - // 合并排序 - void merge(ElementSorter left, ElementSorter right) { - int i = 0 - int lCt = 0 - int rCt = 0 - while (lCt < left.size() && rCt < right.size()) { - result[i++] = left.result[lCt] < right.result[rCt] ? - left.result[lCt++] : right.result[rCt++] + ElementSorter(Element[] elements) { + this(elements, 0, elements.length) } - while (lCt < left.size()) { - result[i++] = left.result[lCt++] + + ElementSorter(Element[] elements, int start, int end) { + this.elements = elements + this.start = start + this.end = end + this.result = new Element[elements.length] } - while (rCt < right.size()) { - result[i++] = right.result[rCt++] + // 合并排序 + void merge(ElementSorter left, ElementSorter right) { + int i = 0 + int lCt = 0 + int rCt = 0 + while (lCt < left.size() && rCt < right.size()) { + result[i++] = left.result[lCt] < right.result[rCt] ? + left.result[lCt++] : right.result[rCt++] + } + while (lCt < left.size()) { + result[i++] = left.result[lCt++] + } + while (rCt < right.size()) { + result[i++] = right.result[rCt++] + } } - } - int size() { - return end - start - } + int size() { + return end - start + } - Element[] getResult() { - return result - } - // 父类声明的抽象方法 - @Override - protected void compute() { - if (size() < SMALL_ENOUGH) { - System.arraycopy(elements, start, result, 0, size()) - Arrays.sort(result, 0, size()) - } else { - int mid = (int) (size() / 2) - ElementSorter left = new ElementSorter(elements, start, start + mid) - ElementSorter right = new ElementSorter(elements, start + mid, end) + Element[] getResult() { + return result + } + // 父类声明的抽象方法 + @Override + protected void compute() { + if (size() < SMALL_ENOUGH) { + System.arraycopy(elements, start, result, 0, size()) + Arrays.sort(result, 0, size()) + } else { + int mid = (int) (size() / 2) + ElementSorter left = new ElementSorter(elements, start, start + mid) + ElementSorter right = new ElementSorter(elements, start + mid, end) // 执行处,将两个task执行,这两个task的任务就是递归的拆解整个数组,然后进入下面的合并排序里 - invokeAll(left, right) - // 调用会报错,因为没有没有对象(或者说 任务单元的资源)去调用这个方法, - // StackOverflowError 这是啥,栈溢出 + invokeAll(left, right) + // 调用会报错,因为没有没有对象(或者说 任务单元的资源)去调用这个方法, // invoke() - merge(left, right) + merge(left, right) + } } - } } diff --git a/concurrency/src/main/java/com/github/kuangcp/forkjoin/ForkJoinEasyDemo.groovy b/concurrency/src/main/java/com/github/kuangcp/forkjoin/ForkJoinEasyDemo.groovy index ed4c6b93..813c3794 100644 --- a/concurrency/src/main/java/com/github/kuangcp/forkjoin/ForkJoinEasyDemo.groovy +++ b/concurrency/src/main/java/com/github/kuangcp/forkjoin/ForkJoinEasyDemo.groovy @@ -7,8 +7,9 @@ import java.util.concurrent.ForkJoinPool */ // 生成数据 -List lu = new ArrayList() -for (int i = 0; i < 12; i++) { +def total = 1000 +List lu = new ArrayList(total) +for (int i = 0; i < total; i++) { Element element = new Element(i + 1) lu.add(element) sleep(2) @@ -16,19 +17,21 @@ for (int i = 0; i < 12; i++) { // 打乱顺序 Collections.shuffle(lu) -for (Element el : lu) { - println(el.toString()) -} elements = lu.toArray(new Element[0])// 传入空数组,省掉空间分配 ElementSorter sorter = new ElementSorter(elements as Element[]) +println("开始排序") // 使用任务池封装一层再调用,或者直接运行方法都可以 //sorter.compute() ForkJoinPool pool = new ForkJoinPool(4) pool.invoke(sorter) -println("排序后") -for (Element element : sorter.getResult()) { - println(element.toString()) -} \ No newline at end of file +for (i in 0.. Date: Wed, 27 Sep 2023 19:58:38 +0800 Subject: [PATCH 297/476] rm: --- concurrency/pom.xml | 14 +- .../github/kuangcp/forkjoin/Counter.groovy | 18 -- .../github/kuangcp/forkjoin/Element.groovy | 24 +- .../kuangcp/forkjoin/ElementSorter.groovy | 2 +- .../kuangcp/forkjoin/SynchronizedDemo.groovy | 51 --- .../github/kuangcp/lock/sync/SleepDemo.java | 75 ++--- .../github/kuangcp/lock/pvp/PlayerTest.java | 304 +++++++++--------- .../queue/use/blocking/VeterinarianTest.java | 37 +-- .../volatiles/NeverStopThreadTest.java | 60 ++-- pom.xml | 6 +- 10 files changed, 267 insertions(+), 324 deletions(-) delete mode 100644 concurrency/src/main/java/com/github/kuangcp/forkjoin/Counter.groovy delete mode 100644 concurrency/src/main/java/com/github/kuangcp/forkjoin/SynchronizedDemo.groovy diff --git a/concurrency/pom.xml b/concurrency/pom.xml index b6b6d6bf..c59f7e23 100644 --- a/concurrency/pom.xml +++ b/concurrency/pom.xml @@ -29,9 +29,19 @@ jedis + + + + + + + + + + - com.bladejava - blade-mvc + com.hellokaton + blade-core org.apache.groovy diff --git a/concurrency/src/main/java/com/github/kuangcp/forkjoin/Counter.groovy b/concurrency/src/main/java/com/github/kuangcp/forkjoin/Counter.groovy deleted file mode 100644 index ffb717e6..00000000 --- a/concurrency/src/main/java/com/github/kuangcp/forkjoin/Counter.groovy +++ /dev/null @@ -1,18 +0,0 @@ -package com.github.kuangcp.forkjoin - -/** - * - * @author kuangcp on 18-11-22-下午6:01 - */ -class Counter { - - int size = 0 - - synchronized void increase() { - size++ - } - - int current() { - return size - } -} diff --git a/concurrency/src/main/java/com/github/kuangcp/forkjoin/Element.groovy b/concurrency/src/main/java/com/github/kuangcp/forkjoin/Element.groovy index a04e709a..3b1da42d 100644 --- a/concurrency/src/main/java/com/github/kuangcp/forkjoin/Element.groovy +++ b/concurrency/src/main/java/com/github/kuangcp/forkjoin/Element.groovy @@ -6,19 +6,19 @@ package com.github.kuangcp.forkjoin */ class Element implements Comparable { - Long id + Long id - Element(Long id) { - this.id = id - } + Element(Long id) { + this.id = id + } - @Override - int compareTo(Object other) { - return this.id - ((Element) other).id - } + @Override + int compareTo(Object other) { + return this.id - ((Element) other).id + } - @Override - String toString() { - return "Element{" + "id=" + id + '}' - } + @Override + String toString() { + return "Element{" + "id=" + id + '}' + } } diff --git a/concurrency/src/main/java/com/github/kuangcp/forkjoin/ElementSorter.groovy b/concurrency/src/main/java/com/github/kuangcp/forkjoin/ElementSorter.groovy index 69dc2b7f..66d425fa 100644 --- a/concurrency/src/main/java/com/github/kuangcp/forkjoin/ElementSorter.groovy +++ b/concurrency/src/main/java/com/github/kuangcp/forkjoin/ElementSorter.groovy @@ -25,7 +25,7 @@ class ElementSorter extends RecursiveAction { this.end = end this.result = new Element[elements.length] } - // 合并排序 + // 归并排序 void merge(ElementSorter left, ElementSorter right) { int i = 0 int lCt = 0 diff --git a/concurrency/src/main/java/com/github/kuangcp/forkjoin/SynchronizedDemo.groovy b/concurrency/src/main/java/com/github/kuangcp/forkjoin/SynchronizedDemo.groovy deleted file mode 100644 index f8c20e36..00000000 --- a/concurrency/src/main/java/com/github/kuangcp/forkjoin/SynchronizedDemo.groovy +++ /dev/null @@ -1,51 +0,0 @@ -package com.github.kuangcp.forkjoin - -/** - * Created by https://github.com/kuangcp - * @author kuangcp* @date 18-4-1 下午2:54 - */ - -//status = 10 -//while ((s = status) >= 0){ -// sleep(500) -// status-- -// synchronized (this) { -// if (status >= 0) { -// try { -// wait(0L); -// } catch (InterruptedException ie) { -// interrupted = true; -// } -// } -// else -// notifyAll(); -// } -//} - -Counter a = new Counter() -a.setSize(89) -new Thread() { - - @Override - void run() { - for (int i = 0; i < 10; i++) { - sleep(200) - a.increase() - } - } -}.start() - -new Thread() { - - @Override - void run() { - for (int i = 0; i < 10; i++) { - sleep(100) - print(a.current()) - } - } -}.start() - -print("<" + a.size + ">") -sleep(3000) -print("|||" + a.size) \ No newline at end of file diff --git a/concurrency/src/main/java/com/github/kuangcp/lock/sync/SleepDemo.java b/concurrency/src/main/java/com/github/kuangcp/lock/sync/SleepDemo.java index 016593dc..194240fa 100644 --- a/concurrency/src/main/java/com/github/kuangcp/lock/sync/SleepDemo.java +++ b/concurrency/src/main/java/com/github/kuangcp/lock/sync/SleepDemo.java @@ -1,8 +1,9 @@ package com.github.kuangcp.lock.sync; -import java.util.concurrent.TimeUnit; import lombok.extern.slf4j.Slf4j; +import java.util.concurrent.TimeUnit; + /** * created by https://gitee.com/gin9 * @@ -11,45 +12,45 @@ @Slf4j public class SleepDemo { - @Slf4j - static class Tool implements Runnable { + @Slf4j + static class Tool implements Runnable { - private String name; + private String name; - public Tool setName(String name) { - this.name = name; - return this; - } + public Tool setName(String name) { + this.name = name; + return this; + } - @Override - public void run() { - while (true) { - synchronized (this) { - try { - log.info("running: name={}", name); - TimeUnit.SECONDS.sleep(1); - } catch (InterruptedException e) { - e.printStackTrace(); - } + @Override + public void run() { + while (true) { + synchronized (this) { + try { + log.info("running: name={}", name); + TimeUnit.SECONDS.sleep(1); + } catch (InterruptedException e) { + log.error("", e); + } + } + } } - } } - } - - // 结果是 0 1 无序输出, 因为线程先后的调度不是完美的, 如果是完美的, 那么就会一直输出0 1 永远拿不到锁 - public static void main(String[] args) { - new Thread(new Tool().setName("0")).start(); - new Thread(new Tool().setName("1")).start(); - - new Thread(() -> { - while (true) { - try { - TimeUnit.SECONDS.sleep(1); - } catch (InterruptedException e) { - e.printStackTrace(); - } - System.out.println(); - } - }).start(); - } + + // 结果是 0 1 无序输出, 因为线程先后的调度不是完美的, 如果是完美的, 那么就会一直输出0 1 永远拿不到锁 + public static void main(String[] args) { + new Thread(new Tool().setName("0")).start(); + new Thread(new Tool().setName("1")).start(); + + new Thread(() -> { + while (true) { + try { + TimeUnit.SECONDS.sleep(1); + } catch (InterruptedException e) { + log.error("", e); + } + System.out.println(); + } + }).start(); + } } diff --git a/concurrency/src/test/java/com/github/kuangcp/lock/pvp/PlayerTest.java b/concurrency/src/test/java/com/github/kuangcp/lock/pvp/PlayerTest.java index 7deca7be..2dfb5ee3 100644 --- a/concurrency/src/test/java/com/github/kuangcp/lock/pvp/PlayerTest.java +++ b/concurrency/src/test/java/com/github/kuangcp/lock/pvp/PlayerTest.java @@ -14,175 +14,175 @@ /** * @author kuangcp on 2019-04-21 11:14 AM - * + *

* 加锁顺序问题测试, 结论 加锁需要顺序,释放锁无需顺序 */ @Slf4j public class PlayerTest { - private final Map playerMap = new HashMap<>(); + private final Map playerMap = new HashMap<>(); - private void arenaLogic(Player a, Player b) throws InterruptedException { - log.info("enter arena: a={} b={}", a, b); - Thread.sleep(ThreadLocalRandom.current().nextInt(40)); - } + private void arenaLogic(Player a, Player b) throws InterruptedException { + log.info("enter arena: a={} b={}", a, b); + Thread.sleep(ThreadLocalRandom.current().nextInt(40)); + } - private void init() { - Player a = new Player(); - Player b = new Player(); - Player c = new Player(); + private void init() { + Player a = new Player(); + Player b = new Player(); + Player c = new Player(); - playerMap.put("a", a); - playerMap.put("b", b); - playerMap.put("c", c); - } + playerMap.put("a", a); + playerMap.put("b", b); + playerMap.put("c", c); + } - private Tuple2 randomPlayer() { - ArrayList list = new ArrayList<>(playerMap.keySet()); + private Tuple2 randomPlayer() { + ArrayList list = new ArrayList<>(playerMap.keySet()); - int aIndex = 0; - int bIndex = 0; - while (aIndex == bIndex) { - aIndex = ThreadLocalRandom.current().nextInt(list.size()); - bIndex = ThreadLocalRandom.current().nextInt(list.size()); + int aIndex = 0; + int bIndex = 0; + while (aIndex == bIndex) { + aIndex = ThreadLocalRandom.current().nextInt(list.size()); + bIndex = ThreadLocalRandom.current().nextInt(list.size()); + } + + String aId = list.get(aIndex); + String bId = list.get(bIndex); + return Tuple2.of(aId, bId); } - String aId = list.get(aIndex); - String bId = list.get(bIndex); - return Tuple2.of(aId, bId); - } - - @Test - public void testDeadLock() throws Exception { - init(); - ExecutorService pool = Executors.newFixedThreadPool(6); - - for (int i = 0; i < 100; i++) { - pool.submit(() -> { - Tuple2 playerPair = this.randomPlayer(); - String aId = playerPair.getFirst(); - String bId = playerPair.getSecond(); - Player a = playerMap.get(aId); - Player b = playerMap.get(bId); - - log.debug("{} {} prepare enter arena", aId, bId); - - boolean enter = false; - try { - a.getLock().lock(); - b.getLock().lock(); - - enter = true; - log.info("enter ids: a={} b={}", aId, bId); - arenaLogic(a, b); - } catch (Exception e) { - log.error("", e); - } finally { - if (enter) { - a.getLock().unlock(); - b.getLock().unlock(); - log.info("{} {} exit arena", aId, bId); - } + @Test + public void testDeadLock() throws Exception { + init(); + ExecutorService pool = Executors.newFixedThreadPool(6); + + for (int i = 0; i < 100; i++) { + pool.submit(() -> { + Tuple2 playerPair = this.randomPlayer(); + String aId = playerPair.getFirst(); + String bId = playerPair.getSecond(); + Player a = playerMap.get(aId); + Player b = playerMap.get(bId); + + log.debug("{} {} prepare enter arena", aId, bId); + + boolean enter = false; + try { + a.getLock().lock(); + b.getLock().lock(); + + enter = true; + log.info("enter ids: a={} b={}", aId, bId); + arenaLogic(a, b); + } catch (Exception e) { + log.error("", e); + } finally { + if (enter) { + a.getLock().unlock(); + b.getLock().unlock(); + log.info("{} {} exit arena", aId, bId); + } + } + }); + Thread.sleep(10); } - }); - Thread.sleep(10); + log.warn("end loop"); + + Thread.currentThread().join(Duration.ofMinutes(10).toMillis()); } - log.warn("end loop"); - - Thread.currentThread().join(Duration.ofMinutes(10).toMillis()); - } - - /** - * 保证获取锁时的顺序,释放锁的顺序无所谓 - */ - @Test - public void testSortLock() throws Exception { - init(); - ExecutorService pool = Executors.newFixedThreadPool(6); - - for (int i = 0; i < 100000; i++) { - pool.submit(() -> { - Tuple2 playerPair = this.randomPlayer(); - String aId = playerPair.getFirst(); - String bId = playerPair.getSecond(); - - if (aId.compareTo(bId) > 0) { - String temp = aId; - aId = bId; - bId = temp; - } - Player a = playerMap.get(aId); - Player b = playerMap.get(bId); - - log.debug("{} {} prepare enter arena", aId, bId); - - boolean enter = false; - try { - a.getLock().lock(); - b.getLock().lock(); - - enter = true; - log.info("enter ids: a={} b={}", aId, bId); - arenaLogic(a, b); - } catch (Exception e) { - log.error("", e); - } finally { - if (enter) { - a.getLock().unlock(); - b.getLock().unlock(); - log.info("{} {} exit arena", aId, bId); - } + /** + * 保证获取锁时的顺序,释放锁的顺序无所谓 + */ + @Test + public void testSortLock() throws Exception { + init(); + ExecutorService pool = Executors.newFixedThreadPool(6); + + for (int i = 0; i < 100000; i++) { + pool.submit(() -> { + Tuple2 playerPair = this.randomPlayer(); + String aId = playerPair.getFirst(); + String bId = playerPair.getSecond(); + + if (aId.compareTo(bId) > 0) { + String temp = aId; + aId = bId; + bId = temp; + } + + Player a = playerMap.get(aId); + Player b = playerMap.get(bId); + + log.debug("{} {} prepare enter arena", aId, bId); + + boolean enter = false; + try { + a.getLock().lock(); + b.getLock().lock(); + + enter = true; + log.info("enter ids: a={} b={}", aId, bId); + arenaLogic(a, b); + } catch (Exception e) { + log.error("", e); + } finally { + if (enter) { + a.getLock().unlock(); + b.getLock().unlock(); + log.info("{} {} exit arena", aId, bId); + } + } + }); + Thread.sleep(10); } - }); - Thread.sleep(10); + log.warn("end loop"); + + Thread.currentThread().join(Duration.ofMinutes(10).toMillis()); } - log.warn("end loop"); - - Thread.currentThread().join(Duration.ofMinutes(10).toMillis()); - } - - @Test - public void testTryThenLock() throws Exception { - init(); - ExecutorService pool = Executors.newFixedThreadPool(6); - - for (int i = 0; i < 1000; i++) { - pool.submit(() -> { - Tuple2 playerPair = this.randomPlayer(); - String aId = playerPair.getFirst(); - String bId = playerPair.getSecond(); - Player a = playerMap.get(aId); - Player b = playerMap.get(bId); - - log.debug("{} {} prepare enter arena", aId, bId); - - boolean enter = false; - try { - - if (a.getLock().tryLock() && b.getLock().tryLock()) { - a.getLock().lock(); - b.getLock().lock(); - enter = true; - log.info("enter ids: a={} b={}", aId, bId); - arenaLogic(a, b); - } else { - log.warn("failed get lock"); - } - } catch (Exception e) { - log.error("", e); - } finally { - if (enter) { - a.getLock().unlock(); - b.getLock().unlock(); - log.info("{} {} exit arena", aId, bId); - } + + @Test + public void testTryThenLock() throws Exception { + init(); + ExecutorService pool = Executors.newFixedThreadPool(6); + + for (int i = 0; i < 1000; i++) { + pool.submit(() -> { + Tuple2 playerPair = this.randomPlayer(); + String aId = playerPair.getFirst(); + String bId = playerPair.getSecond(); + Player a = playerMap.get(aId); + Player b = playerMap.get(bId); + + log.debug("{} {} prepare enter arena", aId, bId); + + boolean enter = false; + try { + + if (a.getLock().tryLock() && b.getLock().tryLock()) { + a.getLock().lock(); + b.getLock().lock(); + enter = true; + log.info("enter ids: a={} b={}", aId, bId); + arenaLogic(a, b); + } else { + log.warn("failed get lock"); + } + } catch (Exception e) { + log.error("", e); + } finally { + if (enter) { + a.getLock().unlock(); + b.getLock().unlock(); + log.info("{} {} exit arena", aId, bId); + } + } + }); + Thread.sleep(10); } - }); - Thread.sleep(10); - } - log.warn("end loop"); - Thread.currentThread().join(Duration.ofMinutes(10).toMillis()); - } + log.warn("end loop"); + Thread.currentThread().join(Duration.ofMinutes(10).toMillis()); + } } diff --git a/concurrency/src/test/java/com/github/kuangcp/queue/use/blocking/VeterinarianTest.java b/concurrency/src/test/java/com/github/kuangcp/queue/use/blocking/VeterinarianTest.java index 109e3d56..8a7452d5 100644 --- a/concurrency/src/test/java/com/github/kuangcp/queue/use/blocking/VeterinarianTest.java +++ b/concurrency/src/test/java/com/github/kuangcp/queue/use/blocking/VeterinarianTest.java @@ -1,33 +1,34 @@ package com.github.kuangcp.queue.use.blocking; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.LinkedBlockingQueue; import lombok.extern.slf4j.Slf4j; import org.junit.Test; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; + /** * @author kuangcp on 2019-04-19 12:41 AM */ @Slf4j public class VeterinarianTest { - @Test - public void testRun() throws Exception { - BlockingQueue> lists = new LinkedBlockingQueue<>(); - lists.add(new Appointment<>(new Cat("1"))); - lists.add(new Appointment<>(new Cat("2"))); - lists.add(new Appointment<>(new Dog("1"))); - lists.add(new Appointment<>(new Dog("2"))); + @Test + public void testRun() throws Exception { + BlockingQueue> lists = new LinkedBlockingQueue<>(); + lists.add(new Appointment<>(new Cat("1"))); + lists.add(new Appointment<>(new Cat("2"))); + lists.add(new Appointment<>(new Dog("1"))); + lists.add(new Appointment<>(new Dog("2"))); - Veterinarian veterinarian = new Veterinarian(lists, 2000); - veterinarian.text = "Veterinarian 1"; - Veterinarian veterinarian2 = new Veterinarian(lists, 1000); - veterinarian2.text = "Veterinarian 2"; + Veterinarian veterinarian = new Veterinarian(lists, 2000); + veterinarian.text = "Veterinarian 1"; + Veterinarian veterinarian2 = new Veterinarian(lists, 1000); + veterinarian2.text = "Veterinarian 2"; - veterinarian.start(); - veterinarian2.start(); + veterinarian.start(); + veterinarian2.start(); - veterinarian.join(); - veterinarian2.join(); - } + veterinarian.join(); + veterinarian2.join(); + } } \ No newline at end of file diff --git a/concurrency/src/test/java/com/github/kuangcp/volatiles/NeverStopThreadTest.java b/concurrency/src/test/java/com/github/kuangcp/volatiles/NeverStopThreadTest.java index cb74b1f0..fbda9855 100644 --- a/concurrency/src/test/java/com/github/kuangcp/volatiles/NeverStopThreadTest.java +++ b/concurrency/src/test/java/com/github/kuangcp/volatiles/NeverStopThreadTest.java @@ -9,43 +9,43 @@ @Slf4j public class NeverStopThreadTest { - @Test - public void testNeverStop() throws Exception { - NeverStopThread demo = new NeverStopThread(); - Thread thread = new Thread(demo::neverStop); - thread.start(); + @Test + public void testNeverStop() throws Exception { + NeverStopThread demo = new NeverStopThread(); + Thread thread = new Thread(demo::neverStop); + thread.start(); - // 如果不加输出, 可能线程都还没创建起来, 这里就直接修改成了 false - log.info("prepare to stop"); - demo.stop(); + // 如果不加输出, 可能线程都还没创建起来, 这里就直接修改成了 false + log.info("prepare to stop"); + demo.stop(); - thread.join(); - // 无法停止的原因是 JMM的原因 值在自己的缓存里, 所以这里改动了 stop 但是 thread 里的 stop 没有更新 - } + thread.join(); + // 无法停止的原因是 JMM的原因 值在自己的缓存里, 所以这里改动了 stop 但是 thread 里的 stop 没有更新 + } - @Test - public void testVolatile() throws InterruptedException { - NeverStopThread demo = new NeverStopThread(); - Thread thread = new Thread(demo::normalStopWithVolatile); - thread.start(); + @Test + public void testVolatile() throws InterruptedException { + NeverStopThread demo = new NeverStopThread(); + Thread thread = new Thread(demo::normalStopWithVolatile); + thread.start(); - // 如果不加输出, 可能线程都还没创建起来, 这里就直接修改成了false - log.info("prepare to stop"); - demo.stopWithVolatile(); + // 如果不加输出, 可能线程都还没创建起来, 这里就直接修改成了false + log.info("prepare to stop"); + demo.stopWithVolatile(); - thread.join(); - } + thread.join(); + } - @Test - public void testSleep() throws InterruptedException { - NeverStopThread demo = new NeverStopThread(); - Thread thread = new Thread(demo::normalStopWithSleep); - thread.start(); + @Test + public void testSleep() throws InterruptedException { + NeverStopThread demo = new NeverStopThread(); + Thread thread = new Thread(demo::normalStopWithSleep); + thread.start(); - log.info("prepare to stop"); - demo.stopWithSleep(); + log.info("prepare to stop"); + demo.stopWithSleep(); - thread.join(); - } + thread.join(); + } } \ No newline at end of file diff --git a/pom.xml b/pom.xml index a3e34c9c..f6ea7232 100644 --- a/pom.xml +++ b/pom.xml @@ -187,9 +187,9 @@ 4.1.67.Final - com.bladejava - blade-mvc - 2.0.15.RELEASE + com.hellokaton + blade-core + 2.1.2.RELEASE javax.mail From 826ba0717db21c9c2e0d9d5edd37e22000576712 Mon Sep 17 00:00:00 2001 From: kuangcp Date: Mon, 9 Oct 2023 18:46:40 +0800 Subject: [PATCH 298/476] fix: compile --- concurrency/pom.xml | 6 ++++++ .../src/main/java/situation/timoutpool/CreateNewPool.java | 2 +- concurrency/src/main/java/web/Application.java | 7 +++++-- .../main/java/web/situation/TimeoutPoolController.java | 8 ++++---- 4 files changed, 16 insertions(+), 7 deletions(-) diff --git a/concurrency/pom.xml b/concurrency/pom.xml index c59f7e23..ebd90a3e 100644 --- a/concurrency/pom.xml +++ b/concurrency/pom.xml @@ -22,6 +22,12 @@ com.github.kuangcp kcp-tool + + + kcp-aop + com.github.kuangcp + + diff --git a/concurrency/src/main/java/situation/timoutpool/CreateNewPool.java b/concurrency/src/main/java/situation/timoutpool/CreateNewPool.java index b9e0ab0a..eeea0895 100644 --- a/concurrency/src/main/java/situation/timoutpool/CreateNewPool.java +++ b/concurrency/src/main/java/situation/timoutpool/CreateNewPool.java @@ -34,7 +34,7 @@ public Result execute(Param param, long timeout, TimeUnit timeUnit) { try { TimeUnit.MILLISECONDS.sleep(ThreadLocalRandom.current().nextInt(600) + 60); } catch (InterruptedException e) { - e.printStackTrace(); + log.error("", e); } // log.info("tmpParam={}", tmpParam); result.getDataList().add(tmpParam.toString()); diff --git a/concurrency/src/main/java/web/Application.java b/concurrency/src/main/java/web/Application.java index 0610d470..f99bed8a 100644 --- a/concurrency/src/main/java/web/Application.java +++ b/concurrency/src/main/java/web/Application.java @@ -1,6 +1,7 @@ package web; -import com.blade.Blade; + +import com.hellokaton.blade.Blade; /** * @author https://github.com/kuangcp on 2021-09-05 00:18 @@ -8,6 +9,8 @@ public class Application { public static void main(String[] args) { - Blade.of().get("/", ctx -> ctx.text("Hello Blade")).start(); + Blade.create() + .listen(32993) + .get("/", ctx -> ctx.text("Hello Blade")).start(Application.class, args); } } diff --git a/concurrency/src/main/java/web/situation/TimeoutPoolController.java b/concurrency/src/main/java/web/situation/TimeoutPoolController.java index f18d7225..ffde728b 100644 --- a/concurrency/src/main/java/web/situation/TimeoutPoolController.java +++ b/concurrency/src/main/java/web/situation/TimeoutPoolController.java @@ -1,8 +1,8 @@ package web.situation; -import com.blade.mvc.annotation.GetRoute; -import com.blade.mvc.annotation.Path; -import com.blade.mvc.http.Response; +import com.hellokaton.blade.annotation.Path; +import com.hellokaton.blade.annotation.route.GET; +import com.hellokaton.blade.mvc.http.Response; import lombok.extern.slf4j.Slf4j; import situation.timoutpool.CreateNewPool; import situation.timoutpool.base.Param; @@ -20,7 +20,7 @@ @Slf4j public class TimeoutPoolController { - @GetRoute("/situation/timeout/createNew") + @GET("/situation/timeout/createNew") public void createNew(Response response) throws InterruptedException { int loop = 60; final CountDownLatch latch = new CountDownLatch(loop); From 4a42a39e1479a4c667399ee93547cbb69888e70e Mon Sep 17 00:00:00 2001 From: kuangcp Date: Mon, 9 Oct 2023 18:54:46 +0800 Subject: [PATCH 299/476] readme --- .../src/main/java/situation/timoutpool/Readme.md | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/concurrency/src/main/java/situation/timoutpool/Readme.md b/concurrency/src/main/java/situation/timoutpool/Readme.md index 298555ff..2bf04b22 100644 --- a/concurrency/src/main/java/situation/timoutpool/Readme.md +++ b/concurrency/src/main/java/situation/timoutpool/Readme.md @@ -1,4 +1,17 @@ -## 并发执行任务,丢弃超时的任务 +## 上游服务吞吐量远大于下游服务的处理能力 + +上游服务吞吐量大于下游单次调用的处理能力,上游服务单个调用会传入大量数据,引发下游服务处理超时 + +> 思路1:拆分大请求为多个小请求,并发执行任务,丢弃超时的任务 对比临时线程池和 CompleteFuture 实现方案 +- 临时线程池 + - 优点:实现简单,随用随释放 + - 缺点:线程突增从而导致CPU hang住的风险 +- CompleteFuture + +> 思路2: Java21 虚拟线程 +方案:拆分大请求为多个小请求,开启虚拟线程完成请求调用 +- 优点:无需考虑线程数突增的风险 +- 缺点:升级Java21 \ No newline at end of file From 2fa763612403787ef6d488b6751acb24fbf5d2aa Mon Sep 17 00:00:00 2001 From: kuangcp Date: Mon, 9 Oct 2023 19:03:46 +0800 Subject: [PATCH 300/476] fmt: log --- .../src/main/antlr4/expression/ExprParser.g4 | 0 .../customserialize/MythSerialize.java | 2 +- .../java/jvm/gc/cms/TriggerFullGCForCMS.java | 47 +- .../reflects/InvokeWithInheritParamTest.java | 6 +- .../github/kuangcp/list/ElementArrayList.java | 75 +- .../kuangcp/volatiles/NeverStopThread.java | 2 +- .../situation/timoutpool/TimeoutExecPool.java | 2 +- .../situation/timoutpool/TimeoutFuture.java | 2 +- .../src/main/java/thread/order/Task.java | 57 +- .../java/thread/order/TaskWithVolatile.java | 46 +- .../src/main/java/thread/tryone/Thread1.java | 106 +-- .../src/main/java/thread/tryone/Thread2.java | 120 +-- .../src/main/java/thread/tryone/Thread4.java | 119 +-- .../kuangcp/latch/CountDownLatchTest.java | 2 +- .../test/java/thread/NotifyAndWaitTest.java | 2 +- .../com/github/kuangcp/hi/SimpleSource.java | 2 +- .../com/github/kuangcp/tank/util/Audio.java | 2 +- .../com/github/kuangcp/tank/util/Saved.java | 10 +- .../kuangcp/tank/v3/PlayStageMgrTest.java | 2 +- .../hdfs/hi/GeneralFileActionDemoTest.java | 2 +- .../com/github/kuangcp/file/CopyFile.java | 4 +- .../kuangcp/future/CompletableFutureTest.java | 4 +- .../github/kuangcp/stream/map/MapTest.java | 2 +- .../order/dao/OrderDaoSpringBootTest.java | 2 +- .../stream/CursorSessionSpringBootTest.java | 2 +- .../github/kuangcp/bio/onechatone/Client.java | 7 +- .../kuangcp/bio/onechatone/ClientThread.java | 33 +- .../kuangcp/bio/onechatone/ServerThread.java | 5 +- .../com/github/kuangcp/port/LogicThread.java | 89 +-- .../github/kuangcp/port/LogicThreadTest.java | 5 +- .../kuangcp/runable/GreetingClient.java | 19 +- .../kuangcp/runable/GreetingServer.java | 20 +- .../kuangcp/runable/SlowRequestTest.java | 2 +- .../simplex/method/ReadProperties.java | 11 +- .../method/SimplexMethodWithFraction.java | 687 +++++++++--------- 35 files changed, 771 insertions(+), 727 deletions(-) delete mode 100644 algorithms/src/main/antlr4/expression/ExprParser.g4 diff --git a/algorithms/src/main/antlr4/expression/ExprParser.g4 b/algorithms/src/main/antlr4/expression/ExprParser.g4 deleted file mode 100644 index e69de29b..00000000 diff --git a/class/src/main/java/com/github/kuangcp/serialize/customserialize/MythSerialize.java b/class/src/main/java/com/github/kuangcp/serialize/customserialize/MythSerialize.java index 7b19e742..5906cb4a 100644 --- a/class/src/main/java/com/github/kuangcp/serialize/customserialize/MythSerialize.java +++ b/class/src/main/java/com/github/kuangcp/serialize/customserialize/MythSerialize.java @@ -76,7 +76,7 @@ public ByteArrayOutputStream out(T object) { byteOutput.write(result.getBytes()); return byteOutput; } catch (IOException | IllegalAccessException | InvocationTargetException e) { - e.printStackTrace(); + log.error("", e); } return null; } diff --git a/class/src/main/java/jvm/gc/cms/TriggerFullGCForCMS.java b/class/src/main/java/jvm/gc/cms/TriggerFullGCForCMS.java index 7160f848..c2de40c0 100644 --- a/class/src/main/java/jvm/gc/cms/TriggerFullGCForCMS.java +++ b/class/src/main/java/jvm/gc/cms/TriggerFullGCForCMS.java @@ -1,5 +1,7 @@ package jvm.gc.cms; +import lombok.extern.slf4j.Slf4j; + import java.util.ArrayList; import java.util.List; import java.util.concurrent.ExecutorService; @@ -8,37 +10,38 @@ /** * @author https://github.com/kuangcp on 2021-05-15 18:53 - * + *

* 问题:内存溢出后会死循环创建 dump 直到填满硬盘,CMSScavengeBeforeRemark 并不能在 CMSGC 的时候触发 FullGC - * + *

* -Xms300m -Xmx300m -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:CMSFullGCsBeforeCompaction=1 * -XX:CMSInitiatingOccupancyFraction=75 -XX:+CMSScavengeBeforeRemark -XX:+HeapDumpBeforeFullGC * -XX:+HeapDumpOnOutOfMemoryError -XX:+CMSClassUnloadingEnabled -XX:+DisableExplicitGC -verbose:gc * -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:HeapDumpPath=/home/kcp/test/java-heapdump.hprof * -Xloggc:/home/kcp/test/gc.log */ +@Slf4j public class TriggerFullGCForCMS { - final static Runnable eatMem = () -> { - List temp = new ArrayList<>(); - for (int i = 0; i < 60; i++) { - try { - TimeUnit.MILLISECONDS.sleep(30); - } catch (InterruptedException e) { - e.printStackTrace(); - } - temp.add(new byte[1024 * 1024]); - System.out.println(Thread.currentThread().getName() + " " + i); - } - }; + final static Runnable eatMem = () -> { + List temp = new ArrayList<>(); + for (int i = 0; i < 60; i++) { + try { + TimeUnit.MILLISECONDS.sleep(30); + } catch (InterruptedException e) { + log.error("", e); + } + temp.add(new byte[1024 * 1024]); + System.out.println(Thread.currentThread().getName() + " " + i); + } + }; - public static void main(String[] args) throws InterruptedException { - final ExecutorService pool = Executors.newFixedThreadPool(4); - List live = new ArrayList<>(); - while (true) { - pool.execute(eatMem); - live.add(new byte[1024 * 1024]); - TimeUnit.SECONDS.sleep(1); + public static void main(String[] args) throws InterruptedException { + final ExecutorService pool = Executors.newFixedThreadPool(4); + List live = new ArrayList<>(); + while (true) { + pool.execute(eatMem); + live.add(new byte[1024 * 1024]); + TimeUnit.SECONDS.sleep(1); + } } - } } diff --git a/class/src/test/java/com/github/kuangcp/reflects/InvokeWithInheritParamTest.java b/class/src/test/java/com/github/kuangcp/reflects/InvokeWithInheritParamTest.java index 696e212c..afa90c18 100644 --- a/class/src/test/java/com/github/kuangcp/reflects/InvokeWithInheritParamTest.java +++ b/class/src/test/java/com/github/kuangcp/reflects/InvokeWithInheritParamTest.java @@ -63,7 +63,7 @@ public void testInvoke() { runParam.setScore(108); verifyInvoke(method, runParam, true); } catch (NoSuchMethodException e) { - e.printStackTrace(); + log.error("", e); } } @@ -74,7 +74,7 @@ public void testInvokeBySuper() { Method method = Logic.class.getDeclaredMethod("isFailed", CommonParam.class); verifySuite(method); } catch (NoSuchMethodException e) { - e.printStackTrace(); + log.error("", e); } } @@ -131,7 +131,7 @@ private void verifyInvoke(Method method, CommonParam param, boolean expect) { try { result = (boolean) method.invoke(new Logic(), param); } catch (IllegalAccessException | InvocationTargetException e) { - e.printStackTrace(); + log.error("", e); } assertThat(result, equalTo(expect)); } diff --git a/concurrency/src/main/java/com/github/kuangcp/list/ElementArrayList.java b/concurrency/src/main/java/com/github/kuangcp/list/ElementArrayList.java index b872ff32..f2985a25 100644 --- a/concurrency/src/main/java/com/github/kuangcp/list/ElementArrayList.java +++ b/concurrency/src/main/java/com/github/kuangcp/list/ElementArrayList.java @@ -1,5 +1,7 @@ package com.github.kuangcp.list; +import lombok.extern.slf4j.Slf4j; + import java.util.ArrayList; import java.util.Iterator; import java.util.concurrent.locks.ReentrantLock; @@ -9,43 +11,44 @@ * * @author kuangcp on 3/4/19-8:01 AM */ -class ElementArrayList { - - private final ArrayList elements; - private final ReentrantLock lock; - private final String name; - private Iterator it; - - ElementArrayList(ArrayList elements, ReentrantLock lock, String name) { - this.elements = elements; - this.lock = lock; - this.name = name; - } - - void addElement(Element ele) { - elements.add(ele); - } - - void prep() { - it = elements.iterator();//设置迭代器 - } - - void listElement() { - lock.lock(); // 进行迭代的时候进行 锁定 , - try { +@Slf4j +public class ElementArrayList { + + private final ArrayList elements; + private final ReentrantLock lock; + private final String name; + private Iterator it; + + ElementArrayList(ArrayList elements, ReentrantLock lock, String name) { + this.elements = elements; + this.lock = lock; + this.name = name; + } + + void addElement(Element ele) { + elements.add(ele); + } + + void prep() { + it = elements.iterator();//设置迭代器 + } + + void listElement() { + lock.lock(); // 进行迭代的时候进行 锁定 , + try { // Thread.sleep(10); - if (it != null) { - System.out.print(name + ": "); - while (it.hasNext()) { - Element element = it.next(); - System.out.print(element + ", "); + if (it != null) { + System.out.print(name + ": "); + while (it.hasNext()) { + Element element = it.next(); + System.out.print(element + ", "); + } + System.out.println(); + } + } catch (Exception e) { + log.error("", e); + } finally { + lock.unlock(); } - System.out.println(); - } - } catch (Exception e) { - e.printStackTrace(); - } finally { - lock.unlock(); } - } } diff --git a/concurrency/src/main/java/com/github/kuangcp/volatiles/NeverStopThread.java b/concurrency/src/main/java/com/github/kuangcp/volatiles/NeverStopThread.java index a820d428..e706ce62 100644 --- a/concurrency/src/main/java/com/github/kuangcp/volatiles/NeverStopThread.java +++ b/concurrency/src/main/java/com/github/kuangcp/volatiles/NeverStopThread.java @@ -33,7 +33,7 @@ void normalStopWithSleep() { try { TimeUnit.MILLISECONDS.sleep(300); } catch (InterruptedException e) { - e.printStackTrace(); + log.error("", e); } log.info("run with sleep"); } diff --git a/concurrency/src/main/java/situation/timoutpool/TimeoutExecPool.java b/concurrency/src/main/java/situation/timoutpool/TimeoutExecPool.java index 39863f9b..6be4c83d 100644 --- a/concurrency/src/main/java/situation/timoutpool/TimeoutExecPool.java +++ b/concurrency/src/main/java/situation/timoutpool/TimeoutExecPool.java @@ -43,7 +43,7 @@ public List execute(List

params, Function handler) { try { Thread.sleep(200); } catch (InterruptedException e) { - e.printStackTrace(); + log.error("", e); } // log.info("{} ", pool.getQueue().size()); if (pool.getQueue().size() == 0) { diff --git a/concurrency/src/main/java/situation/timoutpool/TimeoutFuture.java b/concurrency/src/main/java/situation/timoutpool/TimeoutFuture.java index a5b0f977..afc7f0e3 100644 --- a/concurrency/src/main/java/situation/timoutpool/TimeoutFuture.java +++ b/concurrency/src/main/java/situation/timoutpool/TimeoutFuture.java @@ -27,7 +27,7 @@ public Result execute(Param param, long timeout, TimeUnit timeUnit) { try { TimeUnit.MILLISECONDS.sleep(ThreadLocalRandom.current().nextInt(600) + 200); } catch (InterruptedException e) { - e.printStackTrace(); + log.error("", e); } // log.info("tmpParam={}", tmpParam); result.getDataList().add(tmpParam.toString()); diff --git a/concurrency/src/main/java/thread/order/Task.java b/concurrency/src/main/java/thread/order/Task.java index 49158f16..20406b26 100644 --- a/concurrency/src/main/java/thread/order/Task.java +++ b/concurrency/src/main/java/thread/order/Task.java @@ -1,8 +1,9 @@ package thread.order; +import lombok.extern.slf4j.Slf4j; + import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; -import lombok.extern.slf4j.Slf4j; /** * Created by https://github.com/kuangcp on 18-1-18 下午1:53 @@ -13,34 +14,34 @@ @Slf4j public class Task implements Runnable { - private String target; - private int order; - private AtomicInteger count; //利用原子递增控制线程准入顺序。 - - public Task(String target, int order, AtomicInteger count) { - this.target = target; - this.order = order; - this.count = count; - } - - @Override - public void run() { - while (true) { - int count = this.count.get(); - if (count % 3 != order) { - continue; - } - - // 使用 log 会乱, 使用下面的逻辑也不能完全保障顺序 - this.count.incrementAndGet(); + private String target; + private int order; + private AtomicInteger count; //利用原子递增控制线程准入顺序。 + + public Task(String target, int order, AtomicInteger count) { + this.target = target; + this.order = order; + this.count = count; + } + + @Override + public void run() { + while (true) { + int count = this.count.get(); + if (count % 3 != order) { + continue; + } + + // 使用 log 会乱, 使用下面的逻辑也不能完全保障顺序 + this.count.incrementAndGet(); // log.info("target={} order={} count={}", target, order, count); - System.out.println(target + " " + order + " " + count); + System.out.println(target + " " + order + " " + count); - try { - TimeUnit.SECONDS.sleep(1); - } catch (InterruptedException e) { - e.printStackTrace(); - } + try { + TimeUnit.SECONDS.sleep(1); + } catch (InterruptedException e) { + log.error("", e); + } + } } - } } diff --git a/concurrency/src/main/java/thread/order/TaskWithVolatile.java b/concurrency/src/main/java/thread/order/TaskWithVolatile.java index 0b9efc42..94671fe1 100644 --- a/concurrency/src/main/java/thread/order/TaskWithVolatile.java +++ b/concurrency/src/main/java/thread/order/TaskWithVolatile.java @@ -6,11 +6,11 @@ * Created by https://github.com/kuangcp on 18-1-18 下午1:57 * 使用了volatile关键字。让每个线程都能拿到最新的count的值,当其中一个线程执行++操作后, * 其他两个线程就会拿到最新的值,并检查是否符合准入条件。 - * + *

* volatile不是线程安全的。而且两者没有任何关系。volatile变量不在用户线程保存副本,因此对所有线程都能提供最新的值。 * 但试想,如果多个线程同时并发更新这个变量,其结果也是显而易见的,最后一次的更新会覆盖前面所有更新,从而导致线程不安全。 * 该示例一次只有一个线程满足准入条件,因此不存在对变量的并发更新。 - * + *

* volatile(易失性)的值是最新的与线程安全完全是不相干的,所以不要误用volatile实现并发控制。 * 相关博客 http://blog.csdn.net/yuechang5/article/details/79081697 * @@ -19,31 +19,31 @@ @Slf4j public class TaskWithVolatile implements Runnable { - private String target; - private static volatile int count = 0; - private int order; + private String target; + private static volatile int count = 0; + private int order; - public TaskWithVolatile(String target, int order) { - this.order = order; - this.target = target; - } + public TaskWithVolatile(String target, int order) { + this.order = order; + this.target = target; + } - @Override - public void run() { - while (true) { - if (count % 3 != order) { - continue; - } + @Override + public void run() { + while (true) { + if (count % 3 != order) { + continue; + } - log.info("target={} order={} count={}", target, order, count); + log.info("target={} order={} count={}", target, order, count); // System.out.println(target + " " + order + " " + count); - count += 1; + count += 1; - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - e.printStackTrace(); - } + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + log.error("", e); + } + } } - } } diff --git a/concurrency/src/main/java/thread/tryone/Thread1.java b/concurrency/src/main/java/thread/tryone/Thread1.java index 49a3068f..1532c19b 100644 --- a/concurrency/src/main/java/thread/tryone/Thread1.java +++ b/concurrency/src/main/java/thread/tryone/Thread1.java @@ -1,70 +1,74 @@ /** * 演示两种 - * 如果通过 继承Thread类 来开发线程 - * 实现Runnable接口 来开发线程 - * + * 如果通过 继承Thread类 来开发线程 + * 实现Runnable接口 来开发线程 */ package thread.tryone; +import lombok.extern.slf4j.Slf4j; + public class Thread1 { - public static void main(String[] args) { + public static void main(String[] args) { + + //创建一个Cat对象 + Cat cat = new Cat(); + //启动线程 + cat.start(); + + //实现接口的方式来启动线程 + Dog dog = new Dog(); + Thread t = new Thread(dog); + t.start(); - //创建一个Cat对象 - Cat cat = new Cat(); - //启动线程 - cat.start(); - - //实现接口的方式来启动线程 - Dog dog = new Dog(); - Thread t = new Thread(dog); - t.start(); - - } + } } +@Slf4j //继承线程类 class Cat extends Thread { - int times = 0; - - public void run (){ - while(true){ - //休眠一秒 ,线程就会进入Blocked状态,并释放资源 - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - times++; - System.out.println("Cat hello world !"+times); - if (times==10){ - break;//这里跳出while循环,就会结束这个线程 - } - } - } + int times = 0; + + public void run() { + while (true) { + //休眠一秒 ,线程就会进入Blocked状态,并释放资源 + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + log.error("", e); + } + times++; + System.out.println("Cat hello world !" + times); + if (times == 10) { + break;//这里跳出while循环,就会结束这个线程 + } + } + } } +@Slf4j //实现接口 class Dog implements Runnable { - int times = 0; - public void run() { - while(true){ - //休眠一秒 ,线程就会进入Blocked状态,并释放资源 - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - times++; - System.out.println("Dog Hello World !"+times); - if (times==10){ - break;//这里跳出while循环,就会结束这个线程 - } - } - } - + int times = 0; + + public void run() { + while (true) { + //休眠一秒 ,线程就会进入Blocked状态,并释放资源 + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + log.error("", e); + } + times++; + System.out.println("Dog Hello World !" + times); + if (times == 10) { + break;//这里跳出while循环,就会结束这个线程 + } + } + } + } \ No newline at end of file diff --git a/concurrency/src/main/java/thread/tryone/Thread2.java b/concurrency/src/main/java/thread/tryone/Thread2.java index a848aabe..6be57aed 100644 --- a/concurrency/src/main/java/thread/tryone/Thread2.java +++ b/concurrency/src/main/java/thread/tryone/Thread2.java @@ -1,74 +1,82 @@ package thread.tryone; + +import lombok.extern.slf4j.Slf4j; + /** * 两个线程同时运作 - * @author lenovo * + * @author lenovo */ public class Thread2 { - public static void main(String[] a){ - T1 t1 = new T1(10); - T2 t2 = new T2(10); - - Thread t = new Thread(t1); - Thread s = new Thread(t2); - - t.start(); - s.start(); - } + public static void main(String[] a) { + T1 t1 = new T1(10); + T2 t2 = new T2(10); + + Thread t = new Thread(t1); + Thread s = new Thread(t2); + + t.start(); + s.start(); + } } +@Slf4j //最好是用实现接口来写 方便以后扩展 class T1 implements Runnable { - //做累加的功能 - int n=0; - int res =0; - int times =0; - public T1(int n){ - this.n = n; - } - public void run() { - while (true){ - try { - Thread.sleep(1000); - - }catch (Exception e ){ - e.printStackTrace(); - } - res+=(++times); - System.out.println("当前结果是:"+res); - if (times == n){ - System.out.println("最后的结果是:"+res); - break; - } - } - } - + //做累加的功能 + int n = 0; + int res = 0; + int times = 0; + + public T1(int n) { + this.n = n; + } + + public void run() { + while (true) { + try { + Thread.sleep(1000); + + } catch (Exception e) { + log.error("", e); + } + res += (++times); + System.out.println("当前结果是:" + res); + if (times == n) { + System.out.println("最后的结果是:" + res); + break; + } + } + } + } +@Slf4j class T2 implements Runnable { - - int n=0; - int times=0; - - public T2(int n){ - this.n = n; - } - public void run (){ - while (true){ - try { - Thread.sleep(400); - } catch (InterruptedException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - - times++; - System.out.println("我是一个线程,在输出第"+times+"hello world"); - if (times == n) break; - } - } + + int n = 0; + int times = 0; + + public T2(int n) { + this.n = n; + } + + public void run() { + while (true) { + try { + Thread.sleep(400); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + log.error("", e); + } + + times++; + System.out.println("我是一个线程,在输出第" + times + "hello world"); + if (times == n) break; + } + } } diff --git a/concurrency/src/main/java/thread/tryone/Thread4.java b/concurrency/src/main/java/thread/tryone/Thread4.java index cae3aa92..c4df2519 100644 --- a/concurrency/src/main/java/thread/tryone/Thread4.java +++ b/concurrency/src/main/java/thread/tryone/Thread4.java @@ -1,77 +1,80 @@ +package thread.tryone; + +import lombok.extern.slf4j.Slf4j; + /** * 如果有多个线程因等待同一个对象的标志而处于阻塞状态时,当该对象的标志位(对象锁/文件锁)恢复到1状态时 * 只会有一个线程能够进入同步代码执行,其他的线程仍然处于阻塞的状态 - * 俩线程 并发 我读你 你读我 互斥 - * 就会容易发生死锁 要尽量避免 - * + * 俩线程 并发 我读你 你读我 互斥 + * 就会容易发生死锁 要尽量避免 */ -package thread.tryone; - +@Slf4j public class Thread4 { - public static void main(String[] args) { - // TODO Auto-generated method stub + public static void main(String[] args) { + // TODO Auto-generated method stub + + //定义三个售票窗口 其实不是三个对象,这样的话初始化这里都是2000张,每个窗口都当成一个线程来理解就好了 - //定义三个售票窗口 其实不是三个对象,这样的话初始化这里都是2000张,每个窗口都当成一个线程来理解就好了 - - TicketWindow tw1 = new TicketWindow(); + TicketWindow tw1 = new TicketWindow(); // TicketWindow tw2 = new TicketWindow(); // TicketWindow tw3 = new TicketWindow(); - - //把一个对象封装入三个线程里 和三个对象分别装的区别? - //就是说,三个线程都在各自处理各自的对象,并没有并发,同步的问题 - //可是一旦有静态的(共享性的成员)就会有并发的那些问题,而且出现的问题还都是随机的 - Thread t1 = new Thread(tw1); - Thread t2 = new Thread(tw1); - Thread t3 = new Thread(tw1); - - t1.start(); - t2.start(); - t3.start(); - } + + //把一个对象封装入三个线程里 和三个对象分别装的区别? + //就是说,三个线程都在各自处理各自的对象,并没有并发,同步的问题 + //可是一旦有静态的(共享性的成员)就会有并发的那些问题,而且出现的问题还都是随机的 + Thread t1 = new Thread(tw1); + Thread t2 = new Thread(tw1); + Thread t3 = new Thread(tw1); + + t1.start(); + t2.start(); + t3.start(); + } } +@Slf4j //售票窗口 class TicketWindow implements Runnable { - //一共两千张票 - private static int nums = 200; - @SuppressWarnings("unused") - private Dog3 dog = new Dog3(); - - - public void run (){ - - while (true){ - - //保证其原子性 (同步代码块) - /** 不用同步的锁 - * 如果睡眠时间够长,使用三个对象分别封装 就不会容易出现并发的问题 - * 但是时间一旦短了呢 就会有明显的错误*/ - synchronized (this) { + //一共两千张票 + private static int nums = 200; + @SuppressWarnings("unused") + private Dog3 dog = new Dog3(); + + + public void run() { + + while (true) { + + //保证其原子性 (同步代码块) + /** 不用同步的锁 + * 如果睡眠时间够长,使用三个对象分别封装 就不会容易出现并发的问题 + * 但是时间一旦短了呢 就会有明显的错误*/ + synchronized (this) { // synchronized (dog) 括号里可以放任意的对象只是利用对象的一个对象锁功能, // 相当于用这个对象做一个标志性的状态信息, 0 1 (标志位) - //先判断是否还有票 - if (nums >0){ - //显示售票信息、 - - System.out.println(""+ Thread.currentThread().getName()+"正在售出第 "+nums+"张票"); - try { - Thread.sleep(100); //出票速度 一秒一张 - } catch (InterruptedException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - nums--; - }else { - //售票结束 - break; - } - } - } - } + //先判断是否还有票 + if (nums > 0) { + //显示售票信息、 + + System.out.println("" + Thread.currentThread().getName() + "正在售出第 " + nums + "张票"); + try { + Thread.sleep(100); //出票速度 一秒一张 + } catch (InterruptedException e) { + // TODO Auto-generated catch block + log.error("", e); + } + nums--; + } else { + //售票结束 + break; + } + } + } + } } -class Dog3{ - +class Dog3 { + } diff --git a/concurrency/src/test/java/com/github/kuangcp/latch/CountDownLatchTest.java b/concurrency/src/test/java/com/github/kuangcp/latch/CountDownLatchTest.java index 82742add..1d81ec79 100644 --- a/concurrency/src/test/java/com/github/kuangcp/latch/CountDownLatchTest.java +++ b/concurrency/src/test/java/com/github/kuangcp/latch/CountDownLatchTest.java @@ -27,7 +27,7 @@ public void testFirstUse() throws Exception { log.info("fin: {}", finalI); latch.countDown(); } catch (InterruptedException e) { - e.printStackTrace(); + log.error("", e); } }); } diff --git a/concurrency/src/test/java/thread/NotifyAndWaitTest.java b/concurrency/src/test/java/thread/NotifyAndWaitTest.java index 2dd07d1c..39281138 100644 --- a/concurrency/src/test/java/thread/NotifyAndWaitTest.java +++ b/concurrency/src/test/java/thread/NotifyAndWaitTest.java @@ -49,7 +49,7 @@ public void testLoop() throws InterruptedException { Thread.sleep(4000); ele.back(System.currentTimeMillis() + ""); } catch (InterruptedException e) { - e.printStackTrace(); + log.error("", e); } }).start(); } diff --git a/flink/src/main/java/com/github/kuangcp/hi/SimpleSource.java b/flink/src/main/java/com/github/kuangcp/hi/SimpleSource.java index 23afafd2..91a86288 100644 --- a/flink/src/main/java/com/github/kuangcp/hi/SimpleSource.java +++ b/flink/src/main/java/com/github/kuangcp/hi/SimpleSource.java @@ -46,7 +46,7 @@ private void delayTime() { try { TimeUnit.MILLISECONDS.sleep(300); } catch (InterruptedException e) { - e.printStackTrace(); + log.error("", e); } } } diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/Audio.java b/gui/src/main/java/com/github/kuangcp/tank/util/Audio.java index ca385af9..e00e7ece 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/util/Audio.java +++ b/gui/src/main/java/com/github/kuangcp/tank/util/Audio.java @@ -53,7 +53,7 @@ public void run() { auline = (SourceDataLine) AudioSystem.getLine(info); auline.open(format); } catch (Exception e) { - e.printStackTrace(); + log.error("", e); return; } diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/Saved.java b/gui/src/main/java/com/github/kuangcp/tank/util/Saved.java index 24683c58..975cfe8a 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/util/Saved.java +++ b/gui/src/main/java/com/github/kuangcp/tank/util/Saved.java @@ -4,6 +4,7 @@ import com.github.kuangcp.tank.domain.EnemyTank; import com.github.kuangcp.tank.domain.Hero; import com.github.kuangcp.tank.domain.Iron; +import lombok.extern.slf4j.Slf4j; import java.io.BufferedReader; import java.io.BufferedWriter; @@ -29,6 +30,7 @@ *

* 用数据库实现了续上局及其存盘退出,比文件的操作简单多了。。。 */ +@Slf4j public class Saved { private Hero hero; private List ets; @@ -121,7 +123,7 @@ public void savedAll() { bw.flush();//一定要清除缓存./src/RE/File.txt } catch (Exception e) { - e.printStackTrace(); + log.error("", e); } finally { try { //后打开,先关闭 @@ -214,7 +216,7 @@ public void readAll() { //读取砖块 } } catch (Exception e) { - e.printStackTrace(); + log.error("", e); System.out.println("读取数据有异常"); } finally { try { @@ -367,7 +369,7 @@ public void saveDataBase() { else System.out.println("Hero 保存失败"); // } catch (Exception e) { - e.printStackTrace(); + log.error("", e); } finally { //关闭资源 try { @@ -379,7 +381,7 @@ public void saveDataBase() { cn.close(); } catch (SQLException e) { // TODO Auto-generated catch block - e.printStackTrace(); + log.error("", e); } } diff --git a/gui/src/test/java/com/github/kuangcp/tank/v3/PlayStageMgrTest.java b/gui/src/test/java/com/github/kuangcp/tank/v3/PlayStageMgrTest.java index f2f5893a..804f922d 100644 --- a/gui/src/test/java/com/github/kuangcp/tank/v3/PlayStageMgrTest.java +++ b/gui/src/test/java/com/github/kuangcp/tank/v3/PlayStageMgrTest.java @@ -30,7 +30,7 @@ // try { // TimeUnit.SECONDS.sleep(3); // } catch (InterruptedException e) { -// e.printStackTrace(); +// log.error("", e); // } // System.out.println("Inside fiber coroutine..." + finalI); // latch.countDown(); diff --git a/hadoop/src/test/java/com/github/kuangcp/hdfs/hi/GeneralFileActionDemoTest.java b/hadoop/src/test/java/com/github/kuangcp/hdfs/hi/GeneralFileActionDemoTest.java index 454788ae..34a314c4 100644 --- a/hadoop/src/test/java/com/github/kuangcp/hdfs/hi/GeneralFileActionDemoTest.java +++ b/hadoop/src/test/java/com/github/kuangcp/hdfs/hi/GeneralFileActionDemoTest.java @@ -36,7 +36,7 @@ public void testList() throws IOException { try { GeneralFileActionDemo.deleteByURL(v); } catch (IOException e) { - e.printStackTrace(); + log.error("", e); } }); } diff --git a/io/src/main/java/com/github/kuangcp/file/CopyFile.java b/io/src/main/java/com/github/kuangcp/file/CopyFile.java index b724a106..a4ae8205 100644 --- a/io/src/main/java/com/github/kuangcp/file/CopyFile.java +++ b/io/src/main/java/com/github/kuangcp/file/CopyFile.java @@ -84,7 +84,7 @@ void copyFileByByte(String from, String dest) { outputStream.write(buffer); } } catch (Exception e) { - e.printStackTrace(); + log.error("", e); } finally { try { ResourceTool.close(inputStream, outputStream); @@ -115,7 +115,7 @@ void copyFileByChar(String from, String dest) { } } catch (Exception e) { - e.printStackTrace(); + log.error("", e); } finally { try { ResourceTool.close(fileReader, fileWriter); diff --git a/java8/src/test/java/com/github/kuangcp/future/CompletableFutureTest.java b/java8/src/test/java/com/github/kuangcp/future/CompletableFutureTest.java index 0d2f5490..c5ce6d51 100644 --- a/java8/src/test/java/com/github/kuangcp/future/CompletableFutureTest.java +++ b/java8/src/test/java/com/github/kuangcp/future/CompletableFutureTest.java @@ -24,7 +24,7 @@ public void testAsync() throws Exception { try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { - e.printStackTrace(); + log.error("", e); } log.info("temp"); }); @@ -48,7 +48,7 @@ public void testAsyncPoolSize() throws Exception { try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { - e.printStackTrace(); + log.error("", e); } log.info("temp"); }); diff --git a/java8/src/test/java/com/github/kuangcp/stream/map/MapTest.java b/java8/src/test/java/com/github/kuangcp/stream/map/MapTest.java index 6fb06c52..82c0a0ff 100644 --- a/java8/src/test/java/com/github/kuangcp/stream/map/MapTest.java +++ b/java8/src/test/java/com/github/kuangcp/stream/map/MapTest.java @@ -28,7 +28,7 @@ R convert(I input, Class target) { try { return target.newInstance(); } catch (InstantiationException | IllegalAccessException e) { - e.printStackTrace(); + log.error("", e); } return null; } diff --git a/mybatis/src/test/java/com/github/kuangcp/simple/order/dao/OrderDaoSpringBootTest.java b/mybatis/src/test/java/com/github/kuangcp/simple/order/dao/OrderDaoSpringBootTest.java index faf23b8d..d991e534 100644 --- a/mybatis/src/test/java/com/github/kuangcp/simple/order/dao/OrderDaoSpringBootTest.java +++ b/mybatis/src/test/java/com/github/kuangcp/simple/order/dao/OrderDaoSpringBootTest.java @@ -56,7 +56,7 @@ public void testBulkInsert() throws InterruptedException { v.start(); v.join(); } catch (InterruptedException e) { - e.printStackTrace(); + log.error("", e); } }); diff --git a/mybatis/src/test/java/com/github/kuangcp/stream/CursorSessionSpringBootTest.java b/mybatis/src/test/java/com/github/kuangcp/stream/CursorSessionSpringBootTest.java index 3aa235c5..421828c4 100644 --- a/mybatis/src/test/java/com/github/kuangcp/stream/CursorSessionSpringBootTest.java +++ b/mybatis/src/test/java/com/github/kuangcp/stream/CursorSessionSpringBootTest.java @@ -102,7 +102,7 @@ public void testStreamQuery() throws Exception { // try { // TimeUnit.NANOSECONDS.sleep(100); // } catch (InterruptedException e) { -// e.printStackTrace(); +// log.error("", e); // } result.add(val); }); diff --git a/network/src/main/java/com/github/kuangcp/bio/onechatone/Client.java b/network/src/main/java/com/github/kuangcp/bio/onechatone/Client.java index d2b6b9d6..981423ee 100644 --- a/network/src/main/java/com/github/kuangcp/bio/onechatone/Client.java +++ b/network/src/main/java/com/github/kuangcp/bio/onechatone/Client.java @@ -1,17 +1,20 @@ package com.github.kuangcp.bio.onechatone; import com.github.kuangcp.io.ResourceTool; +import lombok.extern.slf4j.Slf4j; + +import javax.swing.*; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintStream; import java.net.Socket; import java.net.UnknownHostException; -import javax.swing.JOptionPane; /** * Created by Myth on 2017/4/3 0003 */ +@Slf4j public class Client { private Socket socket; @@ -88,7 +91,7 @@ private void closeAllResources() { ResourceTool.close(keyIn, brServer, ps, socket); } catch (Exception e) { - e.printStackTrace(); + log.error("", e); } } diff --git a/network/src/main/java/com/github/kuangcp/bio/onechatone/ClientThread.java b/network/src/main/java/com/github/kuangcp/bio/onechatone/ClientThread.java index c02fb834..e6ea6243 100644 --- a/network/src/main/java/com/github/kuangcp/bio/onechatone/ClientThread.java +++ b/network/src/main/java/com/github/kuangcp/bio/onechatone/ClientThread.java @@ -1,19 +1,24 @@ package com.github.kuangcp.bio.onechatone; +import lombok.extern.slf4j.Slf4j; + import java.io.BufferedReader; /** * Created by Myth on 2017/4/3 0003 */ -public class ClientThread extends Thread{ - BufferedReader br = null; - public ClientThread(BufferedReader br){ +@Slf4j +public class ClientThread extends Thread { + final BufferedReader br; + + public ClientThread(BufferedReader br) { this.br = br; } - public void run(){ - try{ - String line = null; - while((line=br.readLine())!=null){ + + public void run() { + try { + String line; + while ((line = br.readLine()) != null) { //显示服务器读取到的内容 System.out.println(line); /* @@ -23,13 +28,13 @@ public void run(){ 需求总是提不尽的 */ } - }catch (Exception r){ - r.printStackTrace(); - }finally { - try{ - if(br!=null) br.close(); - }catch (Exception e){ - e.printStackTrace(); + } catch (Exception r) { + log.error("", r); + } finally { + try { + if (br != null) br.close(); + } catch (Exception e) { + log.error("", e); } } } diff --git a/network/src/main/java/com/github/kuangcp/bio/onechatone/ServerThread.java b/network/src/main/java/com/github/kuangcp/bio/onechatone/ServerThread.java index d3a4a986..de0d526a 100644 --- a/network/src/main/java/com/github/kuangcp/bio/onechatone/ServerThread.java +++ b/network/src/main/java/com/github/kuangcp/bio/onechatone/ServerThread.java @@ -1,5 +1,7 @@ package com.github.kuangcp.bio.onechatone; +import lombok.extern.slf4j.Slf4j; + import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.PrintStream; @@ -8,6 +10,7 @@ /** * Created by Myth on 2017/4/3 0003 */ +@Slf4j public class ServerThread extends Thread{ private Socket socket; BufferedReader br = null; @@ -64,7 +67,7 @@ public void run(){ }catch (Exception es){ es.printStackTrace(); } - e.printStackTrace(); + log.error("", e); } } diff --git a/network/src/main/java/com/github/kuangcp/port/LogicThread.java b/network/src/main/java/com/github/kuangcp/port/LogicThread.java index 49f14104..dee94535 100644 --- a/network/src/main/java/com/github/kuangcp/port/LogicThread.java +++ b/network/src/main/java/com/github/kuangcp/port/LogicThread.java @@ -1,6 +1,8 @@ package com.github.kuangcp.port; import com.github.kuangcp.io.ResourceTool; +import lombok.extern.slf4j.Slf4j; + import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -9,53 +11,54 @@ /** * 服务器端逻辑线程 */ +@Slf4j public class LogicThread extends Thread { - private Socket socket; - private InputStream is; - private OutputStream os; + private Socket socket; + private InputStream is; + private OutputStream os; - public LogicThread(Socket socket) { - this.socket = socket; - start(); //启动线程 - } + public LogicThread(Socket socket) { + this.socket = socket; + start(); //启动线程 + } - public void run() { - byte[] b = new byte[1024]; - try { - //初始化流 - os = socket.getOutputStream(); - is = socket.getInputStream(); - for (int i = 0; i < 3; i++) { - //读取数据 - int n = is.read(b); - //逻辑处理 - byte[] response = logic(b, 0, n); - //反馈数据 - os.write(response); - } - } catch (Exception e) { - e.printStackTrace(); - } finally { - try { - ResourceTool.close(os, is, socket); - } catch (IOException e) { - e.printStackTrace(); - } + public void run() { + byte[] b = new byte[1024]; + try { + //初始化流 + os = socket.getOutputStream(); + is = socket.getInputStream(); + for (int i = 0; i < 3; i++) { + //读取数据 + int n = is.read(b); + //逻辑处理 + byte[] response = logic(b, 0, n); + //反馈数据 + os.write(response); + } + } catch (Exception e) { + log.error("", e); + } finally { + try { + ResourceTool.close(os, is, socket); + } catch (IOException e) { + log.error("", e); + } + } } - } - /** - * 逻辑处理方法,实现echo逻辑 - * - * @param b 客户端发送数据缓冲区 - * @param off 起始下标 - * @param len 有效数据长度 - */ - private byte[] logic(byte[] b, int off, int len) { - byte[] response = new byte[len]; - //将有效数据拷贝到数组response中 - System.arraycopy(b, 0, response, 0, len); - return response; - } + /** + * 逻辑处理方法,实现echo逻辑 + * + * @param b 客户端发送数据缓冲区 + * @param off 起始下标 + * @param len 有效数据长度 + */ + private byte[] logic(byte[] b, int off, int len) { + byte[] response = new byte[len]; + //将有效数据拷贝到数组response中 + System.arraycopy(b, 0, response, 0, len); + return response; + } } \ No newline at end of file diff --git a/network/src/main/java/com/github/kuangcp/port/LogicThreadTest.java b/network/src/main/java/com/github/kuangcp/port/LogicThreadTest.java index c20db1d3..ea661544 100644 --- a/network/src/main/java/com/github/kuangcp/port/LogicThreadTest.java +++ b/network/src/main/java/com/github/kuangcp/port/LogicThreadTest.java @@ -1,9 +1,12 @@ package com.github.kuangcp.port; +import lombok.extern.slf4j.Slf4j; + import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; +@Slf4j public class LogicThreadTest { public static void main(String[] args) { @@ -15,7 +18,7 @@ public static void main(String[] args) { LogicThread t = new LogicThread(socket); t.start(); } catch (IOException e) { - e.printStackTrace(); + log.error("", e); } } diff --git a/network/src/main/java/com/github/kuangcp/runable/GreetingClient.java b/network/src/main/java/com/github/kuangcp/runable/GreetingClient.java index 3f556776..e0cfd8ae 100644 --- a/network/src/main/java/com/github/kuangcp/runable/GreetingClient.java +++ b/network/src/main/java/com/github/kuangcp/runable/GreetingClient.java @@ -1,20 +1,17 @@ package com.github.kuangcp.runable; -import java.io.BufferedReader; -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.net.Socket; -import java.util.Scanner; +import lombok.extern.slf4j.Slf4j; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.*; +import java.net.Socket; +import java.util.Scanner; + /** * 客户端 */ +@Slf4j public class GreetingClient { private static Logger logger = LoggerFactory.getLogger(GreetingClient.class); @@ -58,7 +55,7 @@ public static void main(String[] s) { client.close(); } catch (IOException e) { - e.printStackTrace(); + log.error("", e); } } @@ -71,7 +68,7 @@ public static void In() { try { logger.info(sin.readLine()); } catch (IOException e) { - e.printStackTrace(); + log.error("", e); } } } diff --git a/network/src/main/java/com/github/kuangcp/runable/GreetingServer.java b/network/src/main/java/com/github/kuangcp/runable/GreetingServer.java index c99b191c..20f9761e 100644 --- a/network/src/main/java/com/github/kuangcp/runable/GreetingServer.java +++ b/network/src/main/java/com/github/kuangcp/runable/GreetingServer.java @@ -1,5 +1,7 @@ package com.github.kuangcp.runable; +import lombok.extern.slf4j.Slf4j; + import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; @@ -7,18 +9,16 @@ import java.net.Socket; import java.net.SocketTimeoutException; import java.util.Scanner; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** * 服务器端 * 输入线程阻塞的原因,不能同时收发,怎么处理?多线程? * 服务器不应该发出,而是做中转站 */ +@Slf4j public class GreetingServer extends Thread { private ServerSocket serverSocket; - private static Logger logger = LoggerFactory.getLogger(GreetingServer.class); public GreetingServer(int port) throws IOException { serverSocket = new ServerSocket(port); @@ -28,19 +28,19 @@ public GreetingServer(int port) throws IOException { public void run() { while (true) { try { - logger.info("##### Waiting for client on port " + serverSocket.getLocalPort() + "..."); + log.info("##### Waiting for client on port " + serverSocket.getLocalPort() + "..."); Socket server = serverSocket.accept(); Scanner scanner = new Scanner(System.in); - logger.info("Just connected to " + server.getRemoteSocketAddress()); + log.info("Just connected to " + server.getRemoteSocketAddress()); DataInputStream in = new DataInputStream(server.getInputStream()); - logger.info("接收到的:" + in.readUTF()); + log.info("接收到的:" + in.readUTF()); // 对客户端发出的消息 DataOutputStream out = new DataOutputStream(server.getOutputStream()); while (true) { String temp = scanner.nextLine(); - logger.info("input:" + temp); + log.info("input:" + temp); out.writeUTF(temp); if ("90".equals(temp)) { break; @@ -52,10 +52,10 @@ public void run() { server.close(); } catch (SocketTimeoutException s) { - logger.info("Socket timed out!"); + log.info("Socket timed out!"); break; } catch (IOException e) { - e.printStackTrace(); + log.error("", e); break; } } @@ -68,7 +68,7 @@ public static void main(String[] args) { Thread t = new GreetingServer(port); t.start(); } catch (IOException e) { - e.printStackTrace(); + log.error("", e); } } } diff --git a/network/src/test/java/com/github/kuangcp/runable/SlowRequestTest.java b/network/src/test/java/com/github/kuangcp/runable/SlowRequestTest.java index d92476eb..1c3f294b 100644 --- a/network/src/test/java/com/github/kuangcp/runable/SlowRequestTest.java +++ b/network/src/test/java/com/github/kuangcp/runable/SlowRequestTest.java @@ -27,7 +27,7 @@ public void testSlowSingleConnection() throws Exception { assert result.isPresent(); // log.info(result.get()); } catch (Exception e) { - e.printStackTrace(); + log.error("", e); } }); } diff --git a/question/src/main/java/com/github/kuangcp/simplex/method/ReadProperties.java b/question/src/main/java/com/github/kuangcp/simplex/method/ReadProperties.java index bea9074c..a7db55c3 100644 --- a/question/src/main/java/com/github/kuangcp/simplex/method/ReadProperties.java +++ b/question/src/main/java/com/github/kuangcp/simplex/method/ReadProperties.java @@ -1,9 +1,11 @@ package com.github.kuangcp.simplex.method; +import lombok.extern.slf4j.Slf4j; + import java.io.File; -import java.io.FileInputStream; import java.net.URL; import java.nio.charset.StandardCharsets; +import java.nio.file.Files; import java.util.Objects; import java.util.Properties; @@ -17,6 +19,7 @@ * 例如 Gradle 编译目录: ./out/build * java -classpath ./classes/java/main/:./resources/main/ com.github.kuangcp.simplex.method.ReadProperties */ +@Slf4j public class ReadProperties { private Properties cfg = new Properties(); @@ -31,9 +34,9 @@ public class ReadProperties { throw new RuntimeException("file not exist"); } File f = new File(resource.getPath()); - cfg.load(new FileInputStream(f)); + cfg.load(Files.newInputStream(f.toPath())); } catch (Exception e) { - e.printStackTrace(); + log.error("", e); throw new RuntimeException(e); } } @@ -58,7 +61,7 @@ public static void main(String[] a) { System.out.println(new String(result.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8)); } catch (Exception e) { - e.printStackTrace(); + log.error("", e); } } } diff --git a/question/src/main/java/com/github/kuangcp/simplex/method/SimplexMethodWithFraction.java b/question/src/main/java/com/github/kuangcp/simplex/method/SimplexMethodWithFraction.java index fab4d7f3..c58399da 100644 --- a/question/src/main/java/com/github/kuangcp/simplex/method/SimplexMethodWithFraction.java +++ b/question/src/main/java/com/github/kuangcp/simplex/method/SimplexMethodWithFraction.java @@ -1,6 +1,8 @@ package com.github.kuangcp.simplex.method; import com.github.kuangcp.math.number.Fraction; +import lombok.extern.slf4j.Slf4j; + import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -9,400 +11,401 @@ /** * Created by Myth on 2017/3/22 0022 */ +@Slf4j public class SimplexMethodWithFraction { - private static ReadProperties config;//读取配置文件 - private static int MAX_PARAMS;//最大参数个数 - private static int EQUALITY;//最大行列式数 - private List Rows = new ArrayList<>();//约束式子系数集合 - private List Max = new ArrayList<>();//原始式子系数集合 - private List

Tables = new ArrayList<>();//单纯形表的总体数据结构 - private List Os = new ArrayList<>();//单纯形表中间计算结果右侧 - private List Zs = new ArrayList<>();//单纯形表计算结果最下一行 - //出入基锁定的坐标 - private Integer resultCol; - private Integer resultRow; - private boolean CONTINUE = true;//记录是否要继续计算的标识属性 - // private boolean SUCCESS = true;//记录是否成功计算出结果 - private Map Xbs = new HashMap<>(); - private Integer Round = 1; + private static ReadProperties config;//读取配置文件 + private static int MAX_PARAMS;//最大参数个数 + private static int EQUALITY;//最大行列式数 + private List Rows = new ArrayList<>();//约束式子系数集合 + private List Max = new ArrayList<>();//原始式子系数集合 + private List
Tables = new ArrayList<>();//单纯形表的总体数据结构 + private List Os = new ArrayList<>();//单纯形表中间计算结果右侧 + private List Zs = new ArrayList<>();//单纯形表计算结果最下一行 + //出入基锁定的坐标 + private Integer resultCol; + private Integer resultRow; + private boolean CONTINUE = true;//记录是否要继续计算的标识属性 + // private boolean SUCCESS = true;//记录是否成功计算出结果 + private Map Xbs = new HashMap<>(); + private Integer Round = 1; - static { - config = new ReadProperties("math/SimplexMethod.properties"); - MAX_PARAMS = config.getInt("MaxParams"); - EQUALITY = config.getInt("Equality"); - } + static { + config = new ReadProperties("math/SimplexMethod.properties"); + MAX_PARAMS = config.getInt("MaxParams"); + EQUALITY = config.getInt("Equality"); + } - public static void main(String[] s) { - SimplexMethodWithFraction sm = new SimplexMethodWithFraction(); - try { - sm.run(); - } catch (Exception e) { - e.printStackTrace(); + public static void main(String[] s) { + SimplexMethodWithFraction sm = new SimplexMethodWithFraction(); + try { + sm.run(); + } catch (Exception e) { + log.error("", e); + } } - } - /** - * 初始化整体数据 - */ - private void init() { - //添加求解式的数据 - String max = config.getString("Max"); - String[] temp = max.split(","); - for (String data : temp) { + /** + * 初始化整体数据 + */ + private void init() { + //添加求解式的数据 + String max = config.getString("Max"); + String[] temp = max.split(","); + for (String data : temp) { // System.out.println(data); - Max.add(Fraction.valueOf(data)); - } - //添加约束式的数据 - for (int i = 1; i <= EQUALITY; i++) { - String buffer = config.getString("E" + i); - String[] tempList = buffer.split(","); - Equality e = new Equality(); - for (String aTempList : tempList) { - e.getParams().add(Fraction.valueOf(aTempList)); - } - e.setResult(Fraction.valueOf(config.getString("B" + i))); - e.setIndex(config.getInt("I" + i)); - Rows.add(e); + Max.add(Fraction.valueOf(data)); + } + //添加约束式的数据 + for (int i = 1; i <= EQUALITY; i++) { + String buffer = config.getString("E" + i); + String[] tempList = buffer.split(","); + Equality e = new Equality(); + for (String aTempList : tempList) { + e.getParams().add(Fraction.valueOf(aTempList)); + } + e.setResult(Fraction.valueOf(config.getString("B" + i))); + e.setIndex(config.getInt("I" + i)); + Rows.add(e); - } - //填入单纯形表总体数据中去 - Equality stRow; - for (int i = 0; i < EQUALITY; i++) { - stRow = Rows.get(i); - Integer index = stRow.getIndex(); - //装填第一行的数据 - Table table = new Table(Max.get(index - 1), index, stRow.getResult(), stRow.getParams(), - null); - Tables.add(table); - } - //查看数据是否正确装填 + } + //填入单纯形表总体数据中去 + Equality stRow; + for (int i = 0; i < EQUALITY; i++) { + stRow = Rows.get(i); + Integer index = stRow.getIndex(); + //装填第一行的数据 + Table table = new Table(Max.get(index - 1), index, stRow.getResult(), stRow.getParams(), + null); + Tables.add(table); + } + //查看数据是否正确装填 // display("单纯形表的中心数据 : ",Tables); - display("求解的方程式系数 :", false, Max, false); + display("求解的方程式系数 :", false, Max, false); // display("约束条件的方程式",Rows); - //展示方程式 - System.out.println("原题样式 : "); - showRows(); + //展示方程式 + System.out.println("原题样式 : "); + showRows(); - } + } - /** - * 循环计算直到出现结果 - */ - public void run() throws Exception { - init(); + /** + * 循环计算直到出现结果 + */ + public void run() throws Exception { + init(); //运算出初始的表格 - display("中间计算结果", Tables); - //计算最底下一行 - CalculateLastRow(); - //计算右边列 - CalculateRightCol(); - //判断是否达到退出条件 - CONTINUE = isNeedContinue(Zs); + display("中间计算结果", Tables); + //计算最底下一行 + CalculateLastRow(); + //计算右边列 + CalculateRightCol(); + //判断是否达到退出条件 + CONTINUE = isNeedContinue(Zs); - //迭代的计算直到满足条件 - while (CONTINUE) { - try { - Thread.sleep(100); - } catch (Exception e) { - e.printStackTrace(); - } - //算完一轮 - Fraction fatherNum = Tables.get(resultRow).getRows().get(resultCol); - List
oldTables = Tables; - Tables = new ArrayList<>(); + //迭代的计算直到满足条件 + while (CONTINUE) { + try { + Thread.sleep(100); + } catch (Exception e) { + log.error("", e); + } + //算完一轮 + Fraction fatherNum = Tables.get(resultRow).getRows().get(resultCol); + List
oldTables = Tables; + Tables = new ArrayList<>(); - //转换出基入基的参数 - List rowsTemps = oldTables.get(resultRow).getRows(); - for (int k = 0; k < rowsTemps.size(); k++) { - rowsTemps.set(k, rowsTemps.get(k).divide(fatherNum)); - } - Table temp = new Table(Max.get(resultCol), resultCol + 1, - oldTables.get(resultRow).getBl().divide(fatherNum), rowsTemps, null); - Tables.add(temp); - //转换剩余的 - for (int j = 0; j < EQUALITY; j++) { - if (j != resultRow) { - rowsTemps = oldTables.get(j).getRows(); - Fraction motherNum = rowsTemps.get(resultCol); - Table fatherRow = Tables.get(0); - for (int k = 0; k < rowsTemps.size(); k++) { - rowsTemps.set(k, rowsTemps.get(k).subtract(motherNum.multiply(fatherRow.getRows().get(k)))); - } - Integer tempXb = oldTables.get(j).getXb(); - Fraction multiply = motherNum.multiply(fatherRow.getBl()); - Table otherTemp = new Table(Max.get(tempXb - 1), tempXb, - oldTables.get(j).getBl().subtract(multiply), rowsTemps, null); - Tables.add(otherTemp); - } + //转换出基入基的参数 + List rowsTemps = oldTables.get(resultRow).getRows(); + for (int k = 0; k < rowsTemps.size(); k++) { + rowsTemps.set(k, rowsTemps.get(k).divide(fatherNum)); + } + Table temp = new Table(Max.get(resultCol), resultCol + 1, + oldTables.get(resultRow).getBl().divide(fatherNum), rowsTemps, null); + Tables.add(temp); + //转换剩余的 + for (int j = 0; j < EQUALITY; j++) { + if (j != resultRow) { + rowsTemps = oldTables.get(j).getRows(); + Fraction motherNum = rowsTemps.get(resultCol); + Table fatherRow = Tables.get(0); + for (int k = 0; k < rowsTemps.size(); k++) { + rowsTemps.set(k, rowsTemps.get(k).subtract(motherNum.multiply(fatherRow.getRows().get(k)))); + } + Integer tempXb = oldTables.get(j).getXb(); + Fraction multiply = motherNum.multiply(fatherRow.getBl()); + Table otherTemp = new Table(Max.get(tempXb - 1), tempXb, + oldTables.get(j).getBl().subtract(multiply), rowsTemps, null); + Tables.add(otherTemp); + } - } - Zs.clear(); - Os.clear(); + } + Zs.clear(); + Os.clear(); // log("下右两个计算集合的大小"+Zs.size()+":"+Os.size()); - display("中间计算结果", true, Tables, true); - //计算最后一行 - CalculateLastRow(); - //判断是否达到退出条件 - CONTINUE = isNeedContinue(Zs); - if (CONTINUE) { - CalculateRightCol(); - } - System.out.println("******************************"); - } - - //运算完成 - //@TODO 要进行判断是否成功计算,然后执行不同的方法 - finallyResult(); - } + display("中间计算结果", true, Tables, true); + //计算最后一行 + CalculateLastRow(); + //判断是否达到退出条件 + CONTINUE = isNeedContinue(Zs); + if (CONTINUE) { + CalculateRightCol(); + } + System.out.println("******************************"); + } - /** - * 处理最后运行结果,进行判断 - * 1. 右列没有一个正数,最后一行也没有正数 - * 2. Xb列死循环,即变量循环的出基入基 - * - * @throws Exception 异常 - */ - private void finallyResult() throws Exception { - Integer RightMin = maxList(Os, false, true, false); - boolean SUCCESS = true; - if (RightMin == -1) { - System.out.println("右列没有一个正数,最后一行也没有正数,原方程没有最优解"); - SUCCESS = false; + //运算完成 + //@TODO 要进行判断是否成功计算,然后执行不同的方法 + finallyResult(); } - //正确的计算出结果 - if (SUCCESS) { - Fraction result = new Fraction(0); - Fraction[] results = new Fraction[MAX_PARAMS]; - for (int i = 0; i < EQUALITY; i++) { - Table t = Tables.get(i); - result = result.add(t.getCb().multiply(t.getBl())); - results[t.getXb() - 1] = t.getBl(); - } - System.out.println("最优目标值是 : " + result); - StringBuilder resultStr = new StringBuilder("X=("); - for (Fraction d : results) { - if (d != null) { - resultStr.append(d).append(","); - } else { - resultStr.append("0,"); + + /** + * 处理最后运行结果,进行判断 + * 1. 右列没有一个正数,最后一行也没有正数 + * 2. Xb列死循环,即变量循环的出基入基 + * + * @throws Exception 异常 + */ + private void finallyResult() throws Exception { + Integer RightMin = maxList(Os, false, true, false); + boolean SUCCESS = true; + if (RightMin == -1) { + System.out.println("右列没有一个正数,最后一行也没有正数,原方程没有最优解"); + SUCCESS = false; } - } - resultStr = new StringBuilder(resultStr.substring(0, resultStr.length() - 1)); - resultStr.append(")"); - System.out.println(resultStr); + //正确的计算出结果 + if (SUCCESS) { + Fraction result = new Fraction(0); + Fraction[] results = new Fraction[MAX_PARAMS]; + for (int i = 0; i < EQUALITY; i++) { + Table t = Tables.get(i); + result = result.add(t.getCb().multiply(t.getBl())); + results[t.getXb() - 1] = t.getBl(); + } + System.out.println("最优目标值是 : " + result); + StringBuilder resultStr = new StringBuilder("X=("); + for (Fraction d : results) { + if (d != null) { + resultStr.append(d).append(","); + } else { + resultStr.append("0,"); + } + } + resultStr = new StringBuilder(resultStr.substring(0, resultStr.length() - 1)); + resultStr.append(")"); + System.out.println(resultStr); + } + for (String key : Xbs.keySet()) { + log(Xbs.get(key) + "轮" + key); + } } - for (String key : Xbs.keySet()) { - log(Xbs.get(key) + "轮" + key); - } - } - /** - * 计算最后一行和最右边的一列 - */ - private void CalculateLastRow() throws Exception { - for (int i = 0; i < MAX_PARAMS; i++) {//循环变量个数 - Fraction temp = Max.get(i); + /** + * 计算最后一行和最右边的一列 + */ + private void CalculateLastRow() throws Exception { + for (int i = 0; i < MAX_PARAMS; i++) {//循环变量个数 + Fraction temp = Max.get(i); // display("目标方程系数组",Max,false); // System.out.println("Max中取到的temp"+temp); - for (int j = 0; j < EQUALITY; j++) {//循环层数 + for (int j = 0; j < EQUALITY; j++) {//循环层数 // System.out.println("乘积 "+Tables.get(j).getCb()+"*"+Tables.get(j).getRows().get(i)+" = "+Tables.get(j).getCb().multiply(Tables.get(j).getRows().get(i))); - temp = temp.subtract(Tables.get(j).getCb().multiply(Tables.get(j).getRows().get(i))); + temp = temp.subtract(Tables.get(j).getCb().multiply(Tables.get(j).getRows().get(i))); // System.out.println("temp:"+temp+"Cb:"+Tables.get(j).getCb()+"*"+Tables.get(j).getRows().get(i)); - } - Zs.add(temp); + } + Zs.add(temp); // System.out.println("jieguo "+temp); - } - display("最后一行", false, Zs, false); - resultCol = maxList(Zs, true, true, true); + } + display("最后一行", false, Zs, false); + resultCol = maxList(Zs, true, true, true); - } + } - //计算右边栏 - private void CalculateRightCol() throws Exception { + //计算右边栏 + private void CalculateRightCol() throws Exception { // log("计算所得行最大Index"+resultCol); - for (int i = 0; i < EQUALITY; i++) { + for (int i = 0; i < EQUALITY; i++) { // log("计算表达式 "+Tables.get(i).getBl()+" / "+Tables.get(i).getRows().get(resultCol)); - Fraction temp = Tables.get(i).getBl().divide(Tables.get(i).getRows().get(resultCol)); + Fraction temp = Tables.get(i).getBl().divide(Tables.get(i).getRows().get(resultCol)); // log("结果"+temp); - Tables.get(i).setO(temp); - Os.add(temp); - } - display("右栏", false, Os, false); - resultRow = maxList(Os, false, true, false); - if (resultRow == -1) { - CONTINUE = false; - } + Tables.get(i).setO(temp); + Os.add(temp); + } + display("右栏", false, Os, false); + resultRow = maxList(Os, false, true, false); + if (resultRow == -1) { + CONTINUE = false; + } // log("右边最小index :"+resultRow); - // @ToDo 计算右边当右边有大于0的数 才继续,否则退出计算 要结合退出函数使用 + // @ToDo 计算右边当右边有大于0的数 才继续,否则退出计算 要结合退出函数使用 - } - - /** - * 1.判断最后一行是否到了最后的计算结果 即全小于0 就可以退出计算了 - * 2.又加入一个判断机制,循环出入基的情况 - * - * @param list 分数数组 - * @return false就不再继续 - */ - private boolean isNeedContinue(List list) { - boolean flag = false; - for (Fraction b : list) { - if (b.isPositive()) { - flag = true; - break; - } - } -// display("检测",Tables); - StringBuilder temp = new StringBuilder(); - for (int i = 0; i < EQUALITY; i++) { - temp.append(Tables.get(i).getXb()); } - if (!Xbs.containsKey(temp.toString())) { - Xbs.put(temp.toString(), Round++ + ""); - } else { - return false; - } - return flag; - - } - /** - * 求解含有非数的集合的极值,异常返回-1 - * - * @param list 分数数组 - * @param isMax 是否求最大 - * @param haveInfinity 是否有非数 - * @param permitMinus 是否允许负数进行笔记比较 - * @return 最大 - */ - public Integer maxList(List list, boolean isMax, boolean haveInfinity, boolean permitMinus) { - Integer index = null; - //有非数的集合 - if (haveInfinity) { - Map tempMap = new HashMap<>(); - //去除非数 - for (int i = 0; i < list.size(); i++) { - if (permitMinus && list.get(i).getDenominator() != 0) { - tempMap.put(i, list.get(i)); + /** + * 1.判断最后一行是否到了最后的计算结果 即全小于0 就可以退出计算了 + * 2.又加入一个判断机制,循环出入基的情况 + * + * @param list 分数数组 + * @return false就不再继续 + */ + private boolean isNeedContinue(List list) { + boolean flag = false; + for (Fraction b : list) { + if (b.isPositive()) { + flag = true; + break; + } } - //不允许负数的情况 - if (!permitMinus) { - if (list.get(i).getDenominator() != 0 && list.get(i).isPositive()) { - tempMap.put(i, list.get(i)); - } +// display("检测",Tables); + StringBuilder temp = new StringBuilder(); + for (int i = 0; i < EQUALITY; i++) { + temp.append(Tables.get(i).getXb()); } - } - if (tempMap.size() == 0) { - return -1; - } - for (Integer integer : tempMap.keySet()) { - if (index == null) { - index = integer; - } else if (isMax && !tempMap.get(index).isGreaterThan(tempMap.get(integer))) { - index = integer; - } else if (!isMax && tempMap.get(index).isGreaterThan(tempMap.get(integer))) { - index = integer; + if (!Xbs.containsKey(temp.toString())) { + Xbs.put(temp.toString(), Round++ + ""); + } else { + return false; } - } + return flag; + } - //没有非数的集合 - if (!haveInfinity) { - if (list.size() == 0) { - return -1; - } - Fraction temp = list.get(0); - for (int i = 0; i < list.size(); i++) { - if (list.get(i).isPositive()) { - if (isMax && !temp.isGreaterThan(list.get(i))) { - index = i; - temp = list.get(i); - } - if (!isMax && temp.isGreaterThan(list.get(i))) { - index = i; - temp = list.get(i); - } + + /** + * 求解含有非数的集合的极值,异常返回-1 + * + * @param list 分数数组 + * @param isMax 是否求最大 + * @param haveInfinity 是否有非数 + * @param permitMinus 是否允许负数进行笔记比较 + * @return 最大 + */ + public Integer maxList(List list, boolean isMax, boolean haveInfinity, boolean permitMinus) { + Integer index = null; + //有非数的集合 + if (haveInfinity) { + Map tempMap = new HashMap<>(); + //去除非数 + for (int i = 0; i < list.size(); i++) { + if (permitMinus && list.get(i).getDenominator() != 0) { + tempMap.put(i, list.get(i)); + } + //不允许负数的情况 + if (!permitMinus) { + if (list.get(i).getDenominator() != 0 && list.get(i).isPositive()) { + tempMap.put(i, list.get(i)); + } + } + } + if (tempMap.size() == 0) { + return -1; + } + for (Integer integer : tempMap.keySet()) { + if (index == null) { + index = integer; + } else if (isMax && !tempMap.get(index).isGreaterThan(tempMap.get(integer))) { + index = integer; + } else if (!isMax && tempMap.get(index).isGreaterThan(tempMap.get(integer))) { + index = integer; + } + } } + //没有非数的集合 + if (!haveInfinity) { + if (list.size() == 0) { + return -1; + } + Fraction temp = list.get(0); + for (int i = 0; i < list.size(); i++) { + if (list.get(i).isPositive()) { + if (isMax && !temp.isGreaterThan(list.get(i))) { + index = i; + temp = list.get(i); + } + if (!isMax && temp.isGreaterThan(list.get(i))) { + index = i; + temp = list.get(i); + } + } - } + } + } + return index; } - return index; - } - - /** - * 方便展示原始数据 - * - * @param list 数据数组 - */ - private void display(String title, List list) { - display(title, true, list, true); - } - /** - * 展示数据 - * - * @param title 标题 - * @param titleTurn 标题是否换行 - * @param list 数据 - * @param turn 内容是否换行 - */ - private void display(String title, boolean titleTurn, List list, boolean turn) { - if (titleTurn) { - System.out.println(title); - } else { - System.out.print(title + " : "); - } - for (Object aList : list) { - if (turn) { - System.out.println(aList.toString()); - } else { - System.out.print(aList.toString() + " "); - } + /** + * 方便展示原始数据 + * + * @param list 数据数组 + */ + private void display(String title, List list) { + display(title, true, list, true); } - if (!turn) { - System.out.println(); - } - } - /** - * 展示整体方程式,原题的样子 - */ - private void showRows() { - StringBuilder MaxRows = new StringBuilder("Max(z)="); - for (int i = 0; i < MAX_PARAMS; i++) { - if (!Max.get(i).isZero()) { - MaxRows.append(Max.get(i)).append(" X").append(i + 1).append(" + "); - } - } - MaxRows = new StringBuilder(MaxRows.substring(0, MaxRows.length() - 2)); - System.out.println("Aim : " + MaxRows); - for (int i = 0; i < EQUALITY; i++) { - StringBuilder Row = new StringBuilder(); - List temp = Rows.get(i).getParams(); - for (int j = 0; j < temp.size(); j++) { - Fraction a = temp.get(j); - if (!a.isZero()) { - if (!a.isOne()) { - Row.append(a.toString()).append("X").append(j + 1).append(" + "); - } else { - Row.append("X").append(j + 1).append(" + "); - } + /** + * 展示数据 + * + * @param title 标题 + * @param titleTurn 标题是否换行 + * @param list 数据 + * @param turn 内容是否换行 + */ + private void display(String title, boolean titleTurn, List list, boolean turn) { + if (titleTurn) { + System.out.println(title); } else { - Row.append(" "); + System.out.print(title + " : "); + } + for (Object aList : list) { + if (turn) { + System.out.println(aList.toString()); + } else { + System.out.print(aList.toString() + " "); + } + } + if (!turn) { + System.out.println(); + } + } + + /** + * 展示整体方程式,原题的样子 + */ + private void showRows() { + StringBuilder MaxRows = new StringBuilder("Max(z)="); + for (int i = 0; i < MAX_PARAMS; i++) { + if (!Max.get(i).isZero()) { + MaxRows.append(Max.get(i)).append(" X").append(i + 1).append(" + "); + } + } + MaxRows = new StringBuilder(MaxRows.substring(0, MaxRows.length() - 2)); + System.out.println("Aim : " + MaxRows); + for (int i = 0; i < EQUALITY; i++) { + StringBuilder Row = new StringBuilder(); + List temp = Rows.get(i).getParams(); + for (int j = 0; j < temp.size(); j++) { + Fraction a = temp.get(j); + if (!a.isZero()) { + if (!a.isOne()) { + Row.append(a.toString()).append("X").append(j + 1).append(" + "); + } else { + Row.append("X").append(j + 1).append(" + "); + } + } else { + Row.append(" "); + } + } + Row = new StringBuilder(Row.substring(0, Row.length() - 2)); + Row.append(" = ").append(Rows.get(i).getResult()).append(" |X").append(Rows.get(i).getIndex()) + .append("|"); + System.out.println("Equality : " + Row); } - } - Row = new StringBuilder(Row.substring(0, Row.length() - 2)); - Row.append(" = ").append(Rows.get(i).getResult()).append(" |X").append(Rows.get(i).getIndex()) - .append("|"); - System.out.println("Equality : " + Row); } - } - private void log(String s) { - System.out.println(s); - } + private void log(String s) { + System.out.println(s); + } } From 76667c1355476ca91e5a3636c2d700ea96ad658f Mon Sep 17 00:00:00 2001 From: kuangcp Date: Mon, 9 Oct 2023 19:09:07 +0800 Subject: [PATCH 301/476] fmt --- .../simplex/method/ReadProperties.java | 74 +++++++++---------- .../asm/cglib/SimpleTransformerTest.java | 28 +++---- .../commoncode/CommonRelationTest.java | 2 +- 3 files changed, 52 insertions(+), 52 deletions(-) diff --git a/question/src/main/java/com/github/kuangcp/simplex/method/ReadProperties.java b/question/src/main/java/com/github/kuangcp/simplex/method/ReadProperties.java index a7db55c3..159e6ec3 100644 --- a/question/src/main/java/com/github/kuangcp/simplex/method/ReadProperties.java +++ b/question/src/main/java/com/github/kuangcp/simplex/method/ReadProperties.java @@ -12,56 +12,56 @@ /** * Created by Myth on 2017/1/13 0013 - 20:51 * 将 java 目录和 resources 目录引入 classpath 即可正常运行 - * + *

* 例如 IDEA编译目录: ./out/production * java -classpath ./classes/:./resources/ com.github.kuangcp.simplex.method.ReadProperties - * + *

* 例如 Gradle 编译目录: ./out/build * java -classpath ./classes/java/main/:./resources/main/ com.github.kuangcp.simplex.method.ReadProperties */ @Slf4j public class ReadProperties { - private Properties cfg = new Properties(); + private Properties cfg = new Properties(); - /** - * @param file 例如 resources 目录下 a.properties - */ - ReadProperties(String file) { - try { - URL resource = this.getClass().getClassLoader().getResource(file); - if (Objects.isNull(resource)) { - throw new RuntimeException("file not exist"); - } - File f = new File(resource.getPath()); - cfg.load(Files.newInputStream(f.toPath())); - } catch (Exception e) { - log.error("", e); - throw new RuntimeException(e); + /** + * @param file 例如 resources 目录下 a.properties + */ + ReadProperties(String file) { + try { + URL resource = this.getClass().getClassLoader().getResource(file); + if (Objects.isNull(resource)) { + throw new RuntimeException("file not exist"); + } + File f = new File(resource.getPath()); + cfg.load(Files.newInputStream(f.toPath())); + } catch (Exception e) { + log.error("", e); + throw new RuntimeException(e); + } } - } - String getString(String key) { - return cfg.getProperty(key); - } + String getString(String key) { + return cfg.getProperty(key); + } - int getInt(String key) { - return Integer.parseInt(cfg.getProperty(key)); - } + int getInt(String key) { + return Integer.parseInt(cfg.getProperty(key)); + } - public double getDouble(String key) { - return Double.parseDouble(getString(key)); - } + public double getDouble(String key) { + return Double.parseDouble(getString(key)); + } - public static void main(String[] a) { - ReadProperties read = new ReadProperties("math/SimplexMethod.properties"); - String result = read.getString("Max"); - try { - //配置文件含中文需要转码 - System.out.println(new String(result.getBytes(StandardCharsets.ISO_8859_1), - StandardCharsets.UTF_8)); - } catch (Exception e) { - log.error("", e); + public static void main(String[] a) { + ReadProperties read = new ReadProperties("math/SimplexMethod.properties"); + String result = read.getString("Max"); + try { + //配置文件含中文需要转码 + System.out.println(new String(result.getBytes(StandardCharsets.ISO_8859_1), + StandardCharsets.UTF_8)); + } catch (Exception e) { + log.error("", e); + } } - } } diff --git a/question/src/test/java/com/github/kuangcp/situation/asm/cglib/SimpleTransformerTest.java b/question/src/test/java/com/github/kuangcp/situation/asm/cglib/SimpleTransformerTest.java index a45dbc52..351e2997 100644 --- a/question/src/test/java/com/github/kuangcp/situation/asm/cglib/SimpleTransformerTest.java +++ b/question/src/test/java/com/github/kuangcp/situation/asm/cglib/SimpleTransformerTest.java @@ -9,19 +9,19 @@ @Slf4j public class SimpleTransformerTest { - @Test - public void testInit() { - // 虽然该类属性是 static final 但是第一次引用的时候才进行加载和初始化 - new Thread(() -> { - try { - SimpleTransformer instance = SimpleTransformer.INSTANCE; - log.info("instance={}", instance); - } catch (Exception e) { - log.error(e.getMessage(), e); - } - }).start(); + @Test + public void testInit() { + // 虽然该类属性是 static final 但是第一次引用的时候才进行加载和初始化 + new Thread(() -> { + try { + SimpleTransformer instance = SimpleTransformer.INSTANCE; + log.info("instance={}", instance); + } catch (Exception e) { + log.error(e.getMessage(), e); + } + }).start(); - // 第二次调用就直接报错 NoClassDefFoundError - log.info("instance={}", SimpleTransformer.INSTANCE); - } + // 第二次调用就直接报错 NoClassDefFoundError + log.info("instance={}", SimpleTransformer.INSTANCE); + } } \ No newline at end of file diff --git a/question/src/test/java/com/github/kuangcp/situation/commoncode/CommonRelationTest.java b/question/src/test/java/com/github/kuangcp/situation/commoncode/CommonRelationTest.java index f9ff1779..39446cbd 100644 --- a/question/src/test/java/com/github/kuangcp/situation/commoncode/CommonRelationTest.java +++ b/question/src/test/java/com/github/kuangcp/situation/commoncode/CommonRelationTest.java @@ -288,7 +288,7 @@ private void assertResultMap(Set exceptMap, Map> res re.addAll(sortMap.values()); re.removeAll(sameSet); - if (re.size() != 0) { + if (!re.isEmpty()) { log.info("re={}", re); Assert.fail(); } From 109249806d110a9d433942c9950b90956c01e4ce Mon Sep 17 00:00:00 2001 From: kuangcp Date: Mon, 9 Oct 2023 19:15:44 +0800 Subject: [PATCH 302/476] readme --- question/README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/question/README.md b/question/README.md index 96313510..957d6a58 100644 --- a/question/README.md +++ b/question/README.md @@ -1,2 +1,4 @@ # 解决特定问题的项目 -1. 运筹学的单纯形法计算 \ No newline at end of file +1. 运筹学的单纯形法计算 +2. 给定多个集合,依据联通性合并,最终结果为多个不联通的集合 +3. ASM 特定场景 使用不当导致的bug \ No newline at end of file From b849ad89634a323a8bf62b415d1858cf9e791596 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Tue, 10 Oct 2023 00:41:50 +0800 Subject: [PATCH 303/476] aes: code explain --- class/src/main/java/security/aes/AESUtil.java | 28 ++++++-- .../java/security/aes/wx/WXBizMsgCrypt.java | 7 +- .../aes/wx/{AES.java => WxMsgAESUtil.java} | 20 +----- ...est.java => WxMsgAESUtilUtilUnitTest.java} | 69 +++++++++++++------ .../security/aes/wx/WXBizMsgCryptTest.java | 24 +------ .../{AESTest.java => WxMsgAESUtilTest.java} | 12 +--- .../test/java/security/base/GuideTest.java | 19 +++++ 7 files changed, 101 insertions(+), 78 deletions(-) rename class/src/main/java/security/aes/wx/{AES.java => WxMsgAESUtil.java} (82%) rename class/src/test/java/security/aes/{AESUtilUnitTest.java => WxMsgAESUtilUtilUnitTest.java} (55%) rename class/src/test/java/security/aes/wx/{AESTest.java => WxMsgAESUtilTest.java} (64%) create mode 100644 class/src/test/java/security/base/GuideTest.java diff --git a/class/src/main/java/security/aes/AESUtil.java b/class/src/main/java/security/aes/AESUtil.java index 60349a47..0a0db26f 100644 --- a/class/src/main/java/security/aes/AESUtil.java +++ b/class/src/main/java/security/aes/AESUtil.java @@ -1,13 +1,13 @@ package security.aes; +import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; +import javax.crypto.KeyGenerator; import javax.crypto.NoSuchPaddingException; +import javax.crypto.SealedObject; import javax.crypto.SecretKey; -import javax.crypto.BadPaddingException; -import javax.crypto.KeyGenerator; import javax.crypto.SecretKeyFactory; -import javax.crypto.SealedObject; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.PBEKeySpec; import javax.crypto.spec.SecretKeySpec; @@ -44,9 +44,12 @@ public static String decrypt(String algorithm, String cipherText, SecretKey key, return new String(plainText); } - public static SecretKey generateKey(int n) throws NoSuchAlgorithmException { + /** + * @see com.sun.crypto.provider.AESCrypt#isKeySizeValid 16 24 32 字节 + */ + public static SecretKey generateKey(int bit) throws NoSuchAlgorithmException { KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); - keyGenerator.init(n); + keyGenerator.init(bit); return keyGenerator.generateKey(); } @@ -57,6 +60,21 @@ public static SecretKey getKeyFromPassword(String password, String salt) return new SecretKeySpec(factory.generateSecret(spec).getEncoded(), "AES"); } + public static String generateIvByte() { + byte[] iv = new byte[16]; + new SecureRandom().nextBytes(iv); + final byte[] encode = Base64.getEncoder().encode(iv); + return new String(encode); + } + + public static IvParameterSpec generateIv(String base64) { + final byte[] iv = Base64.getDecoder().decode(base64); + return new IvParameterSpec(iv); + } + + /** + * 初始向量长度必须等于16字节 + */ public static IvParameterSpec generateIv() { byte[] iv = new byte[16]; new SecureRandom().nextBytes(iv); diff --git a/class/src/main/java/security/aes/wx/WXBizMsgCrypt.java b/class/src/main/java/security/aes/wx/WXBizMsgCrypt.java index b76b9a15..1427601b 100644 --- a/class/src/main/java/security/aes/wx/WXBizMsgCrypt.java +++ b/class/src/main/java/security/aes/wx/WXBizMsgCrypt.java @@ -51,7 +51,9 @@ public WXBizMsgCrypt(String token, String encodingAesKey, String appId) throws A aesKey = Base64.getDecoder().decode(encodingAesKey + "="); } - // 生成4个字节的网络字节序 + /** + * 生成4个字节的网络字节序. 用意:4字节存储一个Int值(消息长度)增加密文消息被篡改的难度 + */ byte[] getNetworkBytesOrder(int sourceNumber) { byte[] orderBytes = new byte[4]; orderBytes[3] = (byte) (sourceNumber & 0xFF); @@ -97,6 +99,8 @@ String encrypt(String randomStr, String text) throws AesException { byte[] networkBytesOrder = getNetworkBytesOrder(textBytes.length); byte[] appidBytes = appId.getBytes(CHARSET); + + // 注意:随机字符串 + 消息长度 + 原文 + appid, 尽量规避解密 // randomStr + networkBytesOrder + text + appid byteCollector.addBytes(randomStrBytes); byteCollector.addBytes(networkBytesOrder); @@ -195,7 +199,6 @@ String decrypt(String text) throws AesException { * @throws AesException 执行失败,请查看该异常的错误码和具体的错误信息 */ public String encryptMsg(String replyMsg) throws AesException { - //加密 return encrypt(getRandomStr(), replyMsg); } diff --git a/class/src/main/java/security/aes/wx/AES.java b/class/src/main/java/security/aes/wx/WxMsgAESUtil.java similarity index 82% rename from class/src/main/java/security/aes/wx/AES.java rename to class/src/main/java/security/aes/wx/WxMsgAESUtil.java index 48bb5f64..7c86f0dd 100644 --- a/class/src/main/java/security/aes/wx/AES.java +++ b/class/src/main/java/security/aes/wx/WxMsgAESUtil.java @@ -11,27 +11,10 @@ import java.util.Base64; @Slf4j -public class AES { +public class WxMsgAESUtil { static Charset CHARSET = StandardCharsets.UTF_8; - static byte[] getNetworkBytesOrder(int sourceNumber) { - byte[] orderBytes = new byte[4]; - orderBytes[3] = (byte) (sourceNumber & 0xFF); - orderBytes[2] = (byte) (sourceNumber >> 8 & 0xFF); - orderBytes[1] = (byte) (sourceNumber >> 16 & 0xFF); - orderBytes[0] = (byte) (sourceNumber >> 24 & 0xFF); - return orderBytes; - } - static int recoverNetworkBytesOrder(byte[] orderBytes) { - int sourceNumber = 0; - for (int i = 0; i < 4; i++) { - sourceNumber <<= 8; - sourceNumber |= orderBytes[i] & 0xff; - } - return sourceNumber; - } - /** * 对明文进行加密. * @@ -55,6 +38,7 @@ static String encrypt(byte[] aesKey, String text) throws AesException { // 设置加密模式为AES的CBC模式 Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding"); SecretKeySpec keySpec = new SecretKeySpec(aesKey, "AES"); + // 取密钥的前16字节作为 iv 初始化向量 IvParameterSpec iv = new IvParameterSpec(aesKey, 0, 16); cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv); diff --git a/class/src/test/java/security/aes/AESUtilUnitTest.java b/class/src/test/java/security/aes/WxMsgAESUtilUtilUnitTest.java similarity index 55% rename from class/src/test/java/security/aes/AESUtilUnitTest.java rename to class/src/test/java/security/aes/WxMsgAESUtilUtilUnitTest.java index 0f2e81cf..3d31a332 100644 --- a/class/src/test/java/security/aes/AESUtilUnitTest.java +++ b/class/src/test/java/security/aes/WxMsgAESUtilUtilUnitTest.java @@ -4,26 +4,32 @@ import org.junit.Assert; import org.junit.Test; -import javax.crypto.*; +import javax.crypto.SealedObject; +import javax.crypto.SecretKey; import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; import java.io.File; -import java.io.IOException; import java.nio.file.Paths; -import java.security.InvalidAlgorithmParameterException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; -import java.security.spec.InvalidKeySpecException; +import java.security.SecureRandom; -public class AESUtilUnitTest { +public class WxMsgAESUtilUtilUnitTest { @Test - public void testStringEncrypt() - throws NoSuchAlgorithmException, IllegalBlockSizeException, InvalidKeyException, - BadPaddingException, InvalidAlgorithmParameterException, NoSuchPaddingException { + public void testStringEncrypt() throws Exception { // given - String input = "baeldung"; + String input = "test for that"; + + // 生成一个key的重点在于 16 24 32 字节数组的随机数,将key base64后配置共享即可完成两端系统加密通信 + byte[] var2 = new byte[16]; + new SecureRandom().nextBytes(var2); + new SecretKeySpec(var2, "AES"); + SecretKey key = AESUtil.generateKey(128); + final String base64 = AESUtil.generateIvByte(); + + // 固定iv密钥 + System.out.println(base64); IvParameterSpec ivParameterSpec = AESUtil.generateIv(); String algorithm = "AES/CBC/PKCS5Padding"; @@ -36,9 +42,7 @@ public void testStringEncrypt() } @Test - public void testGivenFile_whenEncrypt_thenSuccess() - throws NoSuchAlgorithmException, IOException, IllegalBlockSizeException, InvalidKeyException, - BadPaddingException, InvalidAlgorithmParameterException, NoSuchPaddingException { + public void testGivenFile_whenEncrypt_thenSuccess() throws Exception { // given SecretKey key = AESUtil.generateKey(128); String algorithm = "AES/CBC/PKCS5Padding"; @@ -59,10 +63,7 @@ public void testGivenFile_whenEncrypt_thenSuccess() } @Test - public void givenObject_whenEncrypt_thenSuccess() - throws NoSuchAlgorithmException, IllegalBlockSizeException, InvalidKeyException, - InvalidAlgorithmParameterException, NoSuchPaddingException, IOException, BadPaddingException, - ClassNotFoundException { + public void givenObject_whenEncrypt_thenSuccess() throws Exception { // given Student student = new Student("Baeldung", 20); SecretKey key = AESUtil.generateKey(128); @@ -78,14 +79,16 @@ public void givenObject_whenEncrypt_thenSuccess() } @Test - public void givenPassword_whenEncrypt_thenSuccess() - throws InvalidKeySpecException, NoSuchAlgorithmException, IllegalBlockSizeException, - InvalidKeyException, BadPaddingException, InvalidAlgorithmParameterException, NoSuchPaddingException { + public void givenPassword_whenEncrypt_thenSuccess() throws Exception { // given String plainText = "www.baeldung.com"; String password = "baeldung"; String salt = "12345678"; - IvParameterSpec ivParameterSpec = AESUtil.generateIv(); + final String base64 = AESUtil.generateIvByte(); + + // 固定iv密钥 + System.out.println(base64); + IvParameterSpec ivParameterSpec = AESUtil.generateIv(base64); SecretKey key = AESUtil.getKeyFromPassword(password, salt); // when @@ -95,4 +98,26 @@ public void givenPassword_whenEncrypt_thenSuccess() // then Assert.assertEquals(plainText, decryptedCipherText); } + + /** + */ + @Test + public void testConfigWay() throws Exception { + String plainText = "www.baeldung.com"; + String password = "baeldung"; + String salt = "12345678"; + final String iv = AESUtil.generateIvByte(); + final String result = encryptPass(plainText, password, salt, iv); + final String origin = decryptPass(result, password, salt, iv); + + Assert.assertEquals(origin, plainText); + + } + public String encryptPass(String text, String passwd, String salt, String iv) throws Exception { + return AESUtil.encryptPasswordBased(text, AESUtil.getKeyFromPassword(passwd, salt), AESUtil.generateIv(iv)); + } + + public String decryptPass(String text, String passwd, String salt, String iv) throws Exception { + return AESUtil.decryptPasswordBased(text, AESUtil.getKeyFromPassword(passwd, salt), AESUtil.generateIv(iv)); + } } diff --git a/class/src/test/java/security/aes/wx/WXBizMsgCryptTest.java b/class/src/test/java/security/aes/wx/WXBizMsgCryptTest.java index 75d87462..fa098be4 100644 --- a/class/src/test/java/security/aes/wx/WXBizMsgCryptTest.java +++ b/class/src/test/java/security/aes/wx/WXBizMsgCryptTest.java @@ -1,11 +1,8 @@ package security.aes.wx; import lombok.extern.slf4j.Slf4j; -import org.junit.*; +import org.junit.Test; import org.xml.sax.SAXException; -import security.aes.wx.AesException; -import security.aes.wx.SHA1; -import security.aes.wx.WXBizMsgCrypt; import javax.xml.parsers.ParserConfigurationException; import java.io.IOException; @@ -28,30 +25,13 @@ public class WXBizMsgCryptTest { String replyMsg2 = "1407743423"; String afterAesEncrypt2 = "jn1L23DB+6ELqJ+6bruv23M2GmYfkv0xBh2h+XTBOKVKcgDFHle6gqcZ1cZrk3e1qjPQ1F4RsLWzQRG9udbKWesxlkupqcEcW7ZQweImX9+wLMa0GaUzpkycA8+IamDBxn5loLgZpnS7fVAbExOkK5DYHBmv5tptA9tklE/fTIILHR8HLXa5nQvFb3tYPKAlHF3rtTeayNf0QuM+UW/wM9enGIDIJHF7CLHiDNAYxr+r+OrJCmPQyTy8cVWlu9iSvOHPT/77bZqJucQHQ04sq7KZI27OcqpQNSto2OdHCoTccjggX5Z9Mma0nMJBU+jLKJ38YB1fBIz+vBzsYjrTmFQ44YfeEuZ+xRTQwr92vhA9OxchWVINGC50qE/6lmkwWTwGX9wtQpsJKhP+oS7rvTY8+VdzETdfakjkwQ5/Xka042OlUb1/slTwo4RscuQ+RdxSGvDahxAJ6+EAjLt9d8igHngxIbf6YyqqROxuxqIeIch3CssH/LqRs+iAcILvApYZckqmA7FNERspKA5f8GoJ9sv8xmGvZ9Yrf57cExWtnX8aCMMaBropU/1k+hKP5LVdzbWCG0hGwx/dQudYR/eXp3P0XxjlFiy+9DMlaFExWUZQDajPkdPrEeOwofJb"; - @BeforeClass - public static void setUpBeforeClass() throws Exception { - } - - @AfterClass - public static void tearDownAfterClass() throws Exception { - } - - @Before - public void setUp() throws Exception { - - } - - @After - public void tearDown() throws Exception { - } - @Test public void testNormal() throws ParserConfigurationException, SAXException, IOException { try { WXBizMsgCrypt pc = new WXBizMsgCrypt(token, encodingAesKey, appId); String afterEncrpt = pc.encryptMsg(replyMsg); - //生成签名 + //生成签名 防止重放 String signature = SHA1.getSHA1(token, timestamp, nonce, afterEncrpt); // 第三方收到公众号平台发送的消息 diff --git a/class/src/test/java/security/aes/wx/AESTest.java b/class/src/test/java/security/aes/wx/WxMsgAESUtilTest.java similarity index 64% rename from class/src/test/java/security/aes/wx/AESTest.java rename to class/src/test/java/security/aes/wx/WxMsgAESUtilTest.java index 170fe9bc..6e99d638 100644 --- a/class/src/test/java/security/aes/wx/AESTest.java +++ b/class/src/test/java/security/aes/wx/WxMsgAESUtilTest.java @@ -3,8 +3,6 @@ import org.junit.Test; import java.nio.charset.StandardCharsets; -import java.security.Provider; -import java.security.Security; import static org.hamcrest.Matchers.equalTo; import static org.junit.Assert.assertThat; @@ -12,23 +10,19 @@ /** * @author https://github.com/kuangcp on 2020-11-01 21:35 */ -public class AESTest { +public class WxMsgAESUtilTest { @Test public void testEncrypt() throws Exception { // byte[] aesKey = Base64.getDecoder().decode("abcdefghijklmnopqrstuvwxyz0123456789ABCDEFG="); byte[] aesKey = "46EBA22EF5204DD5B110A1F730513965".getBytes(StandardCharsets.UTF_8); - final Provider[] providers = Security.getProviders(); - for (Provider provider : providers) { - System.out.println(provider); - } final String text = "123456"; - String target = AES.encrypt(aesKey, text); + String target = WxMsgAESUtil.encrypt(aesKey, text); System.out.println("密文: " + target); - String origin = AES.decrypt(aesKey, target); + String origin = WxMsgAESUtil.decrypt(aesKey, target); assertThat(origin, equalTo(text)); } } diff --git a/class/src/test/java/security/base/GuideTest.java b/class/src/test/java/security/base/GuideTest.java new file mode 100644 index 00000000..d9a54d0b --- /dev/null +++ b/class/src/test/java/security/base/GuideTest.java @@ -0,0 +1,19 @@ +package security.base; + +import org.junit.Test; + +import java.security.Provider; +import java.security.Security; + +/** + * @author Kuangcp on 2023-10-09 23:54 + */ +public class GuideTest { + @Test + public void testProvider() throws Exception { + final Provider[] providers = Security.getProviders(); + for (Provider provider : providers) { + System.out.println(provider); + } + } +} From f1611a97a6085319445f9ecf07d06ce2d693f1c4 Mon Sep 17 00:00:00 2001 From: kuangcp Date: Tue, 10 Oct 2023 11:09:37 +0800 Subject: [PATCH 304/476] reactor: netty --- .../antlr4/expression/EvalVisitor.java | 2 +- .../antlr4/expression/ExpressionTest.java | 2 +- .../com/github/kuangcp/io/ResourceTool.java | 2 +- .../com/github/kuangcp/io/ResourceTool.java | 2 +- pom.xml | 8 ++ .../com/github/kuangcp/situation/Readme.md | 2 +- spring/pom.xml | 17 ++++ .../reactor/netty/http/FirstHttpServer.java | 92 +++++++++++++++++++ spring/src/main/resources/logback.xml | 36 ++++++++ 9 files changed, 158 insertions(+), 5 deletions(-) create mode 100644 spring/src/main/java/com/github/kuangcp/reactor/netty/http/FirstHttpServer.java create mode 100644 spring/src/main/resources/logback.xml diff --git a/algorithms/src/main/java/com/github/kuangcp/antlr4/expression/EvalVisitor.java b/algorithms/src/main/java/com/github/kuangcp/antlr4/expression/EvalVisitor.java index f7071337..67832a5d 100644 --- a/algorithms/src/main/java/com/github/kuangcp/antlr4/expression/EvalVisitor.java +++ b/algorithms/src/main/java/com/github/kuangcp/antlr4/expression/EvalVisitor.java @@ -5,7 +5,7 @@ import lombok.extern.slf4j.Slf4j; /** - * @author kuangchengping@sinohealth.cn + * @author Github * 2023-09-01 13:46 */ @Slf4j diff --git a/algorithms/src/test/java/com/github/kuangcp/antlr4/expression/ExpressionTest.java b/algorithms/src/test/java/com/github/kuangcp/antlr4/expression/ExpressionTest.java index 4c181fe7..61ef7efa 100644 --- a/algorithms/src/test/java/com/github/kuangcp/antlr4/expression/ExpressionTest.java +++ b/algorithms/src/test/java/com/github/kuangcp/antlr4/expression/ExpressionTest.java @@ -10,7 +10,7 @@ import org.junit.Test; /** - * @author kuangchengping@sinohealth.cn + * @author Github * 2023-08-31 21:23 */ @Slf4j diff --git a/io/src/main/java/com/github/kuangcp/io/ResourceTool.java b/io/src/main/java/com/github/kuangcp/io/ResourceTool.java index 6b7c3485..07c8a9fd 100644 --- a/io/src/main/java/com/github/kuangcp/io/ResourceTool.java +++ b/io/src/main/java/com/github/kuangcp/io/ResourceTool.java @@ -5,7 +5,7 @@ import java.util.Objects; /** - * @author kuangchengping@sinohealth.cn + * @author Github * 2023-08-25 19:37 */ public class ResourceTool { diff --git a/network/src/main/java/com/github/kuangcp/io/ResourceTool.java b/network/src/main/java/com/github/kuangcp/io/ResourceTool.java index 6b7c3485..07c8a9fd 100644 --- a/network/src/main/java/com/github/kuangcp/io/ResourceTool.java +++ b/network/src/main/java/com/github/kuangcp/io/ResourceTool.java @@ -5,7 +5,7 @@ import java.util.Objects; /** - * @author kuangchengping@sinohealth.cn + * @author Github * 2023-08-25 19:37 */ public class ResourceTool { diff --git a/pom.xml b/pom.xml index f6ea7232..d8c97957 100644 --- a/pom.xml +++ b/pom.xml @@ -438,6 +438,14 @@ ${resilience4j.version} + + + io.projectreactor + reactor-bom + 2022.0.11 + pom + import + diff --git a/question/src/main/java/com/github/kuangcp/situation/Readme.md b/question/src/main/java/com/github/kuangcp/situation/Readme.md index 29821d73..33c7fed6 100644 --- a/question/src/main/java/com/github/kuangcp/situation/Readme.md +++ b/question/src/main/java/com/github/kuangcp/situation/Readme.md @@ -1 +1 @@ -# 特定场景下的故障 +# 特定场景下的问题 diff --git a/spring/pom.xml b/spring/pom.xml index b366eeb6..d2db862f 100644 --- a/spring/pom.xml +++ b/spring/pom.xml @@ -69,6 +69,23 @@ commons-dbcp + + com.fasterxml.jackson.core + jackson-core + + + com.fasterxml.jackson.core + jackson-databind + + + + io.projectreactor.netty + reactor-netty-core + + + io.projectreactor.netty + reactor-netty-http + diff --git a/spring/src/main/java/com/github/kuangcp/reactor/netty/http/FirstHttpServer.java b/spring/src/main/java/com/github/kuangcp/reactor/netty/http/FirstHttpServer.java new file mode 100644 index 00000000..97122813 --- /dev/null +++ b/spring/src/main/java/com/github/kuangcp/reactor/netty/http/FirstHttpServer.java @@ -0,0 +1,92 @@ +package com.github.kuangcp.reactor.netty.http; + + +import com.fasterxml.jackson.databind.ObjectMapper; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufAllocator; +import org.reactivestreams.Publisher; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.netty.DisposableServer; +import reactor.netty.http.server.HttpServer; +import reactor.netty.http.server.HttpServerRequest; +import reactor.netty.http.server.HttpServerResponse; + +import java.io.ByteArrayOutputStream; +import java.nio.charset.Charset; +import java.security.SecureRandom; +import java.time.Duration; +import java.util.function.BiFunction; +import java.util.function.Supplier; + +/** + * https://projectreactor.io/docs/netty/release/reference/index.html + * + * @author Github + * 2023-10-10 09:52 + */ +public class FirstHttpServer { + + public static void main(String[] args) { + DisposableServer server = HttpServer.create() + .route(routes -> routes + .get("/hello", + (request, response) -> response.sendString(Mono.just("Hello World!"))) + .post("/echo", + (request, response) -> response.send(request.receive().retain())) + .get("/path/{param}", + (request, response) -> response.sendString(Mono.just(request.param("param")))) + .get("/cal", (request, response) -> { + // 和传统SpringBoot项目对比 + // 内存配置 -Xmx500m hey -c 1200 -n 120000 http://127.0.0.1:32990/cal + // reactor-netty: 耗时 80s 90%RT为1s + // springboot-tomcat: 耗时 160s 90%RT为2.3s + long start = System.nanoTime(); + for (int i = 0; i < 1000; i++) { + Supplier random = () -> new SecureRandom().nextInt(10000000) + 100; + Math.tanh(Math.log(Math.sqrt(random.get()) + random.get())); + } + long end = System.nanoTime(); + return response.sendString(Mono.just((end - start) + "")); + }) + .ws("/ws", + (wsInbound, wsOutbound) -> wsOutbound.send(wsInbound.receive().retain())) + .get("/sse", serveSse()) + ) + .port(32990) + .bindNow(); + + server.onDispose().block(); + } + + /** + * Prepares SSE response + * The "Content-Type" is "text/event-stream" + * The flushing strategy is "flush after every element" emitted by the provided Publisher + */ + private static BiFunction> serveSse() { + Flux flux = Flux.interval(Duration.ofSeconds(3)); + return (request, response) -> + response.sse() + .send(flux.map(FirstHttpServer::toByteBuf), b -> true); + } + + /** + * Transforms the Object to ByteBuf following the expected SSE format. + */ + private static ByteBuf toByteBuf(Object any) { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + try { + out.write("data: ".getBytes(Charset.defaultCharset())); + MAPPER.writeValue(out, any); + out.write("\n\n".getBytes(Charset.defaultCharset())); + } catch (Exception e) { + throw new RuntimeException(e); + } + return ByteBufAllocator.DEFAULT + .buffer() + .writeBytes(out.toByteArray()); + } + + private static final ObjectMapper MAPPER = new ObjectMapper(); +} diff --git a/spring/src/main/resources/logback.xml b/spring/src/main/resources/logback.xml new file mode 100644 index 00000000..25273ba7 --- /dev/null +++ b/spring/src/main/resources/logback.xml @@ -0,0 +1,36 @@ + + + + + + + + + %d{HH:mm:ss.SSS} %highlight(%-5level) [%thread] %logger{30}#%highlight(%M):%yellow(%-3L) %msg%n + + + + DEBUG + + + + + + ${log.base}debug.log + true + + ${log.base}debug.%d{yyyy-MM-dd}.log + + + %d{MM-dd HH:mm:ss.SSS} |-%-5level |%thread| %logger{36}#%M:%L - %msg%n + + + DEBUG + + + + + + + + \ No newline at end of file From fe526936cf2757b08686ff2df830cbf493908ac5 Mon Sep 17 00:00:00 2001 From: kuangcp Date: Thu, 12 Oct 2023 11:53:15 +0800 Subject: [PATCH 305/476] wait lock --- .../github/kuangcp/time/wheel/TimeWheel.java | 562 +++++++++--------- .../kuangcp/time/wheel/TimeWheelTest.java | 290 ++++----- .../github/kuangcp/aop/xml/PersonDaoImpl.java | 4 - .../hibernate/annotation/PersonDaoImpl.java | 4 +- .../annotation/PersonServiceImpl.java | 4 +- .../kuangcp/jdbc/jdbc/PersonDaoImpl.java | 5 +- .../kuangcp/jdbc/jdbc/PersonDaoImpl2.java | 5 +- .../kuangcp/jdbc/jdbc/PersonDaoImpl3.java | 7 +- .../kuangcp/jdbc/jdbc/PersonRowMapper.java | 4 +- .../jdbc/jdbc/transaction/PersonDaoImpl.java | 1 - .../jdbc/transaction/PersonServiceImpl.java | 1 - .../transaction/annotation/PersonDaoImpl.java | 4 +- .../annotation/PersonServiceImpl.java | 4 +- .../com/github/kuangcp/mvc/PersonDaoImpl.java | 1 - .../github/kuangcp/mvc/PersonServiceImpl.java | 1 - .../kuangcp/mvc/annotation/PersonDaoImpl.java | 1 - .../mvc/annotation/PersonServiceImpl.java | 4 +- 17 files changed, 452 insertions(+), 450 deletions(-) diff --git a/algorithms/src/main/java/com/github/kuangcp/time/wheel/TimeWheel.java b/algorithms/src/main/java/com/github/kuangcp/time/wheel/TimeWheel.java index b80c711c..d1648b95 100644 --- a/algorithms/src/main/java/com/github/kuangcp/time/wheel/TimeWheel.java +++ b/algorithms/src/main/java/com/github/kuangcp/time/wheel/TimeWheel.java @@ -1,25 +1,17 @@ package com.github.kuangcp.time.wheel; +import lombok.extern.slf4j.Slf4j; + import java.time.Duration; import java.time.temporal.ChronoUnit; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.Map.Entry; -import java.util.Objects; -import java.util.concurrent.Callable; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; +import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicInteger; -import lombok.extern.slf4j.Slf4j; /** * https://github.com/wolaiye1010/zdc-java-script - * + *

* 类似于 HashMap的设计结构 设计支持 30 天内延迟任务 * * @author https://github.com/kuangcp on 2019-10-11 20:53 @@ -27,303 +19,317 @@ @Slf4j public class TimeWheel { - private boolean debugMode = false; - private volatile boolean stop = false; - private volatile boolean needExitVM = false; - - private volatile long startEmptyTime = -1; - private volatile long maxTimeout = 10L; - private volatile long lastCheckMills; - - private static final int SECONDS_SLOT = 60; - private static final int MINUTES_SLOT = 60; - private static final int HOURS_SLOT = 24; - private static final int DAYS_SLOT = 30; - - - private AtomicInteger currentSecond = new AtomicInteger(0); - private AtomicInteger currentMinute = new AtomicInteger(0); - private AtomicInteger currentHour = new AtomicInteger(0); - private AtomicInteger currentDay = new AtomicInteger(0); - - private ExecutorService pool = Executors.newFixedThreadPool(6); - private ExecutorService master = Executors.newSingleThreadExecutor(); - - // id -> task - private final Map> cacheTasks = new ConcurrentHashMap<>(); - private final Map[]> wheels = new ConcurrentHashMap<>(); - private final Map counters = new ConcurrentHashMap<>(); - private final Map slots = new ConcurrentHashMap<>(); - private final List sortedSlots = Arrays - .asList(ChronoUnit.SECONDS, ChronoUnit.MINUTES, ChronoUnit.HOURS, ChronoUnit.DAYS); - - { - init(); - } - - @SuppressWarnings("unchecked") - private void init() { - LinkedList[] secondsSlot = new LinkedList[SECONDS_SLOT]; - wheels.put(ChronoUnit.SECONDS, secondsSlot); - counters.put(ChronoUnit.SECONDS, currentSecond); - slots.put(ChronoUnit.SECONDS, SECONDS_SLOT); - - LinkedList[] minutesSlot = new LinkedList[MINUTES_SLOT]; - wheels.put(ChronoUnit.MINUTES, minutesSlot); - counters.put(ChronoUnit.MINUTES, currentMinute); - slots.put(ChronoUnit.MINUTES, MINUTES_SLOT); - - LinkedList[] hoursSlot = new LinkedList[HOURS_SLOT]; - wheels.put(ChronoUnit.HOURS, hoursSlot); - counters.put(ChronoUnit.HOURS, currentHour); - slots.put(ChronoUnit.HOURS, HOURS_SLOT); - - LinkedList[] daysSlot = new LinkedList[DAYS_SLOT]; - wheels.put(ChronoUnit.DAYS, daysSlot); - counters.put(ChronoUnit.DAYS, currentDay); - slots.put(ChronoUnit.DAYS, DAYS_SLOT); - } - - public TimeWheel() { - } - - public TimeWheel(long maxTimeout, boolean needExitVM, boolean debugMode) { - this.maxTimeout = maxTimeout; - this.needExitVM = needExitVM; - this.debugMode = debugMode; - } - - public void start() { - master.execute(() -> { - while (!stop) { - long currentMills = System.currentTimeMillis(); - try { - if (!debugMode) { - TimeUnit.MICROSECONDS.sleep(20); - if (currentMills - this.lastCheckMills < 1000) { - continue; - } - } - - log.debug("cache: keys={}", cacheTasks.keySet()); - if (cacheTasks.isEmpty()) { - if (startEmptyTime == -1) { - startEmptyTime = currentMills; - } else if (currentMills - startEmptyTime > maxTimeout) { - log.warn("no any tasks with timeout"); - shutdown(); - continue; - } - } + private boolean debugMode = false; + private volatile boolean stop = false; + private final Object lock = new Object(); + private volatile boolean needExitVM = false; - this.lastCheckMills = currentMills; - int seconds = this.currentSecond.incrementAndGet(); - this.pushTimeWheel(seconds); + private volatile long startEmptyTime = -1; + private volatile long maxTimeout = 10L; + private volatile long lastCheckMills; - LinkedList[] lists = wheels.get(ChronoUnit.SECONDS); - LinkedList list = lists[seconds % SECONDS_SLOT]; - this.invokeAll(seconds, list); - } catch (Exception e) { - log.error("", e); - } - } - }); - } - - private void removeAndAdd(ChronoUnit unit, int index) { - LinkedList[] lists = wheels.get(unit); - LinkedList list = lists[index]; - if (Objects.nonNull(list) && !list.isEmpty()) { - List nodes = list.toList(); - list.clear(); - for (TaskNode node : nodes) { - long millis = node.getLastTime() + node.getDelayMills() - System.currentTimeMillis(); - if (debugMode) { - millis -= SECONDS_SLOT * 1000; - } - if (millis < 0) { - log.error("system invoke task delay: delay={} node={}", millis, node); - } + private static final int SECONDS_SLOT = 60; + private static final int MINUTES_SLOT = 60; + private static final int HOURS_SLOT = 24; + private static final int DAYS_SLOT = 30; - Duration delay = Duration.ofMillis(millis); - log.debug(": delay={}", delay); - insertWheel(node.getId(), delay); - } - } - } - - // TODO BUG - private void pushTimeWheel(int seconds) { - Map tempIndex = new HashMap<>(sortedSlots.size()); - for (int i = 0; i < sortedSlots.size(); i++) { - ChronoUnit unit = sortedSlots.get(i); - Integer threshold = slots.get(unit); - int upIndex = -1; - - // 最小轮 - if (i == 0) { - if (seconds >= threshold) { - int up = seconds / threshold; - AtomicInteger counter = counters.get(unit); - counter.set(0); - upIndex = counters.get(sortedSlots.get(i + 1)).addAndGet(up); - } else { - break; - } - // 最大轮 - } else if (i == sortedSlots.size() - 1) { - AtomicInteger counter = counters.get(unit); - counter.set(0); - this.removeAndAdd(unit, 0); - upIndex = -1; - } else { - AtomicInteger counter = counters.get(unit); - if (counter.get() >= threshold) { - counter.set(0); - upIndex = counters.get(sortedSlots.get(i + 1)).incrementAndGet(); - } - } - if (upIndex != -1) { - tempIndex.put(sortedSlots.get(i + 1), upIndex); - } - } -// if (!tempIndex.isEmpty()) { -// log.info(": tempIndex={}", tempIndex); -// } + private AtomicInteger currentSecond = new AtomicInteger(0); + private AtomicInteger currentMinute = new AtomicInteger(0); + private AtomicInteger currentHour = new AtomicInteger(0); + private AtomicInteger currentDay = new AtomicInteger(0); - if (!tempIndex.isEmpty()) { - for (Entry entry : tempIndex.entrySet()) { - ChronoUnit unit = entry.getKey(); - Integer value = entry.getValue(); - this.removeAndAdd(unit, value % slots.get(unit)); - } - } - } + private ExecutorService pool = Executors.newFixedThreadPool(6); + private ExecutorService master = Executors.newSingleThreadExecutor(); - private void invokeAll(int seconds, LinkedList list) { - if (Objects.isNull(list) || list.isEmpty()) { - log.debug("no tasks need invoke"); - return; - } + // id -> task + private final Map> cacheTasks = new ConcurrentHashMap<>(); + private final Map[]> wheels = new ConcurrentHashMap<>(); + private final Map counters = new ConcurrentHashMap<>(); + private final Map slots = new ConcurrentHashMap<>(); + private final List sortedSlots = Arrays + .asList(ChronoUnit.SECONDS, ChronoUnit.MINUTES, ChronoUnit.HOURS, ChronoUnit.DAYS); - List nodes = list.toList(); - for (TaskNode node : nodes) { - String id = node.getId(); - Callable func = cacheTasks.get(id); - log.debug("[{}]before invoke: id={}", seconds, id); - try { - Future result = pool.submit(func); - log.info("[{}:{}:{}] id={} result={}", this.currentSecond.get(), this.currentMinute.get(), - this.currentHour.get(), id, result.get()); - cacheTasks.remove(id); - } catch (Exception e) { - log.error("", e); - } - } - list.clear(); - log.debug("[{}]invoke all tasks successful.", seconds); - } - - public void shutdown() { - this.stop = true; - log.warn("shutdown"); - if (needExitVM) { - System.exit(1); + { + init(); } - } - public boolean add(String id, Callable func, Duration delay) { - if (Objects.isNull(delay)) { - log.warn("delay is null: id={}", id); - return false; + @SuppressWarnings("unchecked") + private void init() { + LinkedList[] secondsSlot = new LinkedList[SECONDS_SLOT]; + wheels.put(ChronoUnit.SECONDS, secondsSlot); + counters.put(ChronoUnit.SECONDS, currentSecond); + slots.put(ChronoUnit.SECONDS, SECONDS_SLOT); + + LinkedList[] minutesSlot = new LinkedList[MINUTES_SLOT]; + wheels.put(ChronoUnit.MINUTES, minutesSlot); + counters.put(ChronoUnit.MINUTES, currentMinute); + slots.put(ChronoUnit.MINUTES, MINUTES_SLOT); + + LinkedList[] hoursSlot = new LinkedList[HOURS_SLOT]; + wheels.put(ChronoUnit.HOURS, hoursSlot); + counters.put(ChronoUnit.HOURS, currentHour); + slots.put(ChronoUnit.HOURS, HOURS_SLOT); + + LinkedList[] daysSlot = new LinkedList[DAYS_SLOT]; + wheels.put(ChronoUnit.DAYS, daysSlot); + counters.put(ChronoUnit.DAYS, currentDay); + slots.put(ChronoUnit.DAYS, DAYS_SLOT); } - if (Objects.isNull(func)) { - log.warn("func is null: id={}", id); - return false; + + public TimeWheel() { } - if (cacheTasks.containsKey(id)) { - log.warn("task has already exist"); - return false; + public TimeWheel(long maxTimeout, boolean needExitVM, boolean debugMode) { + this.maxTimeout = maxTimeout; + this.needExitVM = needExitVM; + this.debugMode = debugMode; } - boolean result = insertWheel(id, delay); - if (result) { - cacheTasks.put(id, func); + public void start() { + master.execute(() -> { + while (!stop) { + long currentMills = System.currentTimeMillis(); + try { + if (!debugMode) { + TimeUnit.MICROSECONDS.sleep(20); + if (currentMills - this.lastCheckMills < 1000) { + continue; + } + } + + log.debug("cache: keys={}", cacheTasks.keySet()); + if (cacheTasks.isEmpty()) { + if (startEmptyTime == -1) { + startEmptyTime = currentMills; + } else if (currentMills - startEmptyTime > maxTimeout) { + log.warn("not exist task wait and idle timeout"); + shutdown(); + continue; + } + } + + this.lastCheckMills = currentMills; + int seconds = this.currentSecond.incrementAndGet(); + this.pushTimeWheel(seconds); + + LinkedList[] lists = wheels.get(ChronoUnit.SECONDS); + LinkedList list = lists[seconds % SECONDS_SLOT]; + this.invokeAll(seconds, list); + } catch (Exception e) { + log.error("", e); + } + } + }); } - return result; - } - - public void printWheel() { - log.info("sec:{} min:{} hour:{}", this.currentSecond.get(), this.currentMinute.get(), - this.currentHour.get()); - for (ChronoUnit unit : wheels.keySet()) { - LinkedList[] lists = wheels.get(unit); - - boolean hasData = false; - StringBuilder builder = new StringBuilder(unit.toString()); - for (int i = 0; i < lists.length; i++) { - LinkedList list = lists[i]; + + private void removeAndAdd(ChronoUnit unit, int index) { + LinkedList[] lists = wheels.get(unit); + LinkedList list = lists[index]; if (Objects.nonNull(list) && !list.isEmpty()) { - hasData = true; - builder.append(String.format("[%2s] ids=%s. ", i, list.toSimpleString())); + List nodes = list.toList(); + list.clear(); + for (TaskNode node : nodes) { + long millis = node.getLastTime() + node.getDelayMills() - System.currentTimeMillis(); + if (debugMode) { + millis -= SECONDS_SLOT * 1000; + } + if (millis < 0) { + log.error("system invoke task delay: delay={} node={}", millis, node); + } + + Duration delay = Duration.ofMillis(millis); + log.debug(": delay={}", delay); + insertWheel(node.getId(), delay); + } + } + } + + // TODO BUG + private void pushTimeWheel(int seconds) { + Map tempIndex = new HashMap<>(sortedSlots.size()); + for (int i = 0; i < sortedSlots.size(); i++) { + ChronoUnit unit = sortedSlots.get(i); + Integer threshold = slots.get(unit); + int upIndex = -1; + + // 最小轮 + if (i == 0) { + if (seconds >= threshold) { + int up = seconds / threshold; + AtomicInteger counter = counters.get(unit); + counter.set(0); + upIndex = counters.get(sortedSlots.get(i + 1)).addAndGet(up); + } else { + break; + } + // 最大轮 + } else if (i == sortedSlots.size() - 1) { + AtomicInteger counter = counters.get(unit); + counter.set(0); + this.removeAndAdd(unit, 0); + upIndex = -1; + } else { + AtomicInteger counter = counters.get(unit); + if (counter.get() >= threshold) { + counter.set(0); + upIndex = counters.get(sortedSlots.get(i + 1)).incrementAndGet(); + } + } + + if (upIndex != -1) { + tempIndex.put(sortedSlots.get(i + 1), upIndex); + } } - } +// if (!tempIndex.isEmpty()) { +// log.info(": tempIndex={}", tempIndex); +// } - if (hasData) { - log.info(builder.toString()); - } + if (!tempIndex.isEmpty()) { + for (Entry entry : tempIndex.entrySet()) { + ChronoUnit unit = entry.getKey(); + Integer value = entry.getValue(); + this.removeAndAdd(unit, value % slots.get(unit)); + } + } } - } - private boolean insertWheel(String id, Duration delay) { - long days = delay.toDays(); - if (days > 30) { - log.warn("out of timeWheel max delay bound"); - return false; + private void invokeAll(int seconds, LinkedList list) { + if (Objects.isNull(list) || list.isEmpty()) { + log.debug("no tasks need invoke"); + return; + } + + List nodes = list.toList(); + for (TaskNode node : nodes) { + String id = node.getId(); + Callable func = cacheTasks.get(id); + log.debug("[{}]before invoke: id={}", seconds, id); + try { + Future result = pool.submit(func); + log.info("[{}:{}:{}] id={} result={}", this.currentSecond.get(), this.currentMinute.get(), + this.currentHour.get(), id, result.get()); + cacheTasks.remove(id); + } catch (Exception e) { + log.error("", e); + } + } + list.clear(); + log.debug("[{}]invoke all tasks successful.", seconds); } - long current = System.currentTimeMillis(); - long delayMills = delay.toMillis(); - TaskNode node = new TaskNode(id, null, current, delayMills); - if (days > 0) { - return insertWheel(ChronoUnit.DAYS, this.currentDay.get() + days, node); + public void blockWait() throws InterruptedException { + this.blockWait(0); } - long hours = delay.toHours(); - if (hours > 0) { - return insertWheel(ChronoUnit.HOURS, this.currentHour.get() + hours, node); + public void blockWait(long timeout) throws InterruptedException { + synchronized (lock) { + lock.wait(timeout); + } } - long minutes = delay.toMinutes(); - if (minutes > 0) { - return insertWheel(ChronoUnit.MINUTES, this.currentMinute.get() + minutes, node); + public void shutdown() { + this.stop = true; + log.warn("shutdown"); + synchronized (lock) { + lock.notify(); + } + if (needExitVM) { + System.exit(1); + } } - long seconds = delay.getSeconds(); - // TODO expire too long time - if (seconds >= -10 && seconds <= 1) { - return insertWheel(ChronoUnit.SECONDS, this.currentSecond.get() + 1, node); + public boolean add(String id, Callable func, Duration delay) { + if (Objects.isNull(delay)) { + log.warn("delay is null: id={}", id); + return false; + } + if (Objects.isNull(func)) { + log.warn("func is null: id={}", id); + return false; + } + + if (cacheTasks.containsKey(id)) { + log.warn("task has already exist"); + return false; + } + + boolean result = insertWheel(id, delay); + if (result) { + cacheTasks.put(id, func); + } + return result; } - if (seconds > 1) { - return insertWheel(ChronoUnit.SECONDS, this.currentSecond.get() + seconds, node); + + public void printWheel() { + log.info("sec:{} min:{} hour:{}", this.currentSecond.get(), this.currentMinute.get(), + this.currentHour.get()); + for (ChronoUnit unit : wheels.keySet()) { + LinkedList[] lists = wheels.get(unit); + + boolean hasData = false; + StringBuilder builder = new StringBuilder(unit.toString()); + for (int i = 0; i < lists.length; i++) { + LinkedList list = lists[i]; + if (Objects.nonNull(list) && !list.isEmpty()) { + hasData = true; + builder.append(String.format("[%2s] ids=%s. ", i, list.toSimpleString())); + } + } + + if (hasData) { + log.info(builder.toString()); + } + } } - log.warn("task is expire: id={} delay={}", id, delay); - return false; - } + private boolean insertWheel(String id, Duration delay) { + long days = delay.toDays(); + if (days > 30) { + log.warn("out of timeWheel max delay bound"); + return false; + } + + long current = System.currentTimeMillis(); + long delayMills = delay.toMillis(); + TaskNode node = new TaskNode(id, null, current, delayMills); + if (days > 0) { + return insertWheel(ChronoUnit.DAYS, this.currentDay.get() + days, node); + } + + long hours = delay.toHours(); + if (hours > 0) { + return insertWheel(ChronoUnit.HOURS, this.currentHour.get() + hours, node); + } + + long minutes = delay.toMinutes(); + if (minutes > 0) { + return insertWheel(ChronoUnit.MINUTES, this.currentMinute.get() + minutes, node); + } + + long seconds = delay.getSeconds(); + // TODO expire too long time + if (seconds >= -10 && seconds <= 1) { + return insertWheel(ChronoUnit.SECONDS, this.currentSecond.get() + 1, node); + } + if (seconds > 1) { + return insertWheel(ChronoUnit.SECONDS, this.currentSecond.get() + seconds, node); + } + + log.warn("task is expire: id={} delay={}", id, delay); + return false; + } - private boolean insertWheel(ChronoUnit unit, long index, TaskNode node) { - Integer slot = slots.get(unit); - int idx = (int) index % slot; + private boolean insertWheel(ChronoUnit unit, long index, TaskNode node) { + Integer slot = slots.get(unit); + int idx = (int) index % slot; - LinkedList[] lists = wheels.get(unit); - LinkedList list = lists[idx]; - if (Objects.isNull(list)) { - list = new LinkedList<>(); - lists[idx] = list; + LinkedList[] lists = wheels.get(unit); + LinkedList list = lists[idx]; + if (Objects.isNull(list)) { + list = new LinkedList<>(); + lists[idx] = list; + } + return list.add(node); } - return list.add(node); - } } \ No newline at end of file diff --git a/algorithms/src/test/java/com/github/kuangcp/time/wheel/TimeWheelTest.java b/algorithms/src/test/java/com/github/kuangcp/time/wheel/TimeWheelTest.java index 3bfd4c57..a3b97dbb 100644 --- a/algorithms/src/test/java/com/github/kuangcp/time/wheel/TimeWheelTest.java +++ b/algorithms/src/test/java/com/github/kuangcp/time/wheel/TimeWheelTest.java @@ -1,21 +1,19 @@ package com.github.kuangcp.time.wheel; +import lombok.extern.slf4j.Slf4j; +import org.junit.Assert; +import org.junit.Ignore; +import org.junit.Test; + +import java.security.SecureRandom; import java.time.Duration; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.Map.Entry; -import java.util.Optional; -import java.util.OptionalInt; +import java.util.concurrent.Callable; import java.util.concurrent.TimeUnit; +import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.stream.IntStream; -import lombok.extern.slf4j.Slf4j; -import org.junit.Assert; -import org.junit.Ignore; -import org.junit.Test; /** * @author https://github.com/kuangcp on 2019-10-27 12:15 @@ -24,149 +22,159 @@ @Slf4j public class TimeWheelTest { - private TimeWheel timeWheel = new TimeWheel(10L, true, true); + private final TimeWheel timeWheel = new TimeWheel(10L, true, true); - @Test - public void testAdd() throws Exception { - boolean result = timeWheel.add("id", () -> null, Duration.ofMillis(10000)); - Assert.assertTrue(result); - } + @Test + public void testAdd() throws Exception { + boolean result = timeWheel.add("id", () -> null, Duration.ofMillis(10000)); + Assert.assertTrue(result); + } - @Test - public void testRun1() { - for (int i = 0; i < 7; i++) { - boolean result = timeWheel.add("id" + i, () -> null, Duration.ofMillis(i * 3000)); - log.debug(": result={}", result); + @Test + public void testRun1() { + for (int i = 0; i < 7; i++) { + boolean result = timeWheel.add("id" + i, () -> null, Duration.ofMillis(i * 3000)); + log.debug(": result={}", result); + } + timeWheel.start(); + join(); } - timeWheel.start(); - join(); - } - - private void join() { - try { - Thread.currentThread().join(); - } catch (Exception e) { - log.error("", e); + + private void join() { + try { + Thread.currentThread().join(); + } catch (Exception e) { + log.error("", e); + } } - } - @Test - public void testRun2() { - for (int i = 0; i < 13; i++) { - boolean result = timeWheel.add("id" + i, () -> "fds", Duration.ofMillis(5000)); - log.info(": result={}", result); + @Test + public void testRun2() throws InterruptedException { + Callable longCall = () -> { + long start = System.nanoTime(); + for (int i = 0; i < 10000; i++) { + Supplier random = () -> new SecureRandom().nextInt(10000000) + 100; + Math.tanh(Math.log(Math.sqrt(random.get()) + random.get())); + } + long end = System.nanoTime(); + + return (end - start) + ""; + }; + + TimeWheel timeWheel = new TimeWheel(10L, false, true); + for (int x = 0; x < 50; x++) { + boolean result = timeWheel.add("id-" + x, longCall, Duration.ofMillis(5000)); + if (!result) { + log.info("add failed"); + } + } + timeWheel.start(); + + timeWheel.blockWait(); + log.info("finish"); } - timeWheel.start(); - try { - TimeUnit.SECONDS.sleep(10); - timeWheel.shutdown(); - } catch (Exception e) { - log.error("", e); + private long calculate() { + runRecord.add(System.currentTimeMillis()); + + OptionalInt reduce = IntStream.rangeClosed(0, 200).reduce(Integer::sum); + return reduce.orElse(0); } - } - - private long calculate() { - runRecord.add(System.currentTimeMillis()); - - OptionalInt reduce = IntStream.rangeClosed(0, 200).reduce(Integer::sum); - return reduce.orElse(0); - } - - private long calculateComplex() { - runRecord.add(System.currentTimeMillis()); - - Optional sumOpt = IntStream.rangeClosed(0, 990000) - .mapToObj(Long::valueOf) - .sorted(Comparator.comparing(Long::intValue).reversed()) - .sorted(Comparator.comparing(Long::intValue)) - .sorted(Comparator.comparing(Long::intValue).reversed()) - .sorted(Comparator.comparing(Long::intValue)) - .sorted(Comparator.comparing(Long::intValue).reversed()) - .sorted(Comparator.comparing(Long::intValue)) - .sorted(Comparator.comparing(Long::intValue).reversed()) - .sorted(Comparator.comparing(Long::intValue)) - .sorted(Comparator.comparing(Long::intValue).reversed()) - .sorted(Comparator.comparing(Long::intValue)) - .sorted(Comparator.comparing(Long::intValue).reversed()) - .sorted(Comparator.comparing(Long::intValue)) - .sorted(Comparator.comparing(Long::intValue).reversed()) - .sorted(Comparator.comparing(Long::intValue)) - .sorted(Comparator.comparing(Long::intValue).reversed()) - .sorted(Comparator.comparing(Long::intValue)) - .sorted(Comparator.comparing(Long::intValue).reversed()) - .sorted(Comparator.comparing(Long::intValue)) - .sorted(Comparator.comparing(Long::intValue).reversed()) - .sorted(Comparator.comparing(Long::intValue)) - .sorted(Comparator.comparing(Long::intValue).reversed()) - .sorted(Comparator.comparing(Long::intValue)) - .sorted(Comparator.comparing(Long::intValue).reversed()) - .sorted(Comparator.comparing(Long::intValue)) - .sorted(Comparator.comparing(Long::intValue).reversed()) - .sorted(Comparator.comparing(Long::intValue)) - .sorted(Comparator.comparing(Long::intValue).reversed()) - .sorted(Comparator.comparing(Long::intValue)) - .sorted(Comparator.comparing(Long::intValue).reversed()) - .sorted(Comparator.comparing(Long::intValue)) - .sorted(Comparator.comparing(Long::intValue).reversed()) - .sorted(Comparator.comparing(Long::intValue)) - .sorted(Comparator.comparing(Long::intValue).reversed()) - .reduce(Long::sum); - return sumOpt.orElse(0L); - } - - // id -> delay mills - private Map inputData = new HashMap<>(); - // id -> start run mills - private List runRecord = new ArrayList<>(); - private long startTime; - - private void initOverMinData() { - int dataSize = 60 * 60 * 2; - dataSize = 10; - for (int i = 1; i < dataSize; i++) { - inputData.put("id" + i, i * 65_000L); + + private long calculateComplex() { + runRecord.add(System.currentTimeMillis()); + + Optional sumOpt = IntStream.rangeClosed(0, 990000) + .mapToObj(Long::valueOf) + .sorted(Comparator.comparing(Long::intValue).reversed()) + .sorted(Comparator.comparing(Long::intValue)) + .sorted(Comparator.comparing(Long::intValue).reversed()) + .sorted(Comparator.comparing(Long::intValue)) + .sorted(Comparator.comparing(Long::intValue).reversed()) + .sorted(Comparator.comparing(Long::intValue)) + .sorted(Comparator.comparing(Long::intValue).reversed()) + .sorted(Comparator.comparing(Long::intValue)) + .sorted(Comparator.comparing(Long::intValue).reversed()) + .sorted(Comparator.comparing(Long::intValue)) + .sorted(Comparator.comparing(Long::intValue).reversed()) + .sorted(Comparator.comparing(Long::intValue)) + .sorted(Comparator.comparing(Long::intValue).reversed()) + .sorted(Comparator.comparing(Long::intValue)) + .sorted(Comparator.comparing(Long::intValue).reversed()) + .sorted(Comparator.comparing(Long::intValue)) + .sorted(Comparator.comparing(Long::intValue).reversed()) + .sorted(Comparator.comparing(Long::intValue)) + .sorted(Comparator.comparing(Long::intValue).reversed()) + .sorted(Comparator.comparing(Long::intValue)) + .sorted(Comparator.comparing(Long::intValue).reversed()) + .sorted(Comparator.comparing(Long::intValue)) + .sorted(Comparator.comparing(Long::intValue).reversed()) + .sorted(Comparator.comparing(Long::intValue)) + .sorted(Comparator.comparing(Long::intValue).reversed()) + .sorted(Comparator.comparing(Long::intValue)) + .sorted(Comparator.comparing(Long::intValue).reversed()) + .sorted(Comparator.comparing(Long::intValue)) + .sorted(Comparator.comparing(Long::intValue).reversed()) + .sorted(Comparator.comparing(Long::intValue)) + .sorted(Comparator.comparing(Long::intValue).reversed()) + .sorted(Comparator.comparing(Long::intValue)) + .sorted(Comparator.comparing(Long::intValue).reversed()) + .reduce(Long::sum); + return sumOpt.orElse(0L); } - } - - private void showRecords() { - List> entryList = inputData.entrySet().stream() - .sorted(Comparator.comparing(Entry::getValue)) - .collect(Collectors.toList()); - for (int i = 0; i < entryList.size(); i++) { - Long time = runRecord.get(i); - Long delayMills = entryList.get(i).getValue(); - log.info("{}:{} out={} runTime={}", entryList.get(i), time - startTime, - time - startTime - delayMills, time); + + // id -> delay mills + private Map inputData = new HashMap<>(); + // id -> start run mills + private List runRecord = new ArrayList<>(); + private long startTime; + + private void initOverMinData() { + int dataSize = 60 * 60 * 2; + dataSize = 10; + for (int i = 1; i < dataSize; i++) { + inputData.put("id" + i, i * 65_000L); + } } - } - - // task execute time should << time wheel min slot - @Test - public void testRushLoop() { - initOverMinData(); - for (Entry entry : inputData.entrySet()) { - boolean result = timeWheel - .add(entry.getKey(), this::calculate, Duration.ofMillis(entry.getValue())); - log.debug("add task: id={} result={}", entry.getKey(), result); + + private void showRecords() { + List> entryList = inputData.entrySet().stream() + .sorted(Comparator.comparing(Entry::getValue)) + .collect(Collectors.toList()); + for (int i = 0; i < entryList.size(); i++) { + Long time = runRecord.get(i); + Long delayMills = entryList.get(i).getValue(); + log.info("{}:{} out={} runTime={}", entryList.get(i), time - startTime, + time - startTime - delayMills, time); + } } - timeWheel.printWheel(); - timeWheel.start(); - startTime = System.currentTimeMillis(); - Runtime.getRuntime().addShutdownHook(new Thread(this::showRecords)); + // task execute time should << time wheel min slot + @Test + public void testRushLoop() { + initOverMinData(); + for (Entry entry : inputData.entrySet()) { + boolean result = timeWheel + .add(entry.getKey(), this::calculate, Duration.ofMillis(entry.getValue())); + log.debug("add task: id={} result={}", entry.getKey(), result); + } - new Thread(() -> { - while (true) { timeWheel.printWheel(); - try { - TimeUnit.SECONDS.sleep(2); - } catch (InterruptedException e) { - log.error("", e); - } - } - }).start(); - join(); - } + timeWheel.start(); + startTime = System.currentTimeMillis(); + Runtime.getRuntime().addShutdownHook(new Thread(this::showRecords)); + + new Thread(() -> { + while (true) { + timeWheel.printWheel(); + try { + TimeUnit.SECONDS.sleep(2); + } catch (InterruptedException e) { + log.error("", e); + } + } + }).start(); + join(); + } } diff --git a/spring/src/test/java/com/github/kuangcp/aop/xml/PersonDaoImpl.java b/spring/src/test/java/com/github/kuangcp/aop/xml/PersonDaoImpl.java index 58a83831..dd3f5e0a 100644 --- a/spring/src/test/java/com/github/kuangcp/aop/xml/PersonDaoImpl.java +++ b/spring/src/test/java/com/github/kuangcp/aop/xml/PersonDaoImpl.java @@ -7,25 +7,21 @@ public class PersonDaoImpl { public void savePerson() { - // TODO Auto-generated method stub System.out.println("save person"); } public void updatePerson() { - // TODO Auto-generated method stub System.out.println("update person"); } public void deletePerson() { - // TODO Auto-generated method stub System.out.println("delete person"); } public List getPerson() { - // TODO Auto-generated method stub Person person = new Person(); person.setPid(1L); diff --git a/spring/src/test/java/com/github/kuangcp/hibernate/annotation/PersonDaoImpl.java b/spring/src/test/java/com/github/kuangcp/hibernate/annotation/PersonDaoImpl.java index b5bdf2f9..3989c053 100644 --- a/spring/src/test/java/com/github/kuangcp/hibernate/annotation/PersonDaoImpl.java +++ b/spring/src/test/java/com/github/kuangcp/hibernate/annotation/PersonDaoImpl.java @@ -1,9 +1,10 @@ package com.github.kuangcp.hibernate.annotation; -import javax.annotation.Resource; import org.springframework.orm.hibernate5.HibernateTemplate; import org.springframework.stereotype.Repository; +import javax.annotation.Resource; + @Repository("personDao") public class PersonDaoImpl implements PersonDao { @@ -12,7 +13,6 @@ public class PersonDaoImpl implements PersonDao { @Override public void savePerson(Person person) { - // TODO Auto-generated method stub this.hibernateTemplate.save(person); } } diff --git a/spring/src/test/java/com/github/kuangcp/hibernate/annotation/PersonServiceImpl.java b/spring/src/test/java/com/github/kuangcp/hibernate/annotation/PersonServiceImpl.java index 650d88c9..24745bd4 100644 --- a/spring/src/test/java/com/github/kuangcp/hibernate/annotation/PersonServiceImpl.java +++ b/spring/src/test/java/com/github/kuangcp/hibernate/annotation/PersonServiceImpl.java @@ -1,9 +1,10 @@ package com.github.kuangcp.hibernate.annotation; -import javax.annotation.Resource; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import javax.annotation.Resource; + @Service("personService") public class PersonServiceImpl implements PersonService { @@ -12,7 +13,6 @@ public class PersonServiceImpl implements PersonService { @Transactional(readOnly = false) public void savePerson(Person person) { - // TODO Auto-generated method stub this.personDao.savePerson(person); } diff --git a/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/PersonDaoImpl.java b/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/PersonDaoImpl.java index acb66a08..202dff8d 100644 --- a/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/PersonDaoImpl.java +++ b/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/PersonDaoImpl.java @@ -1,12 +1,12 @@ package com.github.kuangcp.jdbc.jdbc; -import java.util.List; import org.springframework.jdbc.core.support.JdbcDaoSupport; +import java.util.List; + public class PersonDaoImpl extends JdbcDaoSupport implements PersonDao { public void savePerson() { - // TODO Auto-generated method stub this.getJdbcTemplate().execute( "insert into course(cid,cname) values(4,'aaa')"); int a = 1 / 0; @@ -16,7 +16,6 @@ public void savePerson() { @Override public List getPersons() { - // TODO Auto-generated method stub return this.getJdbcTemplate().query("select * from course", new PersonRowMapper()); } } diff --git a/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/PersonDaoImpl2.java b/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/PersonDaoImpl2.java index 8f04ff5c..b33b96ea 100644 --- a/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/PersonDaoImpl2.java +++ b/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/PersonDaoImpl2.java @@ -1,8 +1,9 @@ package com.github.kuangcp.jdbc.jdbc; -import java.util.List; import org.springframework.jdbc.core.JdbcTemplate; +import java.util.List; + public class PersonDaoImpl2 implements PersonDao { private JdbcTemplate jdbcTemplate; @@ -16,14 +17,12 @@ public void setJdbcTemplate(JdbcTemplate jdbcTemplate) { } public void savePerson() { - // TODO Auto-generated method stub this.getJdbcTemplate().execute( "insert into course(cid,cname) values(4,'aaa')"); } @Override public List getPersons() { - // TODO Auto-generated method stub return null; } } diff --git a/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/PersonDaoImpl3.java b/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/PersonDaoImpl3.java index 36523763..9e3ef258 100644 --- a/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/PersonDaoImpl3.java +++ b/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/PersonDaoImpl3.java @@ -1,9 +1,10 @@ package com.github.kuangcp.jdbc.jdbc; -import java.util.List; -import javax.sql.DataSource; import org.springframework.jdbc.core.JdbcTemplate; +import javax.sql.DataSource; +import java.util.List; + public class PersonDaoImpl3 extends JdbcTemplate implements PersonDao { public PersonDaoImpl3(DataSource dataSource) { @@ -11,14 +12,12 @@ public PersonDaoImpl3(DataSource dataSource) { } public void savePerson() { - // TODO Auto-generated method stub this.execute( "insert into course(cid,cname) values(4,'aaa')"); } @Override public List getPersons() { - // TODO Auto-generated method stub return null; } } diff --git a/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/PersonRowMapper.java b/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/PersonRowMapper.java index 9b5bf7eb..8554c7d0 100644 --- a/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/PersonRowMapper.java +++ b/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/PersonRowMapper.java @@ -1,14 +1,14 @@ package com.github.kuangcp.jdbc.jdbc; +import org.springframework.jdbc.core.RowMapper; + import java.sql.ResultSet; import java.sql.SQLException; -import org.springframework.jdbc.core.RowMapper; public class PersonRowMapper implements RowMapper { @Override public Object mapRow(ResultSet rs, int rowNum) throws SQLException { - // TODO Auto-generated method stub Person person = new Person(); person.setPid(rs.getLong("cid")); person.setPname(rs.getString("cname")); diff --git a/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/PersonDaoImpl.java b/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/PersonDaoImpl.java index d9b129ea..c3eb161e 100644 --- a/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/PersonDaoImpl.java +++ b/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/PersonDaoImpl.java @@ -6,7 +6,6 @@ public class PersonDaoImpl extends JdbcDaoSupport implements PersonDao { @Override public void savePerson() { - // TODO Auto-generated method stub this.getJdbcTemplate().execute("insert into person(pname) values('aa')"); int a = 1 / 0; this.getJdbcTemplate().execute("insert into person(pname) values('aa')"); diff --git a/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/PersonServiceImpl.java b/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/PersonServiceImpl.java index 4f67aabf..a61d26c5 100644 --- a/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/PersonServiceImpl.java +++ b/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/PersonServiceImpl.java @@ -15,7 +15,6 @@ public void setPersonDao(PersonDao personDao) { @Override public void savePerson() { - // TODO Auto-generated method stub this.personDao.savePerson(); } diff --git a/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/annotation/PersonDaoImpl.java b/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/annotation/PersonDaoImpl.java index b67a7aea..232104ed 100644 --- a/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/annotation/PersonDaoImpl.java +++ b/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/annotation/PersonDaoImpl.java @@ -1,9 +1,10 @@ package com.github.kuangcp.jdbc.jdbc.transaction.annotation; -import javax.annotation.Resource; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository; +import javax.annotation.Resource; + @Repository("personDao") public class PersonDaoImpl implements PersonDao { @@ -12,7 +13,6 @@ public class PersonDaoImpl implements PersonDao { @Override public void savePerson() { - // TODO Auto-generated method stub this.jdbcTemplate.execute("insert into person(pname) values('aa')"); int a = 1 / 0; this.jdbcTemplate.execute("insert into person(pname) values('aa')"); diff --git a/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/annotation/PersonServiceImpl.java b/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/annotation/PersonServiceImpl.java index 5663bee4..ccefd750 100644 --- a/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/annotation/PersonServiceImpl.java +++ b/spring/src/test/java/com/github/kuangcp/jdbc/jdbc/transaction/annotation/PersonServiceImpl.java @@ -1,9 +1,10 @@ package com.github.kuangcp.jdbc.jdbc.transaction.annotation; -import javax.annotation.Resource; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import javax.annotation.Resource; + @Service("personService") public class PersonServiceImpl implements PersonService { @@ -15,7 +16,6 @@ public class PersonServiceImpl implements PersonService { */ @Transactional(readOnly = false) public void savePerson() { - // TODO Auto-generated method stub this.personDao.savePerson(); } diff --git a/spring/src/test/java/com/github/kuangcp/mvc/PersonDaoImpl.java b/spring/src/test/java/com/github/kuangcp/mvc/PersonDaoImpl.java index 9976db13..bd8302ad 100644 --- a/spring/src/test/java/com/github/kuangcp/mvc/PersonDaoImpl.java +++ b/spring/src/test/java/com/github/kuangcp/mvc/PersonDaoImpl.java @@ -4,7 +4,6 @@ public class PersonDaoImpl implements PersonDao { @Override public void savePerson() { - // TODO Auto-generated method stub System.out.println("save person"); } diff --git a/spring/src/test/java/com/github/kuangcp/mvc/PersonServiceImpl.java b/spring/src/test/java/com/github/kuangcp/mvc/PersonServiceImpl.java index 805d4267..0aff06bf 100644 --- a/spring/src/test/java/com/github/kuangcp/mvc/PersonServiceImpl.java +++ b/spring/src/test/java/com/github/kuangcp/mvc/PersonServiceImpl.java @@ -14,7 +14,6 @@ public void setPersonDao(PersonDao personDao) { @Override public void savePerson() { - // TODO Auto-generated method stub this.personDao.savePerson(); } } diff --git a/spring/src/test/java/com/github/kuangcp/mvc/annotation/PersonDaoImpl.java b/spring/src/test/java/com/github/kuangcp/mvc/annotation/PersonDaoImpl.java index a0389239..4aac8b9e 100644 --- a/spring/src/test/java/com/github/kuangcp/mvc/annotation/PersonDaoImpl.java +++ b/spring/src/test/java/com/github/kuangcp/mvc/annotation/PersonDaoImpl.java @@ -7,7 +7,6 @@ public class PersonDaoImpl implements PersonDao { @Override public void savePerson() { - // TODO Auto-generated method stub System.out.println("save person"); } diff --git a/spring/src/test/java/com/github/kuangcp/mvc/annotation/PersonServiceImpl.java b/spring/src/test/java/com/github/kuangcp/mvc/annotation/PersonServiceImpl.java index 7afaf2fc..7a897c44 100644 --- a/spring/src/test/java/com/github/kuangcp/mvc/annotation/PersonServiceImpl.java +++ b/spring/src/test/java/com/github/kuangcp/mvc/annotation/PersonServiceImpl.java @@ -1,8 +1,9 @@ package com.github.kuangcp.mvc.annotation; -import javax.annotation.Resource; import org.springframework.stereotype.Service; +import javax.annotation.Resource; + @Service public class PersonServiceImpl implements PersonService { @@ -11,7 +12,6 @@ public class PersonServiceImpl implements PersonService { @Override public void savePerson() { - // TODO Auto-generated method stub this.personDao.savePerson(); } } From f6817c6be2afa8164ac2ed780b4ccca63248b407 Mon Sep 17 00:00:00 2001 From: kuangcp Date: Thu, 26 Oct 2023 15:16:38 +0800 Subject: [PATCH 306/476] string: find first diff index --- .../kuangcp/string/NumberMaxDiffIndex.java | 141 ++++++++++++++++++ .../string/NumberMaxDiffIndexTest.java | 51 +++++++ 2 files changed, 192 insertions(+) create mode 100644 algorithms/src/main/java/com/github/kuangcp/string/NumberMaxDiffIndex.java create mode 100644 algorithms/src/test/java/com/github/kuangcp/string/NumberMaxDiffIndexTest.java diff --git a/algorithms/src/main/java/com/github/kuangcp/string/NumberMaxDiffIndex.java b/algorithms/src/main/java/com/github/kuangcp/string/NumberMaxDiffIndex.java new file mode 100644 index 00000000..d43c08d4 --- /dev/null +++ b/algorithms/src/main/java/com/github/kuangcp/string/NumberMaxDiffIndex.java @@ -0,0 +1,141 @@ +package com.github.kuangcp.string; + +import org.apache.commons.codec.binary.StringUtils; + +import java.util.Objects; +import java.util.regex.Pattern; + +/** + * @author kuangchengping@sinohealth.cn + * 2023-10-26 13:45 + */ +public class NumberMaxDiffIndex { + + public static int findDiffIndexPoint(String a, String b) { + if (StringUtils.equals(a, b)) { + return 0; + } + if (Objects.isNull(a)) { + a = "0.0"; + } + if (Objects.isNull(b)) { + b = "0.0"; + } + + int ap = a.indexOf("."); + if (ap == -1) { + ap = a.length(); + a += ".0"; + } + int bp = b.indexOf("."); + if (bp == -1) { + bp = b.length(); + b += ".0"; + } + if (ap != bp) { + return Math.max(ap, bp); + } + + int maxDiff = 0; + for (int i = ap - 1; i >= 0; i--) { + if (a.charAt(i) != b.charAt(i)) { + maxDiff = ap - i; + } + } + if (maxDiff != 0) { + return maxDiff; + } + int al = a.length(); + int bl = b.length(); + int maxL = Math.max(al, bl); + for (int i = ap + 1; i < maxL; i++) { + char ac; + if (i >= al) { + ac = '0'; + } else { + ac = a.charAt(i); + } + + char bc; + if (i >= bl) { + bc = '0'; + } else { + bc = b.charAt(i); + } + if (ac != bc) { + return ap - i; + } + } + + return 0; + } + + private static final Pattern pattern = Pattern.compile("\\."); + public static int findDiffIndex(String a, String b) { + if (StringUtils.equals(a, b)) { + return 0; + } + if (Objects.isNull(a)) { + a = ""; + } + if (Objects.isNull(b)) { + b = ""; + } + +// String[] aPair = a.split("\\."); +// String[] bPair = b.split("\\."); + + String[] aPair = pattern.split(a, 0); + String[] bPair = pattern.split(b, 0); + String an = aPair[0]; + String bn = bPair[0]; + + int length = an.length(); + if (length != bn.length()) { + return Math.max(length, bn.length()); + } + for (int i = 0; i < length; i++) { + if (an.charAt(i) != bn.charAt(i)) { + return length - i; + } + } + String as; + if (aPair.length < 2) { + as = "0"; + } else { + as = aPair[1]; + } + String bs; + if (bPair.length < 2) { + bs = "0"; + } else { + bs = bPair[1]; + } + + if (as.length() != bs.length()) { + int d = Math.abs(as.length() - bs.length()); + if (as.length() < bs.length()) { + as += repeat("0", d); + } else { + bs += repeat("0", d); + } + } + + int min = Math.min(as.length(), bs.length()); + for (int i = 0; i < min; i++) { + if (as.charAt(i) != bs.charAt(i)) { + return -(i + 1); + } + } + + return 0; + } + + private static String repeat(CharSequence c, int count) { + StringBuilder rs = new StringBuilder(); + for (int i = 0; i < count; i++) { + rs.append(c); + } + return rs.toString(); + } +} diff --git a/algorithms/src/test/java/com/github/kuangcp/string/NumberMaxDiffIndexTest.java b/algorithms/src/test/java/com/github/kuangcp/string/NumberMaxDiffIndexTest.java new file mode 100644 index 00000000..1f3ad515 --- /dev/null +++ b/algorithms/src/test/java/com/github/kuangcp/string/NumberMaxDiffIndexTest.java @@ -0,0 +1,51 @@ +package com.github.kuangcp.string; + +import org.junit.Test; + +import java.util.function.BiFunction; + +import static org.hamcrest.Matchers.equalTo; +import static org.junit.Assert.assertThat; + +/** + * @author kuangchengping@sinohealth.cn + * 2023-10-26 13:49 + */ +public class NumberMaxDiffIndexTest { + + @Test + public void testDiffIndex() throws Exception { + long start = System.currentTimeMillis(); + for (int i = 0; i < 1000000; i++) { + runAndAssert(NumberMaxDiffIndex::findDiffIndex); + } + long end = System.currentTimeMillis(); + System.out.println("split: " + (end - start)); + + start = System.currentTimeMillis(); + for (int i = 0; i < 1000000; i++) { + runAndAssert(NumberMaxDiffIndex::findDiffIndexPoint); + } + end = System.currentTimeMillis(); + System.out.println("index: "+(end - start)); + } + + private static void runAndAssert(BiFunction func) { + assertThat(func.apply("123.0", "123"), equalTo(0)); + assertThat(func.apply("123.0", "121"), equalTo(1)); + assertThat(func.apply("123.0", "113"), equalTo(2)); + assertThat(func.apply("123.0", "323"), equalTo(3)); + assertThat(func.apply("123.0", "1123"), equalTo(4)); + + assertThat(func.apply("123.34", "123"), equalTo(-1)); + assertThat(func.apply("123.3", "123"), equalTo(-1)); + assertThat(func.apply("123.3", "120.31"), equalTo(1)); + assertThat(func.apply("123.3", "110.31"), equalTo(2)); + assertThat(func.apply("123.3", "12.31"), equalTo(3)); + assertThat(func.apply("12.3", "12.31"), equalTo(-2)); + assertThat(func.apply("12.3", "12.30"), equalTo(0)); + assertThat(func.apply("12.3", ""), equalTo(2)); + assertThat(func.apply("12.3", null), equalTo(2)); + assertThat(func.apply(null, "12.3"), equalTo(2)); + } +} From c8426020764bb586712201e2e811e485890c6385 Mon Sep 17 00:00:00 2001 From: kuangcp Date: Mon, 6 Nov 2023 21:57:28 +0800 Subject: [PATCH 307/476] link --- algorithms/src/test/java/com/github/kuangcp/sort/SortTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/algorithms/src/test/java/com/github/kuangcp/sort/SortTest.java b/algorithms/src/test/java/com/github/kuangcp/sort/SortTest.java index 9d669313..fb9275d4 100644 --- a/algorithms/src/test/java/com/github/kuangcp/sort/SortTest.java +++ b/algorithms/src/test/java/com/github/kuangcp/sort/SortTest.java @@ -12,6 +12,7 @@ @Slf4j public class SortTest { + // https://github.com/Kuangcp/GoBase/tree/master/algorithm/sort // test sort algorithms was correct @Test public void testSortCorrect() { From d806cf31c29129f994ac5f1d03dc2201a3b08463 Mon Sep 17 00:00:00 2001 From: kuangcp Date: Tue, 14 Nov 2023 20:48:52 +0800 Subject: [PATCH 308/476] compile --- .../kuangcp/string/NumberMaxDiffIndex.java | 2 +- .../string/NumberMaxDiffIndexTest.java | 2 +- class/pom.xml | 1 + .../com/github/kuangcp/reflects/BeanA.java | 60 +++++++++++++++++++ .../com/github/kuangcp/reflects/BeanB.java | 59 ++++++++++++++++++ .../kuangcp/reflects/RecordCopyTest.java | 32 ++++++++++ .../reflects/ReflectPerformanceTest.java | 11 +--- .../github/kuangcp/reflects/TargetObject.java | 16 +++++ pom.xml | 2 +- 9 files changed, 174 insertions(+), 11 deletions(-) create mode 100644 class/src/test/java/com/github/kuangcp/reflects/BeanA.java create mode 100644 class/src/test/java/com/github/kuangcp/reflects/BeanB.java create mode 100644 class/src/test/java/com/github/kuangcp/reflects/RecordCopyTest.java create mode 100644 class/src/test/java/com/github/kuangcp/reflects/TargetObject.java diff --git a/algorithms/src/main/java/com/github/kuangcp/string/NumberMaxDiffIndex.java b/algorithms/src/main/java/com/github/kuangcp/string/NumberMaxDiffIndex.java index d43c08d4..118532b8 100644 --- a/algorithms/src/main/java/com/github/kuangcp/string/NumberMaxDiffIndex.java +++ b/algorithms/src/main/java/com/github/kuangcp/string/NumberMaxDiffIndex.java @@ -6,7 +6,7 @@ import java.util.regex.Pattern; /** - * @author kuangchengping@sinohealth.cn + * @author Github * 2023-10-26 13:45 */ public class NumberMaxDiffIndex { diff --git a/algorithms/src/test/java/com/github/kuangcp/string/NumberMaxDiffIndexTest.java b/algorithms/src/test/java/com/github/kuangcp/string/NumberMaxDiffIndexTest.java index 1f3ad515..2af92567 100644 --- a/algorithms/src/test/java/com/github/kuangcp/string/NumberMaxDiffIndexTest.java +++ b/algorithms/src/test/java/com/github/kuangcp/string/NumberMaxDiffIndexTest.java @@ -8,7 +8,7 @@ import static org.junit.Assert.assertThat; /** - * @author kuangchengping@sinohealth.cn + * @author Github * 2023-10-26 13:49 */ public class NumberMaxDiffIndexTest { diff --git a/class/pom.xml b/class/pom.xml index 7488b815..0e9d2ebe 100644 --- a/class/pom.xml +++ b/class/pom.xml @@ -23,6 +23,7 @@ cglib cglib + 3.2.4 diff --git a/class/src/test/java/com/github/kuangcp/reflects/BeanA.java b/class/src/test/java/com/github/kuangcp/reflects/BeanA.java new file mode 100644 index 00000000..9f4001e2 --- /dev/null +++ b/class/src/test/java/com/github/kuangcp/reflects/BeanA.java @@ -0,0 +1,60 @@ +package com.github.kuangcp.reflects; + +import lombok.Data; + +import java.util.Date; +import java.util.List; + +/** + * @author @author Github + * 2023-11-14 20:23 + */ +@Data +public class BeanA { + + private Long applyId; + private Long userId; + private String baseTableName; + private String tableAlias; + private String projectName; + private String newProjectName; + private String templateName; + private Integer requireAttr; + private String readableUsers; + private Integer requireTimeType; + private Date dataExpire; + private String templateType; + + private String applicantName; + private String applicantDepartment; + private String createTime; + private String applyType; + private Integer currentAuditProcessStatus; + + private Integer currentIndex; + + private String currentHandlers; + + private String currentHandlerNames; + private String lastHandlerName; + private String applyComment; + private String docName; + + private Integer currentAuditNodeStatus; + private Integer handleStatus; + private String handleReason; + private List docAuthorization; + private Integer configType; + private Integer type; + private String sql; + private Integer workflowId; + private Boolean enableScheduledTask; + private String cron; + private Long assetId; + private String assetName; + private String syncTaskName; + private String serviceType; + private String state; + private String applyReason; + +} diff --git a/class/src/test/java/com/github/kuangcp/reflects/BeanB.java b/class/src/test/java/com/github/kuangcp/reflects/BeanB.java new file mode 100644 index 00000000..3f86fb8a --- /dev/null +++ b/class/src/test/java/com/github/kuangcp/reflects/BeanB.java @@ -0,0 +1,59 @@ +package com.github.kuangcp.reflects; + +import lombok.Data; + +import java.util.Date; +import java.util.List; + +/** + * @author Github + * 2023-11-14 20:23 + */ +@Data +public class BeanB { + + private Long applyId; + private Long userId; + private String baseTableName; + private String tableAlias; + private String projectName; + private String newProjectName; + private String templateName; + private Integer requireAttr; + private String readableUsers; + private Integer requireTimeType; + private Date dataExpire; + private String templateType; + + private String applicantName; + private String applicantDepartment; + private String createTime; + private String applyType; + private Integer currentAuditProcessStatus; + + private Integer currentIndex; + + private String currentHandlers; + + private String currentHandlerNames; + private String lastHandlerName; + private String applyComment; + private String docName; + + private Integer currentAuditNodeStatus; + private Integer handleStatus; + private String handleReason; + private List docAuthorization; + private Integer configType; + private Integer type; + private String sql; + private Integer workflowId; + private Boolean enableScheduledTask; + private String cron; + private Long assetId; + private String assetName; + private String syncTaskName; + private String serviceType; + private String state; + private String applyReason; +} diff --git a/class/src/test/java/com/github/kuangcp/reflects/RecordCopyTest.java b/class/src/test/java/com/github/kuangcp/reflects/RecordCopyTest.java new file mode 100644 index 00000000..7f3e79cc --- /dev/null +++ b/class/src/test/java/com/github/kuangcp/reflects/RecordCopyTest.java @@ -0,0 +1,32 @@ +package com.github.kuangcp.reflects; + +import net.sf.cglib.beans.BeanCopier; +import org.junit.Test; + +import static org.hamcrest.Matchers.equalTo; +import static org.junit.Assert.assertThat; + +/** + * @author Github + * 2023-11-14 19:49 + */ +public class RecordCopyTest { + + @Test + public void testBeanCopier() throws Exception { + BeanCopier copier = BeanCopier.create(BeanA.class, BeanB.class, false); + for (int i = 0; i < 10000; i++) { + BeanA a = new BeanA(); + a.setDocName("doc"); + BeanB b = new BeanB(); + copier.copy(a, b, null); + assertThat(b.getDocName(), equalTo(a.getDocName())); + } + } + + @Test + public void testUtil() throws Exception { + + } + +} diff --git a/class/src/test/java/com/github/kuangcp/reflects/ReflectPerformanceTest.java b/class/src/test/java/com/github/kuangcp/reflects/ReflectPerformanceTest.java index 71bc1e1f..8c7755f4 100644 --- a/class/src/test/java/com/github/kuangcp/reflects/ReflectPerformanceTest.java +++ b/class/src/test/java/com/github/kuangcp/reflects/ReflectPerformanceTest.java @@ -1,7 +1,6 @@ package com.github.kuangcp.reflects; import com.github.kuangcp.time.GetRunTime; -import lombok.Data; import lombok.extern.slf4j.Slf4j; import net.sf.cglib.reflect.FastClass; import net.sf.cglib.reflect.FastMethod; @@ -11,6 +10,7 @@ /** * 反射的性能问题 http://www.cnblogs.com/zhishan/p/3195771.html + * https://cloud.tencent.com/developer/article/2180451 * cglib(已缓存) 耗时 50%-70% 于缓存, 10% 于 原始方式 * TODO 操作字节码方式取代反射 * TODO jmh @@ -20,15 +20,9 @@ @Slf4j public class ReflectPerformanceTest { - private static final int LOOP_SIZE = 50_000_000; + private static final int LOOP_SIZE = 5000_000; private static final GetRunTime time = new GetRunTime(); - @Data - class TargetObject { - - private int num; - } - // primitive method invoke @Test public void testGetSet() { @@ -82,6 +76,7 @@ public void testCglib() throws Exception { long sum = 0; TargetObject targetObject = new TargetObject(); + // cglib 3.2.4 FastClass testClazz = FastClass.create(TargetObject.class); FastMethod method = testClazz.getMethod("setNum", new Class[]{int.class}); diff --git a/class/src/test/java/com/github/kuangcp/reflects/TargetObject.java b/class/src/test/java/com/github/kuangcp/reflects/TargetObject.java new file mode 100644 index 00000000..9a8f9241 --- /dev/null +++ b/class/src/test/java/com/github/kuangcp/reflects/TargetObject.java @@ -0,0 +1,16 @@ +package com.github.kuangcp.reflects; + +import lombok.Data; + +/** + * @author kuangchengping@sinohealth.cn + * 2023-11-14 20:09 + */ +@Data +public class TargetObject { + + private int num; + + public TargetObject() { + } +} diff --git a/pom.xml b/pom.xml index d8c97957..c23de1a9 100644 --- a/pom.xml +++ b/pom.xml @@ -123,7 +123,7 @@ cglib cglib - 3.2.9 + 3.3.0 org.apache.commons From a1d9ea0fa4fad5453f955754d02951426fd9b801 Mon Sep 17 00:00:00 2001 From: kuangcp Date: Thu, 16 Nov 2023 16:57:43 +0800 Subject: [PATCH 309/476] http server --- .../com/github/kuangcp/reflects/BeanUtil.java | 64 +++++++++++++++ .../kuangcp/reflects/RecordCopyTest.java | 77 ++++++++++++++++++- .../github/kuangcp/reflects/TargetObject.java | 2 +- 3 files changed, 141 insertions(+), 2 deletions(-) create mode 100644 class/src/main/java/com/github/kuangcp/reflects/BeanUtil.java diff --git a/class/src/main/java/com/github/kuangcp/reflects/BeanUtil.java b/class/src/main/java/com/github/kuangcp/reflects/BeanUtil.java new file mode 100644 index 00000000..4840114d --- /dev/null +++ b/class/src/main/java/com/github/kuangcp/reflects/BeanUtil.java @@ -0,0 +1,64 @@ +package com.github.kuangcp.reflects; + +import lombok.extern.slf4j.Slf4j; + +import java.lang.reflect.Field; +import java.util.Arrays; +import java.util.Map; +import java.util.WeakHashMap; +import java.util.stream.Collectors; + +/** + * @author Github + * 2023-11-14 19:49 + */ +@Slf4j +public class BeanUtil { + + private static final Map, Map> cache = new WeakHashMap<>(); + + public static void copyProperties(Object src, Object target) { + copyProperties(src, target, false); + } + + /** + * @param source 源对象 + * @param target 目标对象 + * @param ignoreCase 是否忽略大小写 + */ + public static void copyProperties(Object source, Object target, boolean ignoreCase) { + if (source == null || target == null) { + return; + } + Map sf = fieldMap(source); + + Map tf = fieldMap(target); + + try { + for (Map.Entry entry : tf.entrySet()) { + Field sField = sf.get(entry.getKey()); + Object sval = sField.get(source); + Field tField = tf.get(entry.getKey()); + tField.set(target, sval); + } + } catch (Exception e) { + log.error("", e); + } + } + + public static Map fieldMap(Object source) { + return fieldMap(source.getClass()); + } + + public static Map fieldMap(Class clz) { + return cache.computeIfAbsent(clz, v -> { + log.info("init"); + Field[] fields = clz.getDeclaredFields(); + return Arrays.stream(fields).collect(Collectors + .toMap(Field::getName, d -> { + d.setAccessible(true); + return d; + }, (front, current) -> current)); + }); + } +} diff --git a/class/src/test/java/com/github/kuangcp/reflects/RecordCopyTest.java b/class/src/test/java/com/github/kuangcp/reflects/RecordCopyTest.java index 7f3e79cc..23f18a24 100644 --- a/class/src/test/java/com/github/kuangcp/reflects/RecordCopyTest.java +++ b/class/src/test/java/com/github/kuangcp/reflects/RecordCopyTest.java @@ -1,7 +1,15 @@ package com.github.kuangcp.reflects; +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpServer; import net.sf.cglib.beans.BeanCopier; import org.junit.Test; +import sun.net.httpserver.HttpServerImpl; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.util.Arrays; +import java.util.Date; import static org.hamcrest.Matchers.equalTo; import static org.junit.Assert.assertThat; @@ -12,9 +20,10 @@ */ public class RecordCopyTest { + private static final BeanCopier copier = BeanCopier.create(BeanA.class, BeanB.class, false); + @Test public void testBeanCopier() throws Exception { - BeanCopier copier = BeanCopier.create(BeanA.class, BeanB.class, false); for (int i = 0; i < 10000; i++) { BeanA a = new BeanA(); a.setDocName("doc"); @@ -26,7 +35,73 @@ public void testBeanCopier() throws Exception { @Test public void testUtil() throws Exception { + BeanUtil.fieldMap(BeanA.class); + BeanUtil.fieldMap(BeanB.class); + + for (int i = 0; i < 10000; i++) { + BeanA a = new BeanA(); + a.setDocName("doc"); + BeanB b = new BeanB(); + BeanUtil.copyProperties(a, b, false); + assertThat(b.getDocName(), equalTo(a.getDocName())); + } + } + + public static void main(String[] args) throws Exception { + BeanUtil.fieldMap(BeanA.class); + BeanUtil.fieldMap(BeanB.class); + HttpServer s = HttpServerImpl.create(new InetSocketAddress(8745), 1000); + s.createContext("/u", RecordCopyTest::utilHandle); + s.createContext("/b", RecordCopyTest::copyHandle); + + s.start(); } + static void copyHandle(HttpExchange v) throws IOException { + long start = System.currentTimeMillis(); + String query = v.getRequestURI().getPath(); + int ix = query.lastIndexOf("/"); + String total = query.substring(ix + 1); + for (int i = 0; i < Integer.parseInt(total); i++) { + BeanA a = new BeanA(); + a.setDocName("doc"); + BeanB b = new BeanB(); + copier.copy(a, b, null); + } + long end = System.currentTimeMillis(); + + long gv = end - start; + String res = gv + ""; + v.sendResponseHeaders(200, res.length()); + v.getResponseBody().write(res.getBytes()); + v.getResponseBody().close(); + } + + static void utilHandle(HttpExchange v) throws IOException { + long start = System.currentTimeMillis(); + String query = v.getRequestURI().getPath(); + int ix = query.lastIndexOf("/"); + String total = query.substring(ix + 1); + BeanA a = new BeanA(); + a.setDocName("doc"); + a.setApplicantName("xxx"); + a.setApplyId(123456L); + a.setDataExpire(new Date()); + a.setDocAuthorization(Arrays.asList(2,4)); + + for (int i = 0; i < Integer.parseInt(total); i++) { + BeanB b = new BeanB(); + BeanUtil.copyProperties(a, b, false); + } + long end = System.currentTimeMillis(); + + long gv = end - start; + String res = gv + ""; + v.sendResponseHeaders(200, res.length()); + v.getResponseBody().write(res.getBytes()); + v.getResponseBody().close(); + } + + } diff --git a/class/src/test/java/com/github/kuangcp/reflects/TargetObject.java b/class/src/test/java/com/github/kuangcp/reflects/TargetObject.java index 9a8f9241..2060c46d 100644 --- a/class/src/test/java/com/github/kuangcp/reflects/TargetObject.java +++ b/class/src/test/java/com/github/kuangcp/reflects/TargetObject.java @@ -3,7 +3,7 @@ import lombok.Data; /** - * @author kuangchengping@sinohealth.cn + * @author Github * 2023-11-14 20:09 */ @Data From a0e34266558a1a3c1bf7032abf6314c6949200db Mon Sep 17 00:00:00 2001 From: kuangcp Date: Fri, 24 Nov 2023 11:48:39 +0800 Subject: [PATCH 310/476] rm package --- class/pom.xml | 7 ++++- .../test/java/com/github/kuangcp}/README.md | 0 .../list/ConcurrentModificationTest.java | 13 +++++---- .../kuangcp/list/ListWithArrayTest.java | 8 +++--- .../github/kuangcp/list/OperationTest.java | 13 ++++----- .../map/ConcurrentModificationTest.java | 6 ++-- .../com/github/kuangcp/map/HashMapTest.java | 23 ++++++++++----- .../github/kuangcp/map/IterateMapTest.java | 5 ++-- .../java/com/github/kuangcp/map/README.md | 0 .../kuangcp/map/RandomGetValueTest.java | 9 +++--- .../com/github/kuangcp/set/HashSetTest.java | 3 +- .../com/github/kuangcp/set/TreeSetTest.java | 7 +++-- collection/pom.xml | 28 ------------------- .../github/kuangcp/chainofcommand/Readme.md | 1 + pom.xml | 1 - 15 files changed, 56 insertions(+), 68 deletions(-) rename {collection/src/test/java => class/src/test/java/com/github/kuangcp}/README.md (100%) rename {collection => class}/src/test/java/com/github/kuangcp/list/ConcurrentModificationTest.java (96%) rename {collection => class}/src/test/java/com/github/kuangcp/list/ListWithArrayTest.java (100%) rename {collection => class}/src/test/java/com/github/kuangcp/list/OperationTest.java (99%) rename {collection => class}/src/test/java/com/github/kuangcp/map/ConcurrentModificationTest.java (100%) rename {collection => class}/src/test/java/com/github/kuangcp/map/HashMapTest.java (88%) rename {collection => class}/src/test/java/com/github/kuangcp/map/IterateMapTest.java (99%) rename {collection => class}/src/test/java/com/github/kuangcp/map/README.md (100%) rename {collection => class}/src/test/java/com/github/kuangcp/map/RandomGetValueTest.java (99%) rename {collection => class}/src/test/java/com/github/kuangcp/set/HashSetTest.java (99%) rename {collection => class}/src/test/java/com/github/kuangcp/set/TreeSetTest.java (99%) delete mode 100644 collection/pom.xml create mode 100644 pattern/src/main/java/com/github/kuangcp/chainofcommand/Readme.md diff --git a/class/pom.xml b/class/pom.xml index 0e9d2ebe..fd7173bb 100644 --- a/class/pom.xml +++ b/class/pom.xml @@ -28,9 +28,14 @@ com.github.kuangcp - kcp-core + kcp-tool + + + + + org.apache.commons commons-lang3 diff --git a/collection/src/test/java/README.md b/class/src/test/java/com/github/kuangcp/README.md similarity index 100% rename from collection/src/test/java/README.md rename to class/src/test/java/com/github/kuangcp/README.md diff --git a/collection/src/test/java/com/github/kuangcp/list/ConcurrentModificationTest.java b/class/src/test/java/com/github/kuangcp/list/ConcurrentModificationTest.java similarity index 96% rename from collection/src/test/java/com/github/kuangcp/list/ConcurrentModificationTest.java rename to class/src/test/java/com/github/kuangcp/list/ConcurrentModificationTest.java index 98e3cc6d..80d2842d 100644 --- a/collection/src/test/java/com/github/kuangcp/list/ConcurrentModificationTest.java +++ b/class/src/test/java/com/github/kuangcp/list/ConcurrentModificationTest.java @@ -1,15 +1,18 @@ package com.github.kuangcp.list; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.core.IsEqual.equalTo; - -import java.util.*; - import lombok.extern.slf4j.Slf4j; import org.junit.After; import org.junit.Before; import org.junit.Test; +import java.util.ArrayList; +import java.util.ConcurrentModificationException; +import java.util.Iterator; +import java.util.List; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.IsEqual.equalTo; + /** * ConcurrentModificationException 的产生是因为 failFast 策略 * List 发生修改后就会修改 List 的 modCount diff --git a/collection/src/test/java/com/github/kuangcp/list/ListWithArrayTest.java b/class/src/test/java/com/github/kuangcp/list/ListWithArrayTest.java similarity index 100% rename from collection/src/test/java/com/github/kuangcp/list/ListWithArrayTest.java rename to class/src/test/java/com/github/kuangcp/list/ListWithArrayTest.java index 62d48e4a..ac119902 100644 --- a/collection/src/test/java/com/github/kuangcp/list/ListWithArrayTest.java +++ b/class/src/test/java/com/github/kuangcp/list/ListWithArrayTest.java @@ -1,14 +1,14 @@ package com.github.kuangcp.list; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.core.IsEqual.equalTo; +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import lombok.extern.slf4j.Slf4j; -import org.junit.Test; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.IsEqual.equalTo; /** * List Array 互相转换的一些注意点 diff --git a/collection/src/test/java/com/github/kuangcp/list/OperationTest.java b/class/src/test/java/com/github/kuangcp/list/OperationTest.java similarity index 99% rename from collection/src/test/java/com/github/kuangcp/list/OperationTest.java rename to class/src/test/java/com/github/kuangcp/list/OperationTest.java index bdf35309..a2afa937 100644 --- a/collection/src/test/java/com/github/kuangcp/list/OperationTest.java +++ b/class/src/test/java/com/github/kuangcp/list/OperationTest.java @@ -1,18 +1,17 @@ package com.github.kuangcp.list; -import static org.hamcrest.CoreMatchers.equalTo; -import static org.junit.Assert.assertThat; - import com.github.kuangcp.time.GetRunTime; - -import java.util.ArrayList; -import java.util.List; - import lombok.extern.slf4j.Slf4j; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; +import java.util.ArrayList; +import java.util.List; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.junit.Assert.assertThat; + /** * 交 差 并 补 集合运算 * diff --git a/collection/src/test/java/com/github/kuangcp/map/ConcurrentModificationTest.java b/class/src/test/java/com/github/kuangcp/map/ConcurrentModificationTest.java similarity index 100% rename from collection/src/test/java/com/github/kuangcp/map/ConcurrentModificationTest.java rename to class/src/test/java/com/github/kuangcp/map/ConcurrentModificationTest.java index 33d8e2d8..56ce96a7 100644 --- a/collection/src/test/java/com/github/kuangcp/map/ConcurrentModificationTest.java +++ b/class/src/test/java/com/github/kuangcp/map/ConcurrentModificationTest.java @@ -1,12 +1,12 @@ package com.github.kuangcp.map; +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; + import java.util.HashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import lombok.extern.slf4j.Slf4j; -import org.junit.Test; - /** * TODO 完成 * diff --git a/collection/src/test/java/com/github/kuangcp/map/HashMapTest.java b/class/src/test/java/com/github/kuangcp/map/HashMapTest.java similarity index 88% rename from collection/src/test/java/com/github/kuangcp/map/HashMapTest.java rename to class/src/test/java/com/github/kuangcp/map/HashMapTest.java index 83021da8..a0ccd362 100644 --- a/collection/src/test/java/com/github/kuangcp/map/HashMapTest.java +++ b/class/src/test/java/com/github/kuangcp/map/HashMapTest.java @@ -1,18 +1,18 @@ package com.github.kuangcp.map; -import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.MatcherAssert.assertThat; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.NoArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Objects; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.NoArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.junit.Test; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.MatcherAssert.assertThat; /** * @author kuangcp on 2019-04-16 10:22 AM @@ -96,4 +96,13 @@ private void showMap(Map map) { map.forEach((k, v) -> log.info("k={} v={}", k, v)); } + + @Test + public void testStackOverFlow() throws Exception { + HashMap testMap = new HashMap(); + testMap.put("me", testMap); + HashMap testMap2 = new HashMap(); + testMap2.put(testMap, null); // <---- causes a stack overflow. + + } } diff --git a/collection/src/test/java/com/github/kuangcp/map/IterateMapTest.java b/class/src/test/java/com/github/kuangcp/map/IterateMapTest.java similarity index 99% rename from collection/src/test/java/com/github/kuangcp/map/IterateMapTest.java rename to class/src/test/java/com/github/kuangcp/map/IterateMapTest.java index caa8be1c..394f8a9f 100644 --- a/collection/src/test/java/com/github/kuangcp/map/IterateMapTest.java +++ b/class/src/test/java/com/github/kuangcp/map/IterateMapTest.java @@ -1,13 +1,12 @@ package com.github.kuangcp.map; import com.github.kuangcp.mock.MockMap; +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; import java.util.Iterator; import java.util.Map; -import lombok.extern.slf4j.Slf4j; -import org.junit.Test; - /** * Created by https://github.com/kuangcp on 17-8-21 下午2:48 * Map 的常见迭代用法 diff --git a/collection/src/test/java/com/github/kuangcp/map/README.md b/class/src/test/java/com/github/kuangcp/map/README.md similarity index 100% rename from collection/src/test/java/com/github/kuangcp/map/README.md rename to class/src/test/java/com/github/kuangcp/map/README.md diff --git a/collection/src/test/java/com/github/kuangcp/map/RandomGetValueTest.java b/class/src/test/java/com/github/kuangcp/map/RandomGetValueTest.java similarity index 99% rename from collection/src/test/java/com/github/kuangcp/map/RandomGetValueTest.java rename to class/src/test/java/com/github/kuangcp/map/RandomGetValueTest.java index 681f0365..7cdfd994 100644 --- a/collection/src/test/java/com/github/kuangcp/map/RandomGetValueTest.java +++ b/class/src/test/java/com/github/kuangcp/map/RandomGetValueTest.java @@ -1,15 +1,14 @@ package com.github.kuangcp.map; -import static org.hamcrest.CoreMatchers.hasItem; -import static org.hamcrest.MatcherAssert.assertThat; - import com.github.kuangcp.mock.MockMap; +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; import java.util.Map; import java.util.concurrent.ThreadLocalRandom; -import lombok.extern.slf4j.Slf4j; -import org.junit.Test; +import static org.hamcrest.CoreMatchers.hasItem; +import static org.hamcrest.MatcherAssert.assertThat; /** * @author kuangcp on 18-8-29-下午12:04 diff --git a/collection/src/test/java/com/github/kuangcp/set/HashSetTest.java b/class/src/test/java/com/github/kuangcp/set/HashSetTest.java similarity index 99% rename from collection/src/test/java/com/github/kuangcp/set/HashSetTest.java rename to class/src/test/java/com/github/kuangcp/set/HashSetTest.java index a0e5ca3a..14f84f7c 100644 --- a/collection/src/test/java/com/github/kuangcp/set/HashSetTest.java +++ b/class/src/test/java/com/github/kuangcp/set/HashSetTest.java @@ -1,9 +1,10 @@ package com.github.kuangcp.set; -import java.util.HashSet; import lombok.extern.slf4j.Slf4j; import org.junit.Test; +import java.util.HashSet; + /** * created by https://gitee.com/gin9 * diff --git a/collection/src/test/java/com/github/kuangcp/set/TreeSetTest.java b/class/src/test/java/com/github/kuangcp/set/TreeSetTest.java similarity index 99% rename from collection/src/test/java/com/github/kuangcp/set/TreeSetTest.java rename to class/src/test/java/com/github/kuangcp/set/TreeSetTest.java index 1cf2054d..fad86e1c 100644 --- a/collection/src/test/java/com/github/kuangcp/set/TreeSetTest.java +++ b/class/src/test/java/com/github/kuangcp/set/TreeSetTest.java @@ -1,11 +1,12 @@ package com.github.kuangcp.set; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.core.IsEqual.equalTo; +import org.junit.Test; import java.util.Arrays; import java.util.TreeSet; -import org.junit.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.IsEqual.equalTo; /** * @author https://github.com/kuangcp * @date 2019-05-24 09:22 diff --git a/collection/pom.xml b/collection/pom.xml deleted file mode 100644 index dce83557..00000000 --- a/collection/pom.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - com.github.kuangcp - java-base - 1.0.0 - - - 4.0.0 - - collection - 1.0.0 - - - UTF-8 - UTF-8 - - - - com.github.kuangcp - kcp-tool - - - - - diff --git a/pattern/src/main/java/com/github/kuangcp/chainofcommand/Readme.md b/pattern/src/main/java/com/github/kuangcp/chainofcommand/Readme.md new file mode 100644 index 00000000..446b55c4 --- /dev/null +++ b/pattern/src/main/java/com/github/kuangcp/chainofcommand/Readme.md @@ -0,0 +1 @@ +责任链模式 \ No newline at end of file diff --git a/pom.xml b/pom.xml index c23de1a9..b8c2a5b3 100644 --- a/pom.xml +++ b/pom.xml @@ -22,7 +22,6 @@ algorithms class - collection concurrency flink generic From cf94ae3efb194040660729d581344c964496011c Mon Sep 17 00:00:00 2001 From: kuangcp Date: Thu, 30 Nov 2023 15:43:03 +0800 Subject: [PATCH 311/476] fmt --- concurrency/pom.xml | 6 ++ .../main/java/thread/HowToCreateThread.java | 100 +++++++++--------- .../thread/ShowCreateThreadForSimpleMain.java | 27 ++--- .../java/thread/ThreadInterruptedDemo.java | 73 ++++++------- .../src/main/java/thread/ThreadJoinDemo.java | 50 ++++----- .../java/thread/ThreadStatusTransfer.java | 72 ++++++------- .../src/main/java/thread/pcstatus/Main.java | 22 ++-- .../java/thread/{ => pool}/UseThreadPool.java | 17 +-- .../thread/schdule/SchedulerPoolTest.java | 86 +++++++++++++++ .../java/thread/schdule/TimeTaskTest.java | 39 +++---- 10 files changed, 296 insertions(+), 196 deletions(-) rename concurrency/src/main/java/thread/{ => pool}/UseThreadPool.java (63%) create mode 100644 concurrency/src/test/java/thread/schdule/SchedulerPoolTest.java diff --git a/concurrency/pom.xml b/concurrency/pom.xml index ebd90a3e..dd0a639e 100644 --- a/concurrency/pom.xml +++ b/concurrency/pom.xml @@ -48,6 +48,12 @@ com.hellokaton blade-core + + + blade-log + com.hellokaton + + org.apache.groovy diff --git a/concurrency/src/main/java/thread/HowToCreateThread.java b/concurrency/src/main/java/thread/HowToCreateThread.java index 1645f1d7..9b860be0 100644 --- a/concurrency/src/main/java/thread/HowToCreateThread.java +++ b/concurrency/src/main/java/thread/HowToCreateThread.java @@ -6,69 +6,69 @@ */ class HowToCreateThread { - /** - * 继承方式来实现线程 继承Thread 重写run方法 - */ - static class ExampleOne extends Thread { + /** + * 继承方式来实现线程 继承Thread 重写run方法 + */ + static class ExampleOne extends Thread { - boolean runFlag = true; + boolean runFlag = true; - @Override - public void run() { - while (runFlag) { - System.out.println("继承方式来实现线程 "); - } - } + @Override + public void run() { + while (runFlag) { + System.out.println("继承方式来实现线程 "); + } + } - public void shutdown() { - runFlag = false; + public void shutdown() { + runFlag = false; + } } - } - /** - * 实现接口方式来实现线程 Runnable不是线程,是线程要运行的代码的宿主。 - */ - static class ExampleTwo implements Runnable { + /** + * 实现接口方式来实现线程 Runnable不是线程,是线程要运行的代码的宿主。 + */ + static class ExampleTwo implements Runnable { - boolean runFlag = true; + boolean runFlag = true; - @Override - public void run() { - while (runFlag) { - System.out.println("实现接口方式来实现线程"); - } - } + @Override + public void run() { + while (runFlag) { + System.out.println("实现接口方式来实现线程"); + } + } - public void shutdown() { - runFlag = false; + public void shutdown() { + runFlag = false; + } } - } - /** - * 直接实例化一个匿名内部方法在方法体里 - * 匿名内部类 - */ - static class ExampleThree { + /** + * 直接实例化一个匿名内部方法在方法体里 + * 匿名内部类 + */ + static class ExampleThree { - void test() { - // lambda方式 - new Thread(() -> { - System.out.println("直接实例化一个匿名内部方法在方法体里"); - System.out.println("多行语句"); - }).start(); + void test() { + // lambda方式 + new Thread(() -> { + System.out.println("直接实例化一个匿名内部方法在方法体里"); + System.out.println("多行语句"); + }).start(); - new Thread(() -> System.out.println("只有一条语句")).start(); + new Thread(() -> System.out.println("只有一条语句")).start(); - // 普通方式 - new Thread(new Runnable() { - @Override - public void run() { - System.out.println("直接实例化一个匿名内部方法在方法体里"); - } - }).start(); + // 普通方式 + new Thread(new Runnable() { + @Override + public void run() { + System.out.println("直接实例化一个匿名内部方法在方法体里"); + } + }).start(); - // 方法引用 - new Thread(this::test).start(); + // 方法引用 + new Thread(this::test).start(); + } } - } } diff --git a/concurrency/src/main/java/thread/ShowCreateThreadForSimpleMain.java b/concurrency/src/main/java/thread/ShowCreateThreadForSimpleMain.java index 09c59547..91eba93a 100644 --- a/concurrency/src/main/java/thread/ShowCreateThreadForSimpleMain.java +++ b/concurrency/src/main/java/thread/ShowCreateThreadForSimpleMain.java @@ -1,9 +1,10 @@ package thread; +import lombok.extern.slf4j.Slf4j; + import java.lang.management.ManagementFactory; import java.lang.management.ThreadInfo; import java.lang.management.ThreadMXBean; -import lombok.extern.slf4j.Slf4j; /** * @author kuangcp on 2019-04-19 10:08 AM @@ -11,18 +12,18 @@ @Slf4j public class ShowCreateThreadForSimpleMain { - public static void main(String[] args) { - ThreadMXBean bean = ManagementFactory.getThreadMXBean(); - ThreadInfo[] infos = bean.dumpAllThreads(false, false); - for (ThreadInfo info : infos) { - log.info("id={} name={}", info.getThreadId(), info.getThreadName()); + public static void main(String[] args) { + ThreadMXBean bean = ManagementFactory.getThreadMXBean(); + ThreadInfo[] infos = bean.dumpAllThreads(false, false); + for (ThreadInfo info : infos) { + log.info("id={} name={}", info.getThreadId(), info.getThreadName()); + } } - } - // 预期输出 - //name=Monitor Ctrl-Break - //name=Signal Dispatcher 分发处理发送给JVM进程信号 - //name=Finalizer 调用对象 finalize() - //name=Reference Handler 清除 Reference - //name=main + // 预期输出 + //name=Monitor Ctrl-Break + //name=Signal Dispatcher 分发处理发送给JVM进程信号 + //name=Finalizer 调用对象 finalize() + //name=Reference Handler 清除 Reference + //name=main } \ No newline at end of file diff --git a/concurrency/src/main/java/thread/ThreadInterruptedDemo.java b/concurrency/src/main/java/thread/ThreadInterruptedDemo.java index c411ee4a..827a25e5 100644 --- a/concurrency/src/main/java/thread/ThreadInterruptedDemo.java +++ b/concurrency/src/main/java/thread/ThreadInterruptedDemo.java @@ -1,53 +1,54 @@ package thread; -import java.util.concurrent.TimeUnit; import lombok.extern.slf4j.Slf4j; +import java.util.concurrent.TimeUnit; + /** * @author kuangcp on 2019-04-19 9:07 PM */ @Slf4j public class ThreadInterruptedDemo { - public static void main(String[] args) throws InterruptedException { - Thread sleep = new Thread(new SleepRunner(), "Sleep"); - sleep.setDaemon(true); - - Thread busy = new Thread(new BusyRunner(), "Busy"); - busy.setDaemon(true); - sleep.start(); - busy.start(); - - TimeUnit.SECONDS.sleep(5); - sleep.interrupt(); - busy.interrupt(); - log.info("sleep={}", sleep.isInterrupted()); - log.info("busy={}", busy.isInterrupted()); - - // sleep 方法收到中断信号后, 会抛出中断异常并返回 false 所以这里的输出 是 false true - } - - static class SleepRunner implements Runnable { - - @Override - public void run() { - while (true) { - try { - TimeUnit.SECONDS.sleep(10); - } catch (InterruptedException e) { - log.error(e.getMessage(), e); + public static void main(String[] args) throws InterruptedException { + Thread sleep = new Thread(new SleepRunner(), "Sleep"); + sleep.setDaemon(true); + + Thread busy = new Thread(new BusyRunner(), "Busy"); + busy.setDaemon(true); + sleep.start(); + busy.start(); + + TimeUnit.SECONDS.sleep(5); + sleep.interrupt(); + busy.interrupt(); + log.info("sleep={}", sleep.isInterrupted()); + log.info("busy={}", busy.isInterrupted()); + + // sleep 方法收到中断信号后, 会抛出中断异常并返回 false 所以这里的输出 是 false true + } + + static class SleepRunner implements Runnable { + + @Override + public void run() { + while (true) { + try { + TimeUnit.SECONDS.sleep(10); + } catch (InterruptedException e) { + log.error(e.getMessage(), e); + } + } } - } } - } - static class BusyRunner implements Runnable { + static class BusyRunner implements Runnable { - @Override - public void run() { - while (true) { + @Override + public void run() { + while (true) { - } + } + } } - } } diff --git a/concurrency/src/main/java/thread/ThreadJoinDemo.java b/concurrency/src/main/java/thread/ThreadJoinDemo.java index 61480ea8..bb86b9fd 100644 --- a/concurrency/src/main/java/thread/ThreadJoinDemo.java +++ b/concurrency/src/main/java/thread/ThreadJoinDemo.java @@ -10,38 +10,38 @@ @Slf4j public class ThreadJoinDemo { - static class Domino implements Runnable { + static class Domino implements Runnable { - private final Thread thread; + private final Thread thread; - public Domino(Thread thread) { - this.thread = thread; - } + public Domino(Thread thread) { + this.thread = thread; + } - @Override - public void run() { - try { - thread.join(); - } catch (InterruptedException e) { - log.error(e.getMessage(), e); - } + @Override + public void run() { + try { + thread.join(); + } catch (InterruptedException e) { + log.error(e.getMessage(), e); + } - log.info("{} terminate.", Thread.currentThread().getName()); - } + log.info("{} terminate.", Thread.currentThread().getName()); + } - } + } - // 每个线程的需要等待前驱线程执行完成才能退出 - public static void main(String[] args) throws InterruptedException { - Thread previous = Thread.currentThread(); + // 每个线程的需要等待前驱线程执行完成才能退出 + public static void main(String[] args) throws InterruptedException { + Thread previous = Thread.currentThread(); - for (int i = 0; i < 10; i++) { - Thread thread = new Thread(new Domino(previous), i + ""); - thread.start(); - previous = thread; + for (int i = 0; i < 10; i++) { + Thread thread = new Thread(new Domino(previous), i + ""); + thread.start(); + previous = thread; + } + TimeUnit.SECONDS.sleep(3); + log.info("{} terminate.", Thread.currentThread().getName()); } - TimeUnit.SECONDS.sleep(3); - log.info("{} terminate.", Thread.currentThread().getName()); - } } diff --git a/concurrency/src/main/java/thread/ThreadStatusTransfer.java b/concurrency/src/main/java/thread/ThreadStatusTransfer.java index 35acb93c..719c0d80 100644 --- a/concurrency/src/main/java/thread/ThreadStatusTransfer.java +++ b/concurrency/src/main/java/thread/ThreadStatusTransfer.java @@ -12,44 +12,44 @@ @Slf4j public class ThreadStatusTransfer { - private static boolean flag = true; - - private static final Object lock = new Object(); - - static class Wait implements Runnable { - - @Override - public void run() { - // 如果 synchronized 一个非 final 的变量, 容易发生 当该对象的引用地址更改后, 同步块里的代码可以被并发执行,因为锁的对象发生变化 - synchronized (lock) { - while (flag) { - try { - log.info("flag is true: start wait"); - lock.wait(); - } catch (InterruptedException e) { - log.error(e.getMessage(), e); - } + private static boolean flag = true; + + private static final Object lock = new Object(); + + static class Wait implements Runnable { + + @Override + public void run() { + // 如果 synchronized 一个非 final 的变量, 容易发生 当该对象的引用地址更改后, 同步块里的代码可以被并发执行,因为锁的对象发生变化 + synchronized (lock) { + while (flag) { + try { + log.info("flag is true: start wait"); + lock.wait(); + } catch (InterruptedException e) { + log.error(e.getMessage(), e); + } + } + log.info("flag is false. running"); + } } - log.info("flag is false. running"); - } } - } - - static class Notify implements Runnable { - - @Override - public void run() { - synchronized (lock) { - log.info("hold lock. notify"); - lock.notify(); - flag = false; - } - - // 这一段就是重新获取锁, 会和wait进行竞争, 所以执行顺序不定 - synchronized (lock) { - log.info("hold lock again"); - } + + static class Notify implements Runnable { + + @Override + public void run() { + synchronized (lock) { + log.info("hold lock. notify"); + lock.notify(); + flag = false; + } + + // 这一段就是重新获取锁, 会和wait进行竞争, 所以执行顺序不定 + synchronized (lock) { + log.info("hold lock again"); + } + } } - } } diff --git a/concurrency/src/main/java/thread/pcstatus/Main.java b/concurrency/src/main/java/thread/pcstatus/Main.java index 8d408d78..7cb4fb4f 100644 --- a/concurrency/src/main/java/thread/pcstatus/Main.java +++ b/concurrency/src/main/java/thread/pcstatus/Main.java @@ -8,17 +8,17 @@ */ public class Main { - public static void main(String[] args) { - Share share = new Share(); - Producer p = new Producer(share, 1); - Consumer c = new Consumer(share, 1); + public static void main(String[] args) { + Share share = new Share(); + Producer p = new Producer(share, 1); + Consumer c = new Consumer(share, 1); - Producer p2 = new Producer(share, 2); - Consumer c2 = new Consumer(share, 2); + Producer p2 = new Producer(share, 2); + Consumer c2 = new Consumer(share, 2); - p.start(); - p2.start(); - c.start(); - c2.start(); - } + p.start(); + p2.start(); + c.start(); + c2.start(); + } } diff --git a/concurrency/src/main/java/thread/UseThreadPool.java b/concurrency/src/main/java/thread/pool/UseThreadPool.java similarity index 63% rename from concurrency/src/main/java/thread/UseThreadPool.java rename to concurrency/src/main/java/thread/pool/UseThreadPool.java index 9a44e642..5fdb721d 100644 --- a/concurrency/src/main/java/thread/UseThreadPool.java +++ b/concurrency/src/main/java/thread/pool/UseThreadPool.java @@ -1,14 +1,16 @@ -package thread; +package thread.pool; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledThreadPoolExecutor; /** * Created by https://github.com/kuangcp on 17-8-20 下午8:44 * TODO 线程池 * https://www.cnblogs.com/eesijian/p/5871448.html * Executors + * TODO: https://github.com/alibaba/transmittable-thread-local */ public class UseThreadPool { @@ -25,13 +27,16 @@ private void baseType() { // 创建具有定时功能的线程池 指定基本线程池数量, 该线程池的队列是无限队列 ScheduledExecutorService d = Executors.newScheduledThreadPool(1); - // 创建单线程的线程池,指定延迟 + // 创建单线程的线程池,可指定延迟 ScheduledExecutorService e = Executors.newSingleThreadScheduledExecutor(); -// a.submit(); 提交任务 -// a.execute(); 执行任务 -// a.shutdown(); 关闭线程池, 等待任务执行完成 -// a.shutdownNow(); 关闭线程池, 立即关闭 + // 创建调度作用线程池,可指定延迟 + ScheduledThreadPoolExecutor f = new ScheduledThreadPoolExecutor(1); + + // a.submit(); 提交任务 + // a.execute(); 执行任务 + // a.shutdown(); 关闭线程池, 等待任务执行完成 + // a.shutdownNow(); 关闭线程池, 立即关闭 } } diff --git a/concurrency/src/test/java/thread/schdule/SchedulerPoolTest.java b/concurrency/src/test/java/thread/schdule/SchedulerPoolTest.java new file mode 100644 index 00000000..3bfbc0e8 --- /dev/null +++ b/concurrency/src/test/java/thread/schdule/SchedulerPoolTest.java @@ -0,0 +1,86 @@ +package thread.schdule; + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.concurrent.BasicThreadFactory; +import org.junit.Test; +import org.slf4j.MDC; + +import java.util.concurrent.*; + +/** + * + * @author Github + * 2023-11-30 15:17 + */ +@Slf4j +public class SchedulerPoolTest { + + public static void printException(Runnable r, Throwable t) { + if (t == null && r instanceof Future) { + try { + Future future = (Future) r; + if (future.isDone()) { + future.get(); + } + } catch (CancellationException ce) { + t = ce; + } catch (ExecutionException ee) { + t = ee.getCause(); + } catch (InterruptedException ie) { + Thread.currentThread().interrupt(); + } + } + if (t != null) { + log.error(t.getMessage(), t); + } + } + + @Test + public void testSimple() throws Exception { + String traceId = MDC.get("tid"); + + ScheduledExecutorService pool = new ScheduledThreadPoolExecutor(1, + new BasicThreadFactory.Builder().namingPattern("schedule-pool-%d").daemon(true).build()) { + @Override + protected void afterExecute(Runnable r, Throwable t) { + super.afterExecute(r, t); + printException(r, t); + } + + /** + * 手动传递上下文 + */ + @Override + public ScheduledFuture schedule(Runnable command, long delay, TimeUnit unit) { + if (command == null) { + throw new RuntimeException("schedule command NPE"); + } + String tid = MDC.get("tid"); + return super.schedule(() -> { + if (StringUtils.isNoneBlank(tid)) { + MDC.put("tid", tid); + } + command.run(); + }, delay, unit); + } + + @Override + protected RunnableScheduledFuture decorateTask(Runnable runnable, RunnableScheduledFuture task) { + return super.decorateTask(runnable, task); + } + }; + + for (int i = 0; i < 10; i++) { + int finalI = i; + MDC.put("tid", "U" + i); + TimeUnit.SECONDS.sleep(1); + log.info("SUBMIT {}", finalI); + pool.schedule(() -> { + log.info("RUN {} {}", finalI, MDC.get("tid")); + }, 3, TimeUnit.SECONDS); + } + + Thread.currentThread().join(); + } +} diff --git a/concurrency/src/test/java/thread/schdule/TimeTaskTest.java b/concurrency/src/test/java/thread/schdule/TimeTaskTest.java index 7ec50a35..3f8f0143 100644 --- a/concurrency/src/test/java/thread/schdule/TimeTaskTest.java +++ b/concurrency/src/test/java/thread/schdule/TimeTaskTest.java @@ -1,33 +1,34 @@ package thread.schdule; -import java.util.Timer; -import java.util.TimerTask; import lombok.extern.slf4j.Slf4j; import org.testng.annotations.Test; +import java.util.Timer; +import java.util.TimerTask; + /** * @author kuangcp on 18-8-20-下午3:20 */ @Slf4j public class TimeTaskTest { - // timer thread run, ignore main thread status - public static void main(String[] args) throws InterruptedException { - new TimeTaskTest().testTask(); - } + // timer thread run, ignore main thread status + public static void main(String[] args) throws InterruptedException { + new TimeTaskTest().testTask(); + } - // timer thread will exit after main thread - @Test - public void testTask() throws InterruptedException { - Timer timer = new Timer(); - timer.schedule(new TimerTask() { - @Override - public void run() { - log.info("run"); - } - }, 100, 1000); + // timer thread will exit after main thread + @Test + public void testTask() throws InterruptedException { + Timer timer = new Timer(); + timer.schedule(new TimerTask() { + @Override + public void run() { + log.info("run"); + } + }, 100, 1000); - Thread.sleep(4000); - log.info("main"); - } + Thread.sleep(4000); + log.info("main"); + } } From a4b6620b1197dff0965a6b7b7e0589e6e5df69c3 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Fri, 1 Dec 2023 01:46:10 +0800 Subject: [PATCH 312/476] signal --- .../kuangcp/exit/DebugSignalHandler.java | 22 +++++++++++++++++ .../java/com/github/kuangcp/exit/Readme.md | 4 ++++ .../com/github/kuangcp/exit/TrapSignal.java | 24 +++++++++++++++++++ 3 files changed, 50 insertions(+) create mode 100644 class/src/main/java/com/github/kuangcp/exit/DebugSignalHandler.java create mode 100644 class/src/main/java/com/github/kuangcp/exit/Readme.md create mode 100644 class/src/main/java/com/github/kuangcp/exit/TrapSignal.java diff --git a/class/src/main/java/com/github/kuangcp/exit/DebugSignalHandler.java b/class/src/main/java/com/github/kuangcp/exit/DebugSignalHandler.java new file mode 100644 index 00000000..e44d5fcb --- /dev/null +++ b/class/src/main/java/com/github/kuangcp/exit/DebugSignalHandler.java @@ -0,0 +1,22 @@ +package com.github.kuangcp.exit; + +import sun.misc.Signal; +import sun.misc.SignalHandler; + +/** + * @author Kuangcp on 2023-12-01 01:17 + */ +public class DebugSignalHandler implements SignalHandler { + public static void listenTo(String name) { + Signal signal = new Signal(name); + Signal.handle(signal, new DebugSignalHandler()); + } + + public void handle(Signal signal) { + System.out.println("Signal: " + signal); + if (signal.toString().trim().equals("SIGTERM")) { + System.out.println("SIGTERM raised, terminating..."); + System.exit(1); + } + } +} diff --git a/class/src/main/java/com/github/kuangcp/exit/Readme.md b/class/src/main/java/com/github/kuangcp/exit/Readme.md new file mode 100644 index 00000000..9ff6e9c0 --- /dev/null +++ b/class/src/main/java/com/github/kuangcp/exit/Readme.md @@ -0,0 +1,4 @@ +# Jvm退出 + +1. 操作系统信号接收和处理 +1. 退出前的追加处理 Hook diff --git a/class/src/main/java/com/github/kuangcp/exit/TrapSignal.java b/class/src/main/java/com/github/kuangcp/exit/TrapSignal.java new file mode 100644 index 00000000..692911f9 --- /dev/null +++ b/class/src/main/java/com/github/kuangcp/exit/TrapSignal.java @@ -0,0 +1,24 @@ +package com.github.kuangcp.exit; + +import java.util.concurrent.TimeUnit; + +/** + * @author Kuangcp on 2023-12-01 01:16 + */ +public class TrapSignal { + + public static void main(String[] args) throws InterruptedException { + // 同样地,如果使用了 -Xrs JVM参数,以下信号量的监听也会报错 + DebugSignalHandler.listenTo("HUP"); +// DebugSignalHandler.listenTo("INT"); +// DebugSignalHandler.listenTo("TERM"); + + + // 注意此信号无法监听,报错: Signal already used by VM or OS: SIGKILL +// DebugSignalHandler.listenTo("KILL"); + + while (true) { + TimeUnit.SECONDS.sleep(1); + } + } +} From 48f16bfc326d94af123b581289819e6dc2539bd4 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Fri, 1 Dec 2023 01:46:20 +0800 Subject: [PATCH 313/476] fmt --- .../github/kuangcp/strcuture/tree/BiTree.java | 43 ++++++++----------- .../antlr4/expression/ExpressionTest.java | 3 ++ .../thread/schdule/SchedulerPoolTest.java | 2 - 3 files changed, 21 insertions(+), 27 deletions(-) diff --git a/algorithms/src/main/java/com/github/kuangcp/strcuture/tree/BiTree.java b/algorithms/src/main/java/com/github/kuangcp/strcuture/tree/BiTree.java index 21542745..8f4de6da 100644 --- a/algorithms/src/main/java/com/github/kuangcp/strcuture/tree/BiTree.java +++ b/algorithms/src/main/java/com/github/kuangcp/strcuture/tree/BiTree.java @@ -13,48 +13,41 @@ public class BiTree { public BiTree rc; Scanner sc = new Scanner(System.in); - public void Initiate(BiTree bt){//初始化二叉树 - bt.lc = bt.rc =null; - } - /*public Bitree Create(Bitree bt){ 这是啥 ?建立一堆 无关系的节点干啥? - Object data = sc.next(); - if (data==".") bt=null; - else { - bt = new Bitree(); - bt.data = data; - } - return bt; - }*/ + public void Initiate(BiTree bt) {//初始化二叉树 + bt.lc = bt.rc = null; + } - public void Destory(BiTree bt){ - bt=null; + public void Destory(BiTree bt) { + bt = null; } - public boolean Empty(BiTree bt){ - return bt.lc ==null && bt.rc== null; + public boolean Empty(BiTree bt) { + return bt.lc == null && bt.rc == null; } - public Object Root(BiTree bt){ + public Object Root(BiTree bt) { return bt.data; } - public BiTree Parent(BiTree bt, Object x){ // 求父亲节点 + public BiTree Parent(BiTree bt, Object x) { // 求父亲节点 return bt; } - public BiTree(String preOrder, String inOrder, int preIndex, int inIndex, int count ){ - if (count >0){ //先根和中根非空 - char r =preOrder.charAt(preIndex);//取先根遍历序列中的第一个节点作为根节点 + + public BiTree(String preOrder, String inOrder, int preIndex, int inIndex, int count) { + if (count > 0) { //先根和中根非空 + char r = preOrder.charAt(preIndex);//取先根遍历序列中的第一个节点作为根节点 int i = 0; - for (;i Date: Mon, 4 Dec 2023 10:51:15 +0800 Subject: [PATCH 314/476] gui --- gui/Readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gui/Readme.md b/gui/Readme.md index 42142739..17302eb8 100644 --- a/gui/Readme.md +++ b/gui/Readme.md @@ -10,4 +10,4 @@ gradle calc -x test 打包计算器 ## GTK > [java-gnome](http://java-gnome.sourceforge.net/README.html) - +[idea ui designer](https://www.youtube.com/watch?v=whF_Qm1epQ8)`javafx ui-designer` From a504a547e86c7d349890fd63b2a9681141bb33ae Mon Sep 17 00:00:00 2001 From: kuangcp Date: Wed, 13 Dec 2023 17:56:27 +0800 Subject: [PATCH 315/476] generics --- .../src/main/java/com/github/kuangcp/Ioc.java | 36 +++++++++++++++++++ .../com/github/kuangcp/simple/PairTest.java | 3 +- 2 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 generic/src/main/java/com/github/kuangcp/Ioc.java diff --git a/generic/src/main/java/com/github/kuangcp/Ioc.java b/generic/src/main/java/com/github/kuangcp/Ioc.java new file mode 100644 index 00000000..9fc9942f --- /dev/null +++ b/generic/src/main/java/com/github/kuangcp/Ioc.java @@ -0,0 +1,36 @@ +package com.github.kuangcp; + +import com.github.kuangcp.common.Human; +import com.github.kuangcp.common.Student; + +/** + * + * 2023-12-13 16:26 + */ +public class Ioc { + + /** + * 如果返回不做类型强转,无法编译通过 + * 思考:泛型T在编译期拥有了多态性,不确定。 + */ + public Class getClz(int code) { + if (code == 1) { + return (Class) Student.class; + } else if (code == 2) { + return (Class) Human.class; + } + return null; + } + + /** + * 直接使用通配符是正常 + */ + public Class getClass(int code) { + if (code == 1) { + return Student.class; + } else if (code == 2) { + return Human.class; + } + return null; + } +} diff --git a/generic/src/test/java/com/github/kuangcp/simple/PairTest.java b/generic/src/test/java/com/github/kuangcp/simple/PairTest.java index 60a69754..60ff70cb 100644 --- a/generic/src/test/java/com/github/kuangcp/simple/PairTest.java +++ b/generic/src/test/java/com/github/kuangcp/simple/PairTest.java @@ -3,10 +3,11 @@ import com.github.kuangcp.common.Human; import com.github.kuangcp.common.Junior; import com.github.kuangcp.common.Student; -import java.util.Date; import lombok.extern.slf4j.Slf4j; import org.junit.Test; +import java.util.Date; + /** * Created by https://github.com/kuangcp on 18-1-11 下午5:38 * 这个泛型类的使用 和 集合的泛型使用 用法一致 From d16b612fd8fde254ce4e100104c2c7079a2d9da6 Mon Sep 17 00:00:00 2001 From: kuangcp Date: Tue, 19 Dec 2023 14:17:39 +0800 Subject: [PATCH 316/476] remove useless --- README.md | 1 - io/README.md | 2 - io/pom.xml | 27 --- .../com/github/kuangcp/file/CopyFile.java | 159 ------------------ .../com/github/kuangcp/io/ResourceTool.java | 23 --- .../com/github/kuangcp/standard/Readme.md | 5 - .../com/github/kuangcp/zip/package-info.java | 6 - .../com/github/kuangcp/PropertiesTest.java | 28 --- .../com/github/kuangcp/file/CopyFileTest.java | 86 ---------- network/README.md | 4 +- network/pom.xml | 4 + .../java/com/github/kuangcp/io/CopyFile.java | 149 ++++++++++++++++ .../com/github/kuangcp/io/CopyFileTest.java | 87 ++++++++++ .../com/github/kuangcp/io/PropertiesTest.java | 29 ++++ .../kuangcp/nio}/buffer/BufferTest.java | 4 +- .../test/resources/properties/main.properties | 0 pom.xml | 1 - 17 files changed, 272 insertions(+), 343 deletions(-) delete mode 100644 io/README.md delete mode 100644 io/pom.xml delete mode 100644 io/src/main/java/com/github/kuangcp/file/CopyFile.java delete mode 100644 io/src/main/java/com/github/kuangcp/io/ResourceTool.java delete mode 100644 io/src/main/java/com/github/kuangcp/standard/Readme.md delete mode 100644 io/src/main/java/com/github/kuangcp/zip/package-info.java delete mode 100644 io/src/test/java/com/github/kuangcp/PropertiesTest.java delete mode 100644 io/src/test/java/com/github/kuangcp/file/CopyFileTest.java create mode 100644 network/src/main/java/com/github/kuangcp/io/CopyFile.java create mode 100644 network/src/test/java/com/github/kuangcp/io/CopyFileTest.java create mode 100644 network/src/test/java/com/github/kuangcp/io/PropertiesTest.java rename {io/src/test/java => network/src/test/java/com/github/kuangcp/nio}/buffer/BufferTest.java (95%) rename {io => network}/src/test/resources/properties/main.properties (100%) diff --git a/README.md b/README.md index feb08dec..5b2ffeb7 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,6 @@ | [算法](/algorithms) | [设计模式](/pattern) | [Spring](/spring) | [字节码](/class) | [测试](/test) | [Kafka](/kafka)| | [泛型](/generic) | | [Hadoop](/hadoop) | -| [IO](/io) | | | [Java Gui](/gui) | [业务问题](/question) diff --git a/io/README.md b/io/README.md deleted file mode 100644 index 395ce1f5..00000000 --- a/io/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# IO的学习 -> [笔记详情](https://github.com/Kuangcp/Note/blob/master/Java/AdvancedLearning/IO.md) diff --git a/io/pom.xml b/io/pom.xml deleted file mode 100644 index 0abcb9a6..00000000 --- a/io/pom.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - com.github.kuangcp - java-base - 1.0.0 - - - 4.0.0 - - io - 1.0.0 - - - UTF-8 - UTF-8 - - - - org.apache.commons - commons-lang3 - - - - diff --git a/io/src/main/java/com/github/kuangcp/file/CopyFile.java b/io/src/main/java/com/github/kuangcp/file/CopyFile.java deleted file mode 100644 index a4ae8205..00000000 --- a/io/src/main/java/com/github/kuangcp/file/CopyFile.java +++ /dev/null @@ -1,159 +0,0 @@ -package com.github.kuangcp.file; - -import com.github.kuangcp.io.ResourceTool; -import java.io.BufferedReader; -import java.io.BufferedWriter; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.FileReader; -import java.io.FileWriter; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.nio.file.Files; -import java.nio.file.Path; -import lombok.extern.slf4j.Slf4j; - -/** - * 关于相对路径文件的复制问题 - * - * InputStream 所有输入流基类(建立流)(字节流) InputStreamReader 将字节流 转换成 字符流 BufferedReader 从字符流输入流读取文件 - * - * 注意: 如果输出流尚未关闭 内存的数据尚未刷新到硬盘上, 会导致不一致的情况 - */ -@Slf4j -class CopyFile { - - // 字节流 字节流转换为字符流 缓冲字符流 -> - void copyFileWithSixChannel(String from, String dest) { - InputStream inputStream = null; - InputStreamReader inputStreamReader = null; - BufferedReader bufferedReader = null; - - OutputStream outputStream = null; - OutputStreamWriter outputStreamWriter = null; - BufferedWriter bufferedWriter = null; - - try { - // 在 idea 中就是 project(工作目录) 根目录+path - inputStream = new FileInputStream(from); - inputStreamReader = new InputStreamReader(inputStream); - bufferedReader = new BufferedReader(inputStreamReader); - - outputStream = new FileOutputStream(dest); - outputStreamWriter = new OutputStreamWriter(outputStream); - bufferedWriter = new BufferedWriter(outputStreamWriter); - - //刷新缓存到硬盘中 - bufferedWriter.flush(); - - String L; - while ((L = bufferedReader.readLine()) != null) { - bufferedWriter.write(L + "\r\n"); - } - } catch (Exception e) { - log.error(e.getMessage(), e); - } finally { - try { - // 先打开后关闭 - ResourceTool.close(bufferedReader, inputStreamReader, inputStream); - log.info("close all input stream"); - - ResourceTool.close(bufferedWriter, outputStreamWriter, outputStream); - log.info("close all output stream"); - } catch (Exception e) { - log.error(e.getMessage(), e); - } - } - } - - // 字节流 - void copyFileByByte(String from, String dest) { - FileInputStream inputStream = null; - FileOutputStream outputStream = null; - - try { - inputStream = new FileInputStream(from); - outputStream = new FileOutputStream(dest); - - // 字节缓冲 数组 - byte[] buffer = new byte[1024]; - while (inputStream.read(buffer) != -1) { - outputStream.write(buffer); - } - } catch (Exception e) { - log.error("", e); - } finally { - try { - ResourceTool.close(inputStream, outputStream); - } catch (IOException e) { - log.error(e.getMessage(), e); - } - } - } - - // 字符流 - void copyFileByChar(String from, String dest) { - FileReader fileReader = null; //输入流 - FileWriter fileWriter = null; //输出流 - - try { - fileReader = new FileReader(from); - fileWriter = new FileWriter(dest); - - //读入内存 - char[] p = new char[512]; - int n; - while ((n = fileReader.read(p)) != -1) { -// fileWriter.write(p);//不足512字符的话后面会乱码 - fileWriter.write(p, 0, n);//指定0-n长度 - - String cacheContent = new String(p, 0, n); - System.out.println(cacheContent); - } - - } catch (Exception e) { - log.error("", e); - } finally { - try { - ResourceTool.close(fileReader, fileWriter); - } catch (Exception e) { - log.error(e.getMessage(), e); - } - } - } - - // 缓冲字符流 按行读取 - void copyFileByCharBuffer(String from, String dest) { - BufferedReader reader = null; - BufferedWriter writer = null; - - try { - reader = new BufferedReader(new FileReader(from)); - writer = new BufferedWriter(new FileWriter(dest)); - - String s; - while ((s = reader.readLine()) != null) { - log.info("line: {}", s); - //输出 - writer.write(s + "\r\n"); - } - log.info("成功读取并写入"); - } catch (Exception e) { - log.error(e.getMessage(), e); - } finally { - try { - ResourceTool.close(reader, writer); - } catch (Exception e) { - log.error(e.getMessage(), e); - } - } - } - - // Java7 中引入的 Files - void copyByFiles(Path from, Path dest) throws IOException { - Files.copy(from, dest); - } -} diff --git a/io/src/main/java/com/github/kuangcp/io/ResourceTool.java b/io/src/main/java/com/github/kuangcp/io/ResourceTool.java deleted file mode 100644 index 07c8a9fd..00000000 --- a/io/src/main/java/com/github/kuangcp/io/ResourceTool.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.github.kuangcp.io; - -import java.io.Closeable; -import java.io.IOException; -import java.util.Objects; - -/** - * @author Github - * 2023-08-25 19:37 - */ -public class ResourceTool { - public static void close(Closeable... resources) throws IOException { - if (Objects.isNull(resources)) { - return; - } - for (Closeable closeable : resources) { - if (Objects.isNull(closeable)) { - continue; - } - closeable.close(); - } - } -} diff --git a/io/src/main/java/com/github/kuangcp/standard/Readme.md b/io/src/main/java/com/github/kuangcp/standard/Readme.md deleted file mode 100644 index 0755868f..00000000 --- a/io/src/main/java/com/github/kuangcp/standard/Readme.md +++ /dev/null @@ -1,5 +0,0 @@ -# 标准输入输出 - -http://www.runoob.com/java/java-files-io.html - -http://ifeve.com/java-io/ diff --git a/io/src/main/java/com/github/kuangcp/zip/package-info.java b/io/src/main/java/com/github/kuangcp/zip/package-info.java deleted file mode 100644 index a9b2b751..00000000 --- a/io/src/main/java/com/github/kuangcp/zip/package-info.java +++ /dev/null @@ -1,6 +0,0 @@ -/** - * created by https://gitee.com/gin9 - * TODO https://www.baeldung.com/java-compress-and-uncompress - * https://docs.oracle.com/javase/8/docs/api/java/util/zip/package-summary.html - */ -package com.github.kuangcp.zip; \ No newline at end of file diff --git a/io/src/test/java/com/github/kuangcp/PropertiesTest.java b/io/src/test/java/com/github/kuangcp/PropertiesTest.java deleted file mode 100644 index bdebb642..00000000 --- a/io/src/test/java/com/github/kuangcp/PropertiesTest.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.github.kuangcp; - -import java.io.FileInputStream; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.util.Properties; -import lombok.extern.slf4j.Slf4j; -import org.junit.Test; - -/** - * @author kuangcp on 18-8-21-下午4:51 - */ -@Slf4j -public class PropertiesTest { - - @Test - public void testRead() throws IOException { - String path = Properties.class.getResource("/properties/main.properties").getPath(); - Properties properties = new Properties(); - properties.load(new FileInputStream(path)); - - String a = properties.getProperty("A"); - String b = properties.getProperty("B"); - String decodedB = new String(b.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8); - - log.info("a={} b=[{}] [{}]", a, b, decodedB); - } -} diff --git a/io/src/test/java/com/github/kuangcp/file/CopyFileTest.java b/io/src/test/java/com/github/kuangcp/file/CopyFileTest.java deleted file mode 100644 index a1d7c829..00000000 --- a/io/src/test/java/com/github/kuangcp/file/CopyFileTest.java +++ /dev/null @@ -1,86 +0,0 @@ -package com.github.kuangcp.file; - -import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.MatcherAssert.assertThat; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Optional; -import lombok.extern.slf4j.Slf4j; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -/** - * created by https://gitee.com/gin9 - * - * @author kuangcp on 3/17/19-12:34 AM - */ -@Slf4j -public class CopyFileTest { - - private final CopyFile file = new CopyFile(); - - private final String root = CopyFileTest.class.getResource("/").getPath(); - private final String from = root + "test.log"; - private final String dest = root + "test2.log"; - private final Path fromPath = Paths.get(from); - private final Path destPath = Paths.get(dest); - - private final String content = "hello world"; - - @Before - public void createFile() throws IOException { - Files.createFile(fromPath); - Files.write(fromPath, content.getBytes()); - assert Files.exists(fromPath); - } - - @After - public void deleteFile() throws IOException { - Files.delete(fromPath); - Files.delete(destPath); - - assert !Files.exists(fromPath); - assert !Files.exists(destPath); - } - - @Test - public void testFirst() throws IOException { - file.copyFileWithSixChannel(from, dest); - validateResultFile(); - } - - @Test - public void testCopyByChar() throws IOException { - file.copyFileByChar(from, dest); - validateResultFile(); - } - - @Test - public void testCopyByByte() throws IOException { - file.copyFileByByte(from, dest); - validateResultFile(); - } - - @Test - public void testCopyByCharBuffer() throws IOException { - file.copyFileByCharBuffer(from, dest); - validateResultFile(); - } - - @Test - public void testCopyByFiles() throws IOException { - file.copyByFiles(fromPath, destPath); - validateResultFile(); - } - - private void validateResultFile() throws IOException { - Optional fileContent = Files.lines(destPath).reduce(String::concat); - assert fileContent.isPresent(); - log.debug("validate: content={}", fileContent.get()); - assertThat(fileContent.get(), equalTo(content)); - } -} \ No newline at end of file diff --git a/network/README.md b/network/README.md index 2d5a31e0..c4029577 100644 --- a/network/README.md +++ b/network/README.md @@ -1,4 +1,4 @@ # Socket -> 套接字编程 [详细笔记](https://github.com/Kuangcp/Note/blob/master/Java/AdvancedLearning/Socket.md) | -[IO基础笔记](https://github.com/Kuangcp/Note/blob/master/Java/AdvancedLearning/IO.md) +> 套接字编程 [详细笔记](https://github.com/Kuangcp/Note/blob/master/Java/AdvancedLearning/JavaNetwork.md) | +[IO基础笔记](https://github.com/Kuangcp/Note/blob/master/Java/AdvancedLearning/JavaIO.md) diff --git a/network/pom.xml b/network/pom.xml index ff014ed4..05a4b5d7 100644 --- a/network/pom.xml +++ b/network/pom.xml @@ -26,6 +26,10 @@ com.squareup.okhttp3 okhttp + + org.apache.commons + commons-lang3 + diff --git a/network/src/main/java/com/github/kuangcp/io/CopyFile.java b/network/src/main/java/com/github/kuangcp/io/CopyFile.java new file mode 100644 index 00000000..e72a8d23 --- /dev/null +++ b/network/src/main/java/com/github/kuangcp/io/CopyFile.java @@ -0,0 +1,149 @@ +package com.github.kuangcp.io; + +import lombok.extern.slf4j.Slf4j; + +import java.io.*; +import java.nio.file.Files; +import java.nio.file.Path; + +/** + * 关于相对路径文件的复制问题 + * + * InputStream 所有输入流基类(建立流)(字节流) InputStreamReader 将字节流 转换成 字符流 BufferedReader 从字符流输入流读取文件 + * + * 注意: 如果输出流尚未关闭 内存的数据尚未刷新到硬盘上, 会导致不一致的情况 + */ +@Slf4j +class CopyFile { + + // 字节流 字节流转换为字符流 缓冲字符流 -> + void copyFileWithSixChannel(String from, String dest) { + InputStream inputStream = null; + InputStreamReader inputStreamReader = null; + BufferedReader bufferedReader = null; + + OutputStream outputStream = null; + OutputStreamWriter outputStreamWriter = null; + BufferedWriter bufferedWriter = null; + + try { + // 在 idea 中就是 project(工作目录) 根目录+path + inputStream = new FileInputStream(from); + inputStreamReader = new InputStreamReader(inputStream); + bufferedReader = new BufferedReader(inputStreamReader); + + outputStream = new FileOutputStream(dest); + outputStreamWriter = new OutputStreamWriter(outputStream); + bufferedWriter = new BufferedWriter(outputStreamWriter); + + //刷新缓存到硬盘中 + bufferedWriter.flush(); + + String L; + while ((L = bufferedReader.readLine()) != null) { + bufferedWriter.write(L + "\r\n"); + } + } catch (Exception e) { + log.error(e.getMessage(), e); + } finally { + try { + // 先打开后关闭 + ResourceTool.close(bufferedReader, inputStreamReader, inputStream); + log.info("close all input stream"); + + ResourceTool.close(bufferedWriter, outputStreamWriter, outputStream); + log.info("close all output stream"); + } catch (Exception e) { + log.error(e.getMessage(), e); + } + } + } + + // 字节流 + void copyFileByByte(String from, String dest) { + FileInputStream inputStream = null; + FileOutputStream outputStream = null; + + try { + inputStream = new FileInputStream(from); + outputStream = new FileOutputStream(dest); + + // 字节缓冲 数组 + byte[] buffer = new byte[1024]; + while (inputStream.read(buffer) != -1) { + outputStream.write(buffer); + } + } catch (Exception e) { + log.error("", e); + } finally { + try { + ResourceTool.close(inputStream, outputStream); + } catch (IOException e) { + log.error(e.getMessage(), e); + } + } + } + + // 字符流 + void copyFileByChar(String from, String dest) { + FileReader fileReader = null; //输入流 + FileWriter fileWriter = null; //输出流 + + try { + fileReader = new FileReader(from); + fileWriter = new FileWriter(dest); + + //读入内存 + char[] p = new char[512]; + int n; + while ((n = fileReader.read(p)) != -1) { +// fileWriter.write(p);//不足512字符的话后面会乱码 + fileWriter.write(p, 0, n);//指定0-n长度 + + String cacheContent = new String(p, 0, n); + System.out.println(cacheContent); + } + + } catch (Exception e) { + log.error("", e); + } finally { + try { + ResourceTool.close(fileReader, fileWriter); + } catch (Exception e) { + log.error(e.getMessage(), e); + } + } + } + + // 缓冲字符流 按行读取 + void copyFileByCharBuffer(String from, String dest) { + BufferedReader reader = null; + BufferedWriter writer = null; + + try { + reader = new BufferedReader(new FileReader(from)); + writer = new BufferedWriter(new FileWriter(dest)); + + String s; + while ((s = reader.readLine()) != null) { + log.info("line: {}", s); + //输出 + writer.write(s + "\r\n"); + } + log.info("成功读取并写入"); + } catch (Exception e) { + log.error(e.getMessage(), e); + } finally { + try { + ResourceTool.close(reader, writer); + } catch (Exception e) { + log.error(e.getMessage(), e); + } + } + } + + // Java7 中引入的 Files + void copyByFiles(Path from, Path dest) throws IOException { + Files.copy(from, dest); + } +} diff --git a/network/src/test/java/com/github/kuangcp/io/CopyFileTest.java b/network/src/test/java/com/github/kuangcp/io/CopyFileTest.java new file mode 100644 index 00000000..1d7d297a --- /dev/null +++ b/network/src/test/java/com/github/kuangcp/io/CopyFileTest.java @@ -0,0 +1,87 @@ +package com.github.kuangcp.io; + +import lombok.extern.slf4j.Slf4j; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Optional; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.MatcherAssert.assertThat; + +/** + * created by https://gitee.com/gin9 + * + * @author kuangcp on 3/17/19-12:34 AM + */ +@Slf4j +public class CopyFileTest { + + private final CopyFile file = new CopyFile(); + + private final String root = CopyFileTest.class.getResource("/").getPath(); + private final String from = root + "test.log"; + private final String dest = root + "test2.log"; + private final Path fromPath = Paths.get(from); + private final Path destPath = Paths.get(dest); + + private final String content = "hello world"; + + @Before + public void createFile() throws IOException { + Files.createFile(fromPath); + Files.write(fromPath, content.getBytes()); + assert Files.exists(fromPath); + } + + @After + public void deleteFile() throws IOException { + Files.delete(fromPath); + Files.delete(destPath); + + assert !Files.exists(fromPath); + assert !Files.exists(destPath); + } + + @Test + public void testFirst() throws IOException { + file.copyFileWithSixChannel(from, dest); + validateResultFile(); + } + + @Test + public void testCopyByChar() throws IOException { + file.copyFileByChar(from, dest); + validateResultFile(); + } + + @Test + public void testCopyByByte() throws IOException { + file.copyFileByByte(from, dest); + validateResultFile(); + } + + @Test + public void testCopyByCharBuffer() throws IOException { + file.copyFileByCharBuffer(from, dest); + validateResultFile(); + } + + @Test + public void testCopyByFiles() throws IOException { + file.copyByFiles(fromPath, destPath); + validateResultFile(); + } + + private void validateResultFile() throws IOException { + Optional fileContent = Files.lines(destPath).reduce(String::concat); + assert fileContent.isPresent(); + log.debug("validate: content={}", fileContent.get()); + assertThat(fileContent.get(), equalTo(content)); + } +} \ No newline at end of file diff --git a/network/src/test/java/com/github/kuangcp/io/PropertiesTest.java b/network/src/test/java/com/github/kuangcp/io/PropertiesTest.java new file mode 100644 index 00000000..3757b08e --- /dev/null +++ b/network/src/test/java/com/github/kuangcp/io/PropertiesTest.java @@ -0,0 +1,29 @@ +package com.github.kuangcp.io; + +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; + +import java.io.FileInputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Properties; + +/** + * @author kuangcp on 18-8-21-下午4:51 + */ +@Slf4j +public class PropertiesTest { + + @Test + public void testRead() throws IOException { + String path = Properties.class.getResource("/properties/main.properties").getPath(); + Properties properties = new Properties(); + properties.load(new FileInputStream(path)); + + String a = properties.getProperty("A"); + String b = properties.getProperty("B"); + String decodedB = new String(b.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8); + + log.info("a={} b=[{}] [{}]", a, b, decodedB); + } +} diff --git a/io/src/test/java/buffer/BufferTest.java b/network/src/test/java/com/github/kuangcp/nio/buffer/BufferTest.java similarity index 95% rename from io/src/test/java/buffer/BufferTest.java rename to network/src/test/java/com/github/kuangcp/nio/buffer/BufferTest.java index 67c4226e..faee3039 100644 --- a/io/src/test/java/buffer/BufferTest.java +++ b/network/src/test/java/com/github/kuangcp/nio/buffer/BufferTest.java @@ -1,6 +1,4 @@ -package buffer; - -import java.io.File; +package com.github.kuangcp.nio.buffer; import lombok.extern.slf4j.Slf4j; import org.junit.Test; diff --git a/io/src/test/resources/properties/main.properties b/network/src/test/resources/properties/main.properties similarity index 100% rename from io/src/test/resources/properties/main.properties rename to network/src/test/resources/properties/main.properties diff --git a/pom.xml b/pom.xml index b8c2a5b3..d9c793c3 100644 --- a/pom.xml +++ b/pom.xml @@ -28,7 +28,6 @@ guava gui hadoop - io java8 kafka mybatis From 3059e6edadb927254de91ac551ea456517183bc3 Mon Sep 17 00:00:00 2001 From: kuangcp Date: Tue, 19 Dec 2023 14:25:38 +0800 Subject: [PATCH 317/476] merge package --- .../github/kuangcp/generic}/ArrayUtils.java | 2 +- .../java/com/github/kuangcp/generic}/Ioc.java | 7 ++--- .../com/github/kuangcp/generic}/README.md | 0 .../github/kuangcp/generic}/common/Human.java | 2 +- .../kuangcp/generic}/common/Junior.java | 2 +- .../kuangcp/generic}/common/Student.java | 2 +- .../generic}/distribution/SampleAble.java | 2 +- .../generic}/distribution/SampleUtil.java | 16 +++++------ .../kuangcp/generic}/inherit/Container.java | 2 +- .../generic}/nesting/AbstractLoader.java | 2 +- .../kuangcp/generic}/nesting/HumanLoader.java | 2 +- .../kuangcp/generic}/nesting/HumanVO.java | 2 +- .../kuangcp/generic}/nesting/JsonVO.java | 2 +- .../github/kuangcp/generic}/nesting/Readme.md | 0 .../kuangcp/generic}/simple/DateInterval.java | 2 +- .../github/kuangcp/generic}/simple/Pair.java | 2 +- .../generic}/simple/SimpleGenericMethod.java | 2 +- .../test/java/com/github/kuangcp/README.md | 2 -- .../test/java/com/github/kuangcp/Readme.md | 1 - .../kuangcp/generic}/ArrayUtilsTest.java | 7 ++--- .../generic}/constraint/GenericArrayTest.java | 5 ++-- .../kuangcp/generic}/distribution/DogRef.java | 2 +- .../generic}/distribution/SampleUtilTest.java | 17 ++++++------ .../kuangcp/generic}/inherit/InheritTest.java | 5 ++-- .../generic}/nesting/HumanLoaderTest.java | 5 ++-- .../kuangcp/generic}/simple/PairTest.java | 2 +- .../github/kuangcp/generic}/simple/Score.java | 2 +- .../simple/SimpleGenericMethodTest.java | 2 +- generic/pom.xml | 27 ------------------- network/pom.xml | 12 +++++++++ pom.xml | 1 - 31 files changed, 61 insertions(+), 78 deletions(-) rename {generic/src/main/java/com/github/kuangcp => class/src/main/java/com/github/kuangcp/generic}/ArrayUtils.java (95%) rename {generic/src/main/java/com/github/kuangcp => class/src/main/java/com/github/kuangcp/generic}/Ioc.java (83%) rename {generic/src/main/java/com/github/kuangcp => class/src/main/java/com/github/kuangcp/generic}/README.md (100%) rename {generic/src/main/java/com/github/kuangcp => class/src/main/java/com/github/kuangcp/generic}/common/Human.java (86%) rename {generic/src/main/java/com/github/kuangcp => class/src/main/java/com/github/kuangcp/generic}/common/Junior.java (83%) rename {generic/src/main/java/com/github/kuangcp => class/src/main/java/com/github/kuangcp/generic}/common/Student.java (87%) rename {generic/src/main/java/com/github/kuangcp => class/src/main/java/com/github/kuangcp/generic}/distribution/SampleAble.java (66%) rename {generic/src/main/java/com/github/kuangcp => class/src/main/java/com/github/kuangcp/generic}/distribution/SampleUtil.java (96%) rename {generic/src/main/java/com/github/kuangcp => class/src/main/java/com/github/kuangcp/generic}/inherit/Container.java (90%) rename {generic/src/main/java/com/github/kuangcp => class/src/main/java/com/github/kuangcp/generic}/nesting/AbstractLoader.java (90%) rename {generic/src/main/java/com/github/kuangcp => class/src/main/java/com/github/kuangcp/generic}/nesting/HumanLoader.java (87%) rename {generic/src/main/java/com/github/kuangcp => class/src/main/java/com/github/kuangcp/generic}/nesting/HumanVO.java (83%) rename {generic/src/main/java/com/github/kuangcp => class/src/main/java/com/github/kuangcp/generic}/nesting/JsonVO.java (66%) rename {generic/src/main/java/com/github/kuangcp => class/src/main/java/com/github/kuangcp/generic}/nesting/Readme.md (100%) rename {generic/src/main/java/com/github/kuangcp => class/src/main/java/com/github/kuangcp/generic}/simple/DateInterval.java (98%) rename {generic/src/main/java/com/github/kuangcp => class/src/main/java/com/github/kuangcp/generic}/simple/Pair.java (97%) rename {generic/src/main/java/com/github/kuangcp => class/src/main/java/com/github/kuangcp/generic}/simple/SimpleGenericMethod.java (95%) delete mode 100644 class/src/test/java/com/github/kuangcp/README.md delete mode 100644 class/src/test/java/com/github/kuangcp/Readme.md rename {generic/src/test/java/com/github/kuangcp => class/src/test/java/com/github/kuangcp/generic}/ArrayUtilsTest.java (96%) rename {generic/src/test/java/com/github/kuangcp => class/src/test/java/com/github/kuangcp/generic}/constraint/GenericArrayTest.java (96%) rename {generic/src/test/java/com/github/kuangcp => class/src/test/java/com/github/kuangcp/generic}/distribution/DogRef.java (90%) rename {generic/src/test/java/com/github/kuangcp => class/src/test/java/com/github/kuangcp/generic}/distribution/SampleUtilTest.java (98%) rename {generic/src/test/java/com/github/kuangcp => class/src/test/java/com/github/kuangcp/generic}/inherit/InheritTest.java (97%) rename {generic/src/test/java/com/github/kuangcp => class/src/test/java/com/github/kuangcp/generic}/nesting/HumanLoaderTest.java (86%) rename {generic/src/test/java/com/github/kuangcp => class/src/test/java/com/github/kuangcp/generic}/simple/PairTest.java (99%) rename {generic/src/test/java/com/github/kuangcp => class/src/test/java/com/github/kuangcp/generic}/simple/Score.java (96%) rename {generic/src/test/java/com/github/kuangcp => class/src/test/java/com/github/kuangcp/generic}/simple/SimpleGenericMethodTest.java (95%) delete mode 100644 generic/pom.xml diff --git a/generic/src/main/java/com/github/kuangcp/ArrayUtils.java b/class/src/main/java/com/github/kuangcp/generic/ArrayUtils.java similarity index 95% rename from generic/src/main/java/com/github/kuangcp/ArrayUtils.java rename to class/src/main/java/com/github/kuangcp/generic/ArrayUtils.java index 76b0a72e..32cc5827 100644 --- a/generic/src/main/java/com/github/kuangcp/ArrayUtils.java +++ b/class/src/main/java/com/github/kuangcp/generic/ArrayUtils.java @@ -1,4 +1,4 @@ -package com.github.kuangcp; +package com.github.kuangcp.generic; import java.lang.reflect.Array; import java.util.Arrays; diff --git a/generic/src/main/java/com/github/kuangcp/Ioc.java b/class/src/main/java/com/github/kuangcp/generic/Ioc.java similarity index 83% rename from generic/src/main/java/com/github/kuangcp/Ioc.java rename to class/src/main/java/com/github/kuangcp/generic/Ioc.java index 9fc9942f..fcf31d01 100644 --- a/generic/src/main/java/com/github/kuangcp/Ioc.java +++ b/class/src/main/java/com/github/kuangcp/generic/Ioc.java @@ -1,7 +1,8 @@ -package com.github.kuangcp; +package com.github.kuangcp.generic; -import com.github.kuangcp.common.Human; -import com.github.kuangcp.common.Student; + +import com.github.kuangcp.generic.common.Human; +import com.github.kuangcp.generic.common.Student; /** * diff --git a/generic/src/main/java/com/github/kuangcp/README.md b/class/src/main/java/com/github/kuangcp/generic/README.md similarity index 100% rename from generic/src/main/java/com/github/kuangcp/README.md rename to class/src/main/java/com/github/kuangcp/generic/README.md diff --git a/generic/src/main/java/com/github/kuangcp/common/Human.java b/class/src/main/java/com/github/kuangcp/generic/common/Human.java similarity index 86% rename from generic/src/main/java/com/github/kuangcp/common/Human.java rename to class/src/main/java/com/github/kuangcp/generic/common/Human.java index 25ffc432..ee423da1 100644 --- a/generic/src/main/java/com/github/kuangcp/common/Human.java +++ b/class/src/main/java/com/github/kuangcp/generic/common/Human.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.common; +package com.github.kuangcp.generic.common; import lombok.Data; import lombok.EqualsAndHashCode; diff --git a/generic/src/main/java/com/github/kuangcp/common/Junior.java b/class/src/main/java/com/github/kuangcp/generic/common/Junior.java similarity index 83% rename from generic/src/main/java/com/github/kuangcp/common/Junior.java rename to class/src/main/java/com/github/kuangcp/generic/common/Junior.java index 41d4cb3e..e5e90c6f 100644 --- a/generic/src/main/java/com/github/kuangcp/common/Junior.java +++ b/class/src/main/java/com/github/kuangcp/generic/common/Junior.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.common; +package com.github.kuangcp.generic.common; import lombok.Data; import lombok.EqualsAndHashCode; diff --git a/generic/src/main/java/com/github/kuangcp/common/Student.java b/class/src/main/java/com/github/kuangcp/generic/common/Student.java similarity index 87% rename from generic/src/main/java/com/github/kuangcp/common/Student.java rename to class/src/main/java/com/github/kuangcp/generic/common/Student.java index 33793831..642bca00 100644 --- a/generic/src/main/java/com/github/kuangcp/common/Student.java +++ b/class/src/main/java/com/github/kuangcp/generic/common/Student.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.common; +package com.github.kuangcp.generic.common; import lombok.Data; import lombok.EqualsAndHashCode; diff --git a/generic/src/main/java/com/github/kuangcp/distribution/SampleAble.java b/class/src/main/java/com/github/kuangcp/generic/distribution/SampleAble.java similarity index 66% rename from generic/src/main/java/com/github/kuangcp/distribution/SampleAble.java rename to class/src/main/java/com/github/kuangcp/generic/distribution/SampleAble.java index 9e9030a0..edef0c73 100644 --- a/generic/src/main/java/com/github/kuangcp/distribution/SampleAble.java +++ b/class/src/main/java/com/github/kuangcp/generic/distribution/SampleAble.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.distribution; +package com.github.kuangcp.generic.distribution; /** * @author kuangcp on 3/21/19-5:42 PM diff --git a/generic/src/main/java/com/github/kuangcp/distribution/SampleUtil.java b/class/src/main/java/com/github/kuangcp/generic/distribution/SampleUtil.java similarity index 96% rename from generic/src/main/java/com/github/kuangcp/distribution/SampleUtil.java rename to class/src/main/java/com/github/kuangcp/generic/distribution/SampleUtil.java index f91eb1f7..a6da1648 100644 --- a/generic/src/main/java/com/github/kuangcp/distribution/SampleUtil.java +++ b/class/src/main/java/com/github/kuangcp/generic/distribution/SampleUtil.java @@ -1,18 +1,14 @@ -package com.github.kuangcp.distribution; +package com.github.kuangcp.generic.distribution; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Map; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.math3.distribution.EnumeratedIntegerDistribution; + +import java.util.*; import java.util.Map.Entry; -import java.util.Objects; -import java.util.Set; import java.util.concurrent.ThreadLocalRandom; import java.util.function.BiFunction; import java.util.stream.Collectors; import java.util.stream.IntStream; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.math3.distribution.EnumeratedIntegerDistribution; /** * @author kuangcp on 3/21/19-5:51 PM @@ -26,7 +22,7 @@ public class SampleUtil { * 放回式取样 概率相差很大时可能循环很多次 这是有缺陷的 */ public static List sampleWithNoRepeatedAndPutBack(List list, - int count) { + int count) { return sampleResult(list, count, SampleUtil::sampleWithNoRepeated); } diff --git a/generic/src/main/java/com/github/kuangcp/inherit/Container.java b/class/src/main/java/com/github/kuangcp/generic/inherit/Container.java similarity index 90% rename from generic/src/main/java/com/github/kuangcp/inherit/Container.java rename to class/src/main/java/com/github/kuangcp/generic/inherit/Container.java index 5b6aaf13..38ee9e5c 100644 --- a/generic/src/main/java/com/github/kuangcp/inherit/Container.java +++ b/class/src/main/java/com/github/kuangcp/generic/inherit/Container.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.inherit; +package com.github.kuangcp.generic.inherit; import java.util.ArrayList; import java.util.List; diff --git a/generic/src/main/java/com/github/kuangcp/nesting/AbstractLoader.java b/class/src/main/java/com/github/kuangcp/generic/nesting/AbstractLoader.java similarity index 90% rename from generic/src/main/java/com/github/kuangcp/nesting/AbstractLoader.java rename to class/src/main/java/com/github/kuangcp/generic/nesting/AbstractLoader.java index 6dfd5b7b..269f7481 100644 --- a/generic/src/main/java/com/github/kuangcp/nesting/AbstractLoader.java +++ b/class/src/main/java/com/github/kuangcp/generic/nesting/AbstractLoader.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.nesting; +package com.github.kuangcp.generic.nesting; import java.util.Map; diff --git a/generic/src/main/java/com/github/kuangcp/nesting/HumanLoader.java b/class/src/main/java/com/github/kuangcp/generic/nesting/HumanLoader.java similarity index 87% rename from generic/src/main/java/com/github/kuangcp/nesting/HumanLoader.java rename to class/src/main/java/com/github/kuangcp/generic/nesting/HumanLoader.java index 14168c00..4536baba 100644 --- a/generic/src/main/java/com/github/kuangcp/nesting/HumanLoader.java +++ b/class/src/main/java/com/github/kuangcp/generic/nesting/HumanLoader.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.nesting; +package com.github.kuangcp.generic.nesting; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; diff --git a/generic/src/main/java/com/github/kuangcp/nesting/HumanVO.java b/class/src/main/java/com/github/kuangcp/generic/nesting/HumanVO.java similarity index 83% rename from generic/src/main/java/com/github/kuangcp/nesting/HumanVO.java rename to class/src/main/java/com/github/kuangcp/generic/nesting/HumanVO.java index 42ffd01c..fb95f71a 100644 --- a/generic/src/main/java/com/github/kuangcp/nesting/HumanVO.java +++ b/class/src/main/java/com/github/kuangcp/generic/nesting/HumanVO.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.nesting; +package com.github.kuangcp.generic.nesting; import lombok.Data; diff --git a/generic/src/main/java/com/github/kuangcp/nesting/JsonVO.java b/class/src/main/java/com/github/kuangcp/generic/nesting/JsonVO.java similarity index 66% rename from generic/src/main/java/com/github/kuangcp/nesting/JsonVO.java rename to class/src/main/java/com/github/kuangcp/generic/nesting/JsonVO.java index bcf93435..ff8871a6 100644 --- a/generic/src/main/java/com/github/kuangcp/nesting/JsonVO.java +++ b/class/src/main/java/com/github/kuangcp/generic/nesting/JsonVO.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.nesting; +package com.github.kuangcp.generic.nesting; /** * @author kuangcp on 19-1-10-下午2:29 diff --git a/generic/src/main/java/com/github/kuangcp/nesting/Readme.md b/class/src/main/java/com/github/kuangcp/generic/nesting/Readme.md similarity index 100% rename from generic/src/main/java/com/github/kuangcp/nesting/Readme.md rename to class/src/main/java/com/github/kuangcp/generic/nesting/Readme.md diff --git a/generic/src/main/java/com/github/kuangcp/simple/DateInterval.java b/class/src/main/java/com/github/kuangcp/generic/simple/DateInterval.java similarity index 98% rename from generic/src/main/java/com/github/kuangcp/simple/DateInterval.java rename to class/src/main/java/com/github/kuangcp/generic/simple/DateInterval.java index 4d167b50..97858d3f 100644 --- a/generic/src/main/java/com/github/kuangcp/simple/DateInterval.java +++ b/class/src/main/java/com/github/kuangcp/generic/simple/DateInterval.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.simple; +package com.github.kuangcp.generic.simple; import java.util.Date; diff --git a/generic/src/main/java/com/github/kuangcp/simple/Pair.java b/class/src/main/java/com/github/kuangcp/generic/simple/Pair.java similarity index 97% rename from generic/src/main/java/com/github/kuangcp/simple/Pair.java rename to class/src/main/java/com/github/kuangcp/generic/simple/Pair.java index d0b8ff0b..57d168e2 100644 --- a/generic/src/main/java/com/github/kuangcp/simple/Pair.java +++ b/class/src/main/java/com/github/kuangcp/generic/simple/Pair.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.simple; +package com.github.kuangcp.generic.simple; import lombok.Data; diff --git a/generic/src/main/java/com/github/kuangcp/simple/SimpleGenericMethod.java b/class/src/main/java/com/github/kuangcp/generic/simple/SimpleGenericMethod.java similarity index 95% rename from generic/src/main/java/com/github/kuangcp/simple/SimpleGenericMethod.java rename to class/src/main/java/com/github/kuangcp/generic/simple/SimpleGenericMethod.java index c66416a9..70163d38 100644 --- a/generic/src/main/java/com/github/kuangcp/simple/SimpleGenericMethod.java +++ b/class/src/main/java/com/github/kuangcp/generic/simple/SimpleGenericMethod.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.simple; +package com.github.kuangcp.generic.simple; import java.io.Serializable; diff --git a/class/src/test/java/com/github/kuangcp/README.md b/class/src/test/java/com/github/kuangcp/README.md deleted file mode 100644 index 12846c45..00000000 --- a/class/src/test/java/com/github/kuangcp/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# 集合 -> [笔记详情](https://github.com/Kuangcp/Note/blob/master/Java/AdvancedLearning/Collection.md) diff --git a/class/src/test/java/com/github/kuangcp/Readme.md b/class/src/test/java/com/github/kuangcp/Readme.md deleted file mode 100644 index 2bbd3657..00000000 --- a/class/src/test/java/com/github/kuangcp/Readme.md +++ /dev/null @@ -1 +0,0 @@ -# 类和对象 \ No newline at end of file diff --git a/generic/src/test/java/com/github/kuangcp/ArrayUtilsTest.java b/class/src/test/java/com/github/kuangcp/generic/ArrayUtilsTest.java similarity index 96% rename from generic/src/test/java/com/github/kuangcp/ArrayUtilsTest.java rename to class/src/test/java/com/github/kuangcp/generic/ArrayUtilsTest.java index 096f70b2..25a954f9 100644 --- a/generic/src/test/java/com/github/kuangcp/ArrayUtilsTest.java +++ b/class/src/test/java/com/github/kuangcp/generic/ArrayUtilsTest.java @@ -1,10 +1,11 @@ -package com.github.kuangcp; +package com.github.kuangcp.generic; + +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; import java.util.Arrays; import java.util.List; import java.util.Objects; -import lombok.extern.slf4j.Slf4j; -import org.junit.Test; /** * @author kuangcp on 2019-04-22 11:39 AM diff --git a/generic/src/test/java/com/github/kuangcp/constraint/GenericArrayTest.java b/class/src/test/java/com/github/kuangcp/generic/constraint/GenericArrayTest.java similarity index 96% rename from generic/src/test/java/com/github/kuangcp/constraint/GenericArrayTest.java rename to class/src/test/java/com/github/kuangcp/generic/constraint/GenericArrayTest.java index 9ae5dba9..d55b2638 100644 --- a/generic/src/test/java/com/github/kuangcp/constraint/GenericArrayTest.java +++ b/class/src/test/java/com/github/kuangcp/generic/constraint/GenericArrayTest.java @@ -1,9 +1,10 @@ -package com.github.kuangcp.constraint; +package com.github.kuangcp.generic.constraint; + +import org.junit.Test; import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import org.junit.Test; /** * 不能实例化 参数化类型的数组, 声明是可以的 正确的解决方式应该是 参数化集合 diff --git a/generic/src/test/java/com/github/kuangcp/distribution/DogRef.java b/class/src/test/java/com/github/kuangcp/generic/distribution/DogRef.java similarity index 90% rename from generic/src/test/java/com/github/kuangcp/distribution/DogRef.java rename to class/src/test/java/com/github/kuangcp/generic/distribution/DogRef.java index 8497e21c..cc71e91c 100644 --- a/generic/src/test/java/com/github/kuangcp/distribution/DogRef.java +++ b/class/src/test/java/com/github/kuangcp/generic/distribution/DogRef.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.distribution; +package com.github.kuangcp.generic.distribution; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; diff --git a/generic/src/test/java/com/github/kuangcp/distribution/SampleUtilTest.java b/class/src/test/java/com/github/kuangcp/generic/distribution/SampleUtilTest.java similarity index 98% rename from generic/src/test/java/com/github/kuangcp/distribution/SampleUtilTest.java rename to class/src/test/java/com/github/kuangcp/generic/distribution/SampleUtilTest.java index f60e34cd..f286c848 100644 --- a/generic/src/test/java/com/github/kuangcp/distribution/SampleUtilTest.java +++ b/class/src/test/java/com/github/kuangcp/generic/distribution/SampleUtilTest.java @@ -1,17 +1,18 @@ -package com.github.kuangcp.distribution; - -import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.lessThan; +package com.github.kuangcp.generic.distribution; import com.github.kuangcp.time.GetRunTime; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + import java.util.List; import java.util.Map; import java.util.stream.Collectors; import java.util.stream.IntStream; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.lessThan; /** * @author kuangcp on 3/23/19-4:09 PM diff --git a/generic/src/test/java/com/github/kuangcp/inherit/InheritTest.java b/class/src/test/java/com/github/kuangcp/generic/inherit/InheritTest.java similarity index 97% rename from generic/src/test/java/com/github/kuangcp/inherit/InheritTest.java rename to class/src/test/java/com/github/kuangcp/generic/inherit/InheritTest.java index 4e500834..2c33552c 100644 --- a/generic/src/test/java/com/github/kuangcp/inherit/InheritTest.java +++ b/class/src/test/java/com/github/kuangcp/generic/inherit/InheritTest.java @@ -1,10 +1,11 @@ -package com.github.kuangcp.inherit; +package com.github.kuangcp.generic.inherit; import com.github.kuangcp.common.Human; import com.github.kuangcp.common.Student; +import org.junit.Test; + import java.util.Arrays; import java.util.List; -import org.junit.Test; /** * @author kuangcp on 3/6/19-3:16 PM diff --git a/generic/src/test/java/com/github/kuangcp/nesting/HumanLoaderTest.java b/class/src/test/java/com/github/kuangcp/generic/nesting/HumanLoaderTest.java similarity index 86% rename from generic/src/test/java/com/github/kuangcp/nesting/HumanLoaderTest.java rename to class/src/test/java/com/github/kuangcp/generic/nesting/HumanLoaderTest.java index c05328bd..d9c07811 100644 --- a/generic/src/test/java/com/github/kuangcp/nesting/HumanLoaderTest.java +++ b/class/src/test/java/com/github/kuangcp/generic/nesting/HumanLoaderTest.java @@ -1,8 +1,9 @@ -package com.github.kuangcp.nesting; +package com.github.kuangcp.generic.nesting; -import java.util.Map; import org.junit.Test; +import java.util.Map; + /** * @author kuangcp on 3/6/19-3:27 PM */ diff --git a/generic/src/test/java/com/github/kuangcp/simple/PairTest.java b/class/src/test/java/com/github/kuangcp/generic/simple/PairTest.java similarity index 99% rename from generic/src/test/java/com/github/kuangcp/simple/PairTest.java rename to class/src/test/java/com/github/kuangcp/generic/simple/PairTest.java index 60ff70cb..62be346b 100644 --- a/generic/src/test/java/com/github/kuangcp/simple/PairTest.java +++ b/class/src/test/java/com/github/kuangcp/generic/simple/PairTest.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.simple; +package com.github.kuangcp.generic.simple; import com.github.kuangcp.common.Human; import com.github.kuangcp.common.Junior; diff --git a/generic/src/test/java/com/github/kuangcp/simple/Score.java b/class/src/test/java/com/github/kuangcp/generic/simple/Score.java similarity index 96% rename from generic/src/test/java/com/github/kuangcp/simple/Score.java rename to class/src/test/java/com/github/kuangcp/generic/simple/Score.java index 29b0c330..d50be800 100644 --- a/generic/src/test/java/com/github/kuangcp/simple/Score.java +++ b/class/src/test/java/com/github/kuangcp/generic/simple/Score.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.simple; +package com.github.kuangcp.generic.simple; import java.io.Serializable; diff --git a/generic/src/test/java/com/github/kuangcp/simple/SimpleGenericMethodTest.java b/class/src/test/java/com/github/kuangcp/generic/simple/SimpleGenericMethodTest.java similarity index 95% rename from generic/src/test/java/com/github/kuangcp/simple/SimpleGenericMethodTest.java rename to class/src/test/java/com/github/kuangcp/generic/simple/SimpleGenericMethodTest.java index df0cd8f0..1906230f 100644 --- a/generic/src/test/java/com/github/kuangcp/simple/SimpleGenericMethodTest.java +++ b/class/src/test/java/com/github/kuangcp/generic/simple/SimpleGenericMethodTest.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.simple; +package com.github.kuangcp.generic.simple; import org.junit.Test; diff --git a/generic/pom.xml b/generic/pom.xml deleted file mode 100644 index 656fa679..00000000 --- a/generic/pom.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - com.github.kuangcp - java-base - 1.0.0 - - - 4.0.0 - - generic - 1.0.0 - - - UTF-8 - UTF-8 - - - - org.apache.commons - commons-math3 - - - - diff --git a/network/pom.xml b/network/pom.xml index 05a4b5d7..f6ac5da4 100644 --- a/network/pom.xml +++ b/network/pom.xml @@ -30,6 +30,18 @@ org.apache.commons commons-lang3 + + + commons-net + commons-net + 3.6 + + + org.mockftpserver + MockFtpServer + 2.7.1 + test + diff --git a/pom.xml b/pom.xml index d9c793c3..c525d94a 100644 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,6 @@ class concurrency flink - generic guava gui hadoop From 9e3f014291c20c58509b4914c6aef2ae2243fc5d Mon Sep 17 00:00:00 2001 From: kuangcp Date: Tue, 19 Dec 2023 14:51:55 +0800 Subject: [PATCH 318/476] ftp --- README.md | 16 +- network/pom.xml | 16 +- .../kuangcp/app/ftp/FTPClientExample.java | 479 ++++++++++++++++++ .../com/github/kuangcp/app/ftp/FtpClient.java | 74 +++ .../kuangcp/app/ftp/FtpClientMockTest.java | 58 +++ .../github/kuangcp/app/ftp/FtpClientTest.java | 40 ++ .../com/github/kuangcp/io/PropertiesTest.java | 1 + 7 files changed, 673 insertions(+), 11 deletions(-) create mode 100644 network/src/main/java/com/github/kuangcp/app/ftp/FTPClientExample.java create mode 100644 network/src/main/java/com/github/kuangcp/app/ftp/FtpClient.java create mode 100644 network/src/test/java/com/github/kuangcp/app/ftp/FtpClientMockTest.java create mode 100644 network/src/test/java/com/github/kuangcp/app/ftp/FtpClientTest.java diff --git a/README.md b/README.md index 5b2ffeb7..7a31e10e 100644 --- a/README.md +++ b/README.md @@ -8,15 +8,13 @@ ************************ -| 基础 | 进阶 | 应用 | -|:----|:----|:----| -| [Java8](/java8) | [并发](/concurrency) | [Netty](/netty)| -| [集合](/collection) | [网络](/network) | [Guava](/guava)| -| [算法](/algorithms) | [设计模式](/pattern) | [Spring](/spring) -| [字节码](/class) | [测试](/test) | [Kafka](/kafka)| -| [泛型](/generic) | | [Hadoop](/hadoop) | - -[Java Gui](/gui) | [业务问题](/question) +| 基础 | 进阶 | 应用 | +|:-------------------:|:---:|:-------------------------------:| +| [集合 字节码 反射](/class) | [并发](/concurrency) | [Netty](/netty) | +| [Java8](/java8) | [网络](/network) | [Guava](/guava) | +| [算法](/algorithms) | [设计模式](/pattern) | [Spring](/spring) | +| [Java Gui](/gui) | [测试](/test) | [业务问题](/question) | + diff --git a/network/pom.xml b/network/pom.xml index f6ac5da4..6d35dfea 100644 --- a/network/pom.xml +++ b/network/pom.xml @@ -21,6 +21,12 @@ com.github.kuangcp kcp-tool + + + slf4j-api + org.slf4j + + com.squareup.okhttp3 @@ -34,13 +40,19 @@ commons-net commons-net - 3.6 + 3.10.0 org.mockftpserver MockFtpServer - 2.7.1 + 3.1.0 test + + + slf4j-api + org.slf4j + + diff --git a/network/src/main/java/com/github/kuangcp/app/ftp/FTPClientExample.java b/network/src/main/java/com/github/kuangcp/app/ftp/FTPClientExample.java new file mode 100644 index 00000000..00fcc030 --- /dev/null +++ b/network/src/main/java/com/github/kuangcp/app/ftp/FTPClientExample.java @@ -0,0 +1,479 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.github.kuangcp.app.ftp; + +import org.apache.commons.net.PrintCommandListener; +import org.apache.commons.net.ftp.*; +import org.apache.commons.net.io.CopyStreamEvent; +import org.apache.commons.net.io.CopyStreamListener; +import org.apache.commons.net.util.TrustManagerUtils; + +import java.io.*; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.Arrays; + +/** + * https://commons.apache.org/proper/commons-net/ + * + * This is an example program demonstrating how to use the FTPClient class. This program connects to an FTP server and retrieves the specified file. If the -s + * flag is used, it stores the local file at the FTP server. Just so you can see what's happening, all reply strings are printed. If the -b flag is used, a + * binary transfer is assumed (default is ASCII). See below for further options. + */ +public final class FTPClientExample { + + public static final String USAGE = "Expected Parameters: [options] [ []]\n" + + "\nDefault behavior is to download a file and use ASCII transfer mode.\n" + "\t-a - use local active mode (default is local passive)\n" + + "\t-A - anonymous login (omit user and password parameters)\n" + "\t-b - use binary transfer mode\n" + + "\t-c cmd - issue arbitrary command (remote is used as a parameter if provided) \n" + + "\t-d - list directory details using MLSD (remote is used as the pathname if provided)\n" + "\t-e - use EPSV with IPv4 (default false)\n" + + "\t-E - encoding to use for control channel\n" + "\t-f - issue FEAT command (remote and local files are ignored)\n" + + "\t-h - list hidden files (applies to -l and -n only)\n" + "\t-i - issue SIZE command for a file\n" + + "\t-k secs - use keep-alive timer (setControlKeepAliveTimeout)\n" + "\t-l - list files using LIST (remote is used as the pathname if provided)\n" + + "\t Files are listed twice: first in raw mode, then as the formatted parsed data.\n" + + "\t N.B. if the wrong server-type is used, output may be lost. Use -U or -S as necessary.\n" + + "\t-L - use lenient future dates (server dates may be up to 1 day into future)\n" + + "\t-m - list file details using MDTM (remote is used as the pathname if provided)\n" + + "\t-n - list file names using NLST (remote is used as the pathname if provided)\n" + + "\t-p true|false|protocol[,true|false] - use FTPSClient with the specified protocol and/or isImplicit setting\n" + + "\t-s - store file on server (upload)\n" + "\t-S - systemType set server system type (e.g. UNIX VMS WINDOWS)\n" + + "\t-t - list file details using MLST (remote is used as the pathname if provided)\n" + "\t-U - save unparseable responses\n" + + "\t-w msec - wait time for keep-alive reply (setControlKeepAliveReplyTimeout)\n" + + "\t-T all|valid|none - use one of the built-in TrustManager implementations (none = JVM default)\n" + + "\t-y format - set default date format string\n" + "\t-Y format - set recent date format string\n" + + "\t-Z timezone - set the server time zone for parsing LIST responses\n" + + "\t-z timezone - set the time zone for displaying MDTM, LIST, MLSD, MLST responses\n" + + "\t-PrH server[:port] - HTTP Proxy host and optional port[80] \n" + "\t-PrU user - HTTP Proxy server user\n" + + "\t-PrP password - HTTP Proxy server password\n" + "\t-# - add hash display during transfers\n"; + + private static CopyStreamListener createListener() { + return new CopyStreamListener() { + private long megsTotal; + + @Override + public void bytesTransferred(final CopyStreamEvent event) { + bytesTransferred(event.getTotalBytesTransferred(), event.getBytesTransferred(), event.getStreamSize()); + } + + @Override + public void bytesTransferred(final long totalBytesTransferred, final int bytesTransferred, final long streamSize) { + final long megs = totalBytesTransferred / 1000000; + for (long l = megsTotal; l < megs; l++) { + System.err.print("#"); + } + megsTotal = megs; + } + }; + } + + public static void main(final String[] args) throws UnknownHostException { + boolean storeFile = false, binaryTransfer = false, error = false, listFiles = false, listNames = false, hidden = false; + boolean localActive = false, useEpsvWithIPv4 = false, feat = false, printHash = false; + boolean mlst = false, mlsd = false, mdtm = false, saveUnparseable = false; + boolean size = false; + boolean lenient = false; + long keepAliveTimeoutSeconds = -1; + int controlKeepAliveReplyTimeoutMillis = -1; + int minParams = 5; // listings require 3 params + String protocol = null; // SSL protocol + String doCommand = null; + String trustmgr = null; + String proxyHost = null; + int proxyPort = 80; + String proxyUser = null; + String proxyPassword = null; + String user = null; + String password = null; + String encoding = null; + String serverTimeZoneId = null; + String displayTimeZoneId = null; + String serverType = null; + String defaultDateFormat = null; + String recentDateFormat = null; + + int base = 0; + for (base = 0; base < args.length; base++) { + if (args[base].equals("-s")) { + storeFile = true; + } else if (args[base].equals("-a")) { + localActive = true; + } else if (args[base].equals("-A")) { + user = "anonymous"; + password = System.getProperty("user.name") + "@" + InetAddress.getLocalHost().getHostName(); + } else if (args[base].equals("-b")) { + binaryTransfer = true; + } else if (args[base].equals("-c")) { + doCommand = args[++base]; + minParams = 3; + } else if (args[base].equals("-d")) { + mlsd = true; + minParams = 3; + } else if (args[base].equals("-e")) { + useEpsvWithIPv4 = true; + } else if (args[base].equals("-E")) { + encoding = args[++base]; + } else if (args[base].equals("-f")) { + feat = true; + minParams = 3; + } else if (args[base].equals("-h")) { + hidden = true; + } else if (args[base].equals("-i")) { + size = true; + minParams = 3; + } else if (args[base].equals("-k")) { + keepAliveTimeoutSeconds = Long.parseLong(args[++base]); + } else if (args[base].equals("-l")) { + listFiles = true; + minParams = 3; + } else if (args[base].equals("-m")) { + mdtm = true; + minParams = 3; + } else if (args[base].equals("-L")) { + lenient = true; + } else if (args[base].equals("-n")) { + listNames = true; + minParams = 3; + } else if (args[base].equals("-p")) { + protocol = args[++base]; + } else if (args[base].equals("-S")) { + serverType = args[++base]; + } else if (args[base].equals("-t")) { + mlst = true; + minParams = 3; + } else if (args[base].equals("-U")) { + saveUnparseable = true; + } else if (args[base].equals("-w")) { + controlKeepAliveReplyTimeoutMillis = Integer.parseInt(args[++base]); + } else if (args[base].equals("-T")) { + trustmgr = args[++base]; + } else if (args[base].equals("-y")) { + defaultDateFormat = args[++base]; + } else if (args[base].equals("-Y")) { + recentDateFormat = args[++base]; + } else if (args[base].equals("-Z")) { + serverTimeZoneId = args[++base]; + } else if (args[base].equals("-z")) { + displayTimeZoneId = args[++base]; + } else if (args[base].equals("-PrH")) { + proxyHost = args[++base]; + final String[] parts = proxyHost.split(":"); + if (parts.length == 2) { + proxyHost = parts[0]; + proxyPort = Integer.parseInt(parts[1]); + } + } else if (args[base].equals("-PrU")) { + proxyUser = args[++base]; + } else if (args[base].equals("-PrP")) { + proxyPassword = args[++base]; + } else if (args[base].equals("-#")) { + printHash = true; + } else { + break; + } + } + + final int remain = args.length - base; + if (user != null) { + minParams -= 2; + } + if (remain < minParams) // server, user, pass, remote, local [protocol] + { + if (args.length > 0) { + System.err.println("Actual Parameters: " + Arrays.toString(args)); + } + System.err.println(USAGE); + System.exit(1); + } + + String server = args[base++]; + int port = 0; + final String[] parts = server.split(":"); + if (parts.length == 2) { + server = parts[0]; + port = Integer.parseInt(parts[1]); + } + if (user == null) { + user = args[base++]; + password = args[base++]; + } + + String remote = null; + if (args.length - base > 0) { + remote = args[base++]; + } + + String local = null; + if (args.length - base > 0) { + local = args[base++]; + } + + final FTPClient ftp; + if (protocol == null) { + if (proxyHost != null) { + System.out.println("Using HTTP proxy server: " + proxyHost); + ftp = new FTPHTTPClient(proxyHost, proxyPort, proxyUser, proxyPassword); + } else { + ftp = new FTPClient(); + } + } else { + final FTPSClient ftps; + if (protocol.equals("true")) { + ftps = new FTPSClient(true); + } else if (protocol.equals("false")) { + ftps = new FTPSClient(false); + } else { + final String[] prot = protocol.split(","); + if (prot.length == 1) { // Just protocol + ftps = new FTPSClient(protocol); + } else { // protocol,true|false + ftps = new FTPSClient(prot[0], Boolean.parseBoolean(prot[1])); + } + } + ftp = ftps; + if ("all".equals(trustmgr)) { + ftps.setTrustManager(TrustManagerUtils.getAcceptAllTrustManager()); + } else if ("valid".equals(trustmgr)) { + ftps.setTrustManager(TrustManagerUtils.getValidateServerCertificateTrustManager()); + } else if ("none".equals(trustmgr)) { + ftps.setTrustManager(null); + } + } + + if (printHash) { + ftp.setCopyStreamListener(createListener()); + } + if (keepAliveTimeoutSeconds >= 0) { + ftp.setControlKeepAliveTimeout(keepAliveTimeoutSeconds); + } + if (controlKeepAliveReplyTimeoutMillis >= 0) { + ftp.setControlKeepAliveReplyTimeout(controlKeepAliveReplyTimeoutMillis); + } + if (encoding != null) { + ftp.setControlEncoding(encoding); + } + ftp.setListHiddenFiles(hidden); + + // suppress login details + ftp.addProtocolCommandListener(new PrintCommandListener(new PrintWriter(System.out), true)); + + final FTPClientConfig config; + if (serverType != null) { + config = new FTPClientConfig(serverType); + } else { + config = new FTPClientConfig(); + } + config.setUnparseableEntries(saveUnparseable); + if (defaultDateFormat != null) { + config.setDefaultDateFormatStr(defaultDateFormat); + } + if (recentDateFormat != null) { + config.setRecentDateFormatStr(recentDateFormat); + } + ftp.configure(config); + + try { + final int reply; + if (port > 0) { + ftp.connect(server, port); + } else { + ftp.connect(server); + } + System.out.println("Connected to " + server + " on " + (port > 0 ? port : ftp.getDefaultPort())); + + // After connection attempt, you should check the reply code to verify + // success. + reply = ftp.getReplyCode(); + + if (!FTPReply.isPositiveCompletion(reply)) { + ftp.disconnect(); + System.err.println("FTP server refused connection."); + System.exit(1); + } + } catch (final IOException e) { + if (ftp.isConnected()) { + try { + ftp.disconnect(); + } catch (final IOException f) { + // do nothing + } + } + System.err.println("Could not connect to server."); + e.printStackTrace(); + System.exit(1); + } + + __main: + try { + if (!ftp.login(user, password)) { + ftp.logout(); + error = true; + break __main; + } + + System.out.println("Remote system is " + ftp.getSystemType()); + + if (binaryTransfer) { + ftp.setFileType(FTP.BINARY_FILE_TYPE); + } else { + // in theory this should not be necessary as servers should default to ASCII, + // but they don't all do so - see NET-500 + ftp.setFileType(FTP.ASCII_FILE_TYPE); + } + + // Use passive mode as default because most of us are + // behind firewalls these days. + if (localActive) { + ftp.enterLocalActiveMode(); + } else { + ftp.enterLocalPassiveMode(); + } + + ftp.setUseEPSVwithIPv4(useEpsvWithIPv4); + + if (storeFile) { + try (final InputStream input = new FileInputStream(local)) { + ftp.storeFile(remote, input); + } + + if (keepAliveTimeoutSeconds > 0) { + showCslStats(ftp); + } + } + // Allow multiple list types for single invocation + else if (listFiles || mlsd || mdtm || mlst || listNames || size) { + if (mlsd) { + for (final FTPFile f : ftp.mlistDir(remote)) { + System.out.println(f.getRawListing()); + System.out.println(f.toFormattedString(displayTimeZoneId)); + } + } + if (mdtm) { + final FTPFile f = ftp.mdtmFile(remote); + if (f != null) { + System.out.println(f.getRawListing()); + System.out.println(f.toFormattedString(displayTimeZoneId)); + } else { + System.out.println("File not found"); + } + } + if (mlst) { + final FTPFile f = ftp.mlistFile(remote); + if (f != null) { + System.out.println(f.toFormattedString(displayTimeZoneId)); + } + } + if (listNames) { + for (final String s : ftp.listNames(remote)) { + System.out.println(s); + } + } + if (size) { + // TODO ? +// System.out.println("Size=" + ftp.getSize(remote)); + } + // Do this last because it changes the client + if (listFiles) { + if (lenient || serverTimeZoneId != null) { + config.setLenientFutureDates(lenient); + if (serverTimeZoneId != null) { + config.setServerTimeZoneId(serverTimeZoneId); + } + ftp.configure(config); + } + + for (final FTPFile f : ftp.listFiles(remote)) { + System.out.println(f.getRawListing()); + System.out.println(f.toFormattedString(displayTimeZoneId)); + } + } + } else if (feat) { + // boolean feature check + if (remote != null) { // See if the command is present + if (ftp.hasFeature(remote)) { + System.out.println("Has feature: " + remote); + } else if (FTPReply.isPositiveCompletion(ftp.getReplyCode())) { + System.out.println("FEAT " + remote + " was not detected"); + } else { + System.out.println("Command failed: " + ftp.getReplyString()); + } + + // Strings feature check + final String[] features = ftp.featureValues(remote); + if (features != null) { + for (final String f : features) { + System.out.println("FEAT " + remote + "=" + f + "."); + } + } else if (FTPReply.isPositiveCompletion(ftp.getReplyCode())) { + System.out.println("FEAT " + remote + " is not present"); + } else { + System.out.println("Command failed: " + ftp.getReplyString()); + } + } else if (ftp.features()) { +// Command listener has already printed the output + } else { + System.out.println("Failed: " + ftp.getReplyString()); + } + } else if (doCommand != null) { + if (ftp.doCommand(doCommand, remote)) { +// Command listener has already printed the output +// for(String s : ftp.getReplyStrings()) { +// System.out.println(s); +// } + } else { + System.out.println("Failed: " + ftp.getReplyString()); + } + } else { + try (final OutputStream output = new FileOutputStream(local)) { + ftp.retrieveFile(remote, output); + } + + if (keepAliveTimeoutSeconds > 0) { + showCslStats(ftp); + } + } + + ftp.noop(); // check that control connection is working OK + + ftp.logout(); + } catch (final FTPConnectionClosedException e) { + error = true; + System.err.println("Server closed connection."); + e.printStackTrace(); + } catch (final IOException e) { + error = true; + e.printStackTrace(); + } finally { + if (ftp.isConnected()) { + try { + ftp.disconnect(); + } catch (final IOException f) { + // do nothing + } + } + } + + System.exit(error ? 1 : 0); + } // end main + + private static void showCslStats(final FTPClient ftp) { +// @SuppressWarnings("deprecation") // debug code +// final int[] stats = ftp.getCslDebug(); +// System.out.println("CslDebug=" + Arrays.toString(stats)); + + } +} diff --git a/network/src/main/java/com/github/kuangcp/app/ftp/FtpClient.java b/network/src/main/java/com/github/kuangcp/app/ftp/FtpClient.java new file mode 100644 index 00000000..a4534c4d --- /dev/null +++ b/network/src/main/java/com/github/kuangcp/app/ftp/FtpClient.java @@ -0,0 +1,74 @@ +package com.github.kuangcp.app.ftp; + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.net.PrintCommandListener; +import org.apache.commons.net.ftp.FTPClient; +import org.apache.commons.net.ftp.FTPFile; +import org.apache.commons.net.ftp.FTPReply; + +import java.io.*; +import java.nio.file.Files; +import java.util.Arrays; +import java.util.Collection; +import java.util.stream.Collectors; + +/** + * https://www.baeldung.com/java-ftp-client + * + * 2023-12-19 14:18 + */ +@Slf4j +public class FtpClient { + + private final String server; + private final int port; + private final String user; + private final String password; + private FTPClient ftp; + + public FtpClient(String server, int port, String user, String password) { + this.server = server; + this.port = port; + this.user = user; + this.password = password; + } + + public void open() throws IOException { + ftp = new FTPClient(); + + ftp.addProtocolCommandListener(new PrintCommandListener(new PrintWriter(System.out))); + + ftp.connect(server, port); + int reply = ftp.getReplyCode(); + if (!FTPReply.isPositiveCompletion(reply)) { + ftp.disconnect(); + throw new IOException("Exception in connecting to FTP Server"); + } + + boolean login = ftp.login(user, password); + log.info("login={}", login); + } + + public void close() throws IOException { + ftp.disconnect(); + } + + public Collection listFiles(String path) throws IOException { + // 如果本地启动了ftp服务,需要设置,否则会报425 https://stackoverflow.com/questions/72452185/java-ftp-client-failing-with-425-failed-to-establish-connection + ftp.enterLocalPassiveMode(); + + FTPFile[] files = ftp.listFiles(path); + return Arrays.stream(files) + .map(FTPFile::getName) + .collect(Collectors.toList()); + } + + public void downloadFile(String source, String destination) throws IOException { + FileOutputStream out = new FileOutputStream(destination); + ftp.retrieveFile(source, out); + } + + void putFileToPath(File file, String path) throws IOException { + ftp.storeFile(path, Files.newInputStream(file.toPath())); + } +} diff --git a/network/src/test/java/com/github/kuangcp/app/ftp/FtpClientMockTest.java b/network/src/test/java/com/github/kuangcp/app/ftp/FtpClientMockTest.java new file mode 100644 index 00000000..7e20a11b --- /dev/null +++ b/network/src/test/java/com/github/kuangcp/app/ftp/FtpClientMockTest.java @@ -0,0 +1,58 @@ +package com.github.kuangcp.app.ftp; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mockftpserver.fake.FakeFtpServer; +import org.mockftpserver.fake.UserAccount; +import org.mockftpserver.fake.filesystem.DirectoryEntry; +import org.mockftpserver.fake.filesystem.FileEntry; +import org.mockftpserver.fake.filesystem.FileSystem; +import org.mockftpserver.fake.filesystem.UnixFakeFileSystem; + +import java.io.IOException; +import java.util.Collection; + +/** + * + * @author kuangchengping@sinohealth.cn + * 2023-12-19 14:41 + */ +public class FtpClientMockTest { + private FakeFtpServer fakeFtpServer; + + private FtpClient ftpClient; + + @Before + public void setup() throws IOException { + fakeFtpServer = new FakeFtpServer(); + fakeFtpServer.addUserAccount(new UserAccount("user", "password", "/data")); + + FileSystem fileSystem = new UnixFakeFileSystem(); + fileSystem.add(new DirectoryEntry("/data")); + fileSystem.add(new FileEntry("/data/foobar.txt", "abcdef 1234567890")); + fakeFtpServer.setFileSystem(fileSystem); + fakeFtpServer.setServerControlPort(0); + + fakeFtpServer.start(); + + ftpClient = new FtpClient("localhost", fakeFtpServer.getServerControlPort(), "user", "password"); + ftpClient.open(); + } + + @After + public void teardown() throws IOException { + ftpClient.close(); + fakeFtpServer.stop(); + } + + + @Test + public void testList() throws IOException { + Collection files = ftpClient.listFiles(""); +// assertThat(files).contains("foobar.txt"); + for (String file : files) { + System.out.println(file); + } + } +} diff --git a/network/src/test/java/com/github/kuangcp/app/ftp/FtpClientTest.java b/network/src/test/java/com/github/kuangcp/app/ftp/FtpClientTest.java new file mode 100644 index 00000000..cf86e201 --- /dev/null +++ b/network/src/test/java/com/github/kuangcp/app/ftp/FtpClientTest.java @@ -0,0 +1,40 @@ +package com.github.kuangcp.app.ftp; + +import lombok.extern.slf4j.Slf4j; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.util.Collection; + +/** + * + * @author kuangchengping@sinohealth.cn + * 2023-12-19 14:31 + */ +@Slf4j +public class FtpClientTest { + + private FtpClient ftpClient; + + @Before + public void setup() throws IOException { + ftpClient = new FtpClient("localhost", 2121, "test", "test"); + ftpClient.open(); + } + + @After + public void teardown() throws IOException { + ftpClient.close(); + } + + @Test + public void testList() throws IOException { + Collection files = ftpClient.listFiles("/ftp"); +// assertThat(files).contains("foobar.txt"); + for (String file : files) { + log.info(file); + } + } +} diff --git a/network/src/test/java/com/github/kuangcp/io/PropertiesTest.java b/network/src/test/java/com/github/kuangcp/io/PropertiesTest.java index 3757b08e..33f58990 100644 --- a/network/src/test/java/com/github/kuangcp/io/PropertiesTest.java +++ b/network/src/test/java/com/github/kuangcp/io/PropertiesTest.java @@ -24,6 +24,7 @@ public void testRead() throws IOException { String b = properties.getProperty("B"); String decodedB = new String(b.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8); + log.info("a={} b=[{}] [{}]", a, b, decodedB); } } From 71cc75f2d61c50fe28c0b6d429c368763770d2d8 Mon Sep 17 00:00:00 2001 From: kuangcp Date: Wed, 20 Dec 2023 09:55:04 +0800 Subject: [PATCH 319/476] recursive search --- .../com/github/kuangcp/app/ftp/FtpClient.java | 13 ++++ .../github/kuangcp/app/ftp/FtpClientTest.java | 69 +++++++++++++++++++ 2 files changed, 82 insertions(+) diff --git a/network/src/main/java/com/github/kuangcp/app/ftp/FtpClient.java b/network/src/main/java/com/github/kuangcp/app/ftp/FtpClient.java index a4534c4d..51aa6f04 100644 --- a/network/src/main/java/com/github/kuangcp/app/ftp/FtpClient.java +++ b/network/src/main/java/com/github/kuangcp/app/ftp/FtpClient.java @@ -4,6 +4,7 @@ import org.apache.commons.net.PrintCommandListener; import org.apache.commons.net.ftp.FTPClient; import org.apache.commons.net.ftp.FTPFile; +import org.apache.commons.net.ftp.FTPFileFilter; import org.apache.commons.net.ftp.FTPReply; import java.io.*; @@ -33,6 +34,11 @@ public FtpClient(String server, int port, String user, String password) { this.password = password; } + + public void enterLocalPassiveMode(){ + ftp.enterLocalPassiveMode(); + } + public void open() throws IOException { ftp = new FTPClient(); @@ -63,6 +69,13 @@ public Collection listFiles(String path) throws IOException { .collect(Collectors.toList()); } + public Collection listFiles(String path, final FTPFileFilter filter) throws IOException { + FTPFile[] files = ftp.listFiles(path, filter); + return Arrays.stream(files) + .map(FTPFile::getName) + .collect(Collectors.toList()); + } + public void downloadFile(String source, String destination) throws IOException { FileOutputStream out = new FileOutputStream(destination); ftp.retrieveFile(source, out); diff --git a/network/src/test/java/com/github/kuangcp/app/ftp/FtpClientTest.java b/network/src/test/java/com/github/kuangcp/app/ftp/FtpClientTest.java index cf86e201..adf04c92 100644 --- a/network/src/test/java/com/github/kuangcp/app/ftp/FtpClientTest.java +++ b/network/src/test/java/com/github/kuangcp/app/ftp/FtpClientTest.java @@ -1,12 +1,19 @@ package com.github.kuangcp.app.ftp; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.net.ftp.FTPFile; +import org.apache.commons.net.ftp.FTPFileFilter; import org.junit.After; import org.junit.Before; import org.junit.Test; import java.io.IOException; +import java.util.ArrayList; import java.util.Collection; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.atomic.AtomicInteger; /** * @@ -37,4 +44,66 @@ public void testList() throws IOException { log.info(file); } } + + @Test + public void testSearch() throws Exception { + String key = "java"; + FTPFileFilter filter = a -> { + if (Objects.equals(FTPFile.DIRECTORY_TYPE, a.getType())) { + System.out.println(a.getName()); + } + return StringUtils.containsIgnoreCase(a.getName(), key); + }; + ftpClient.enterLocalPassiveMode(); + + Collection files = ftpClient.listFiles("/ftp", filter); + for (String file : files) { + log.info(file); + } + } + + + private AtomicInteger c = new AtomicInteger(); + @Test + public void testSearchRecursive() throws Exception { + ftpClient.enterLocalPassiveMode(); + List files = this.search("/ftp", "java"); + for (String file : files) { +// log.info(file); + } + System.out.println(c.get()); + } + + public List search(String path, String key) throws IOException { + List result = new ArrayList<>(); + + + log.info("path={}", path); + c.incrementAndGet(); + FTPFileFilter filter = a -> { + if (Objects.equals(FTPFile.DIRECTORY_TYPE, a.getType())) { + System.out.println(a.getName()); + try { + String dir = path + "/" + a.getName(); + List next = this.search(dir, key); + if (!next.isEmpty()) { + for (String f : next) { + if (f.contains("/")) { + result.add(f); + } else { + result.add(path + "/" + f); + } + } + } + } catch (IOException e) { + log.error("", e); + } + } + return StringUtils.containsIgnoreCase(a.getName(), key); + }; + Collection files = ftpClient.listFiles(path, filter); + result.addAll(files); + + return result; + } } From f6384f178bffbc5113cf78f6841df72a9dce6a02 Mon Sep 17 00:00:00 2001 From: kuangcp Date: Fri, 12 Jan 2024 18:03:04 +0800 Subject: [PATCH 320/476] fmt --- .../github/kuangcp/list/CopyOnWriteDemo.java | 41 +++++++++---------- 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/concurrency/src/main/java/com/github/kuangcp/list/CopyOnWriteDemo.java b/concurrency/src/main/java/com/github/kuangcp/list/CopyOnWriteDemo.java index 395fdc78..42f497be 100644 --- a/concurrency/src/main/java/com/github/kuangcp/list/CopyOnWriteDemo.java +++ b/concurrency/src/main/java/com/github/kuangcp/list/CopyOnWriteDemo.java @@ -1,10 +1,7 @@ package com.github.kuangcp.list; -import java.util.Iterator; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.locks.ReentrantLock; -import lombok.AllArgsConstructor; -import lombok.Data; /** * Created by https://github.com/kuangcp on 17-8-15 上午9:41 @@ -46,27 +43,27 @@ */ public class CopyOnWriteDemo { - public static void main(String[] s) { - CopyOnWriteArrayList elements = new CopyOnWriteArrayList<>(); - ReentrantLock lock = new ReentrantLock(); - ElementList list = new ElementList<>(elements, lock, "list > "); + public static void main(String[] s) { + CopyOnWriteArrayList elements = new CopyOnWriteArrayList<>(); + ReentrantLock lock = new ReentrantLock(); + ElementList list = new ElementList<>(elements, lock, "list > "); - // 两个独立的线程分别加锁并得到了副本,所以运行得到的结果是不同的 - new Thread(() -> { - list.addElement(new Element("1")); - list.addElement(new Element("2")); - list.addElement(new Element("3")); - list.addElement(new Element("4")); - list.prep(); - list.listElement("th1 : "); - }).start(); + // 两个独立的线程分别加锁并得到了副本,所以运行得到的结果是不同的 + new Thread(() -> { + list.addElement(new Element("1")); + list.addElement(new Element("2")); + list.addElement(new Element("3")); + list.addElement(new Element("4")); + list.prep(); + list.listElement("th1 : "); + }).start(); - new Thread(() -> { - list.addElement(new Element("5")); - list.prep(); - list.listElement("th2 : "); - }).start(); + new Thread(() -> { + list.addElement(new Element("5")); + list.prep(); + list.listElement("th2 : "); + }).start(); - } + } } From 55625d27f119c5d91267ede5af778abde36f2acd Mon Sep 17 00:00:00 2001 From: kuangcp Date: Wed, 24 Jan 2024 18:29:59 +0800 Subject: [PATCH 321/476] demo --- .../kuangcp/latch/CountDownLatchTest.java | 26 ++++++++++++++++--- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/concurrency/src/test/java/com/github/kuangcp/latch/CountDownLatchTest.java b/concurrency/src/test/java/com/github/kuangcp/latch/CountDownLatchTest.java index 1d81ec79..027d24bd 100644 --- a/concurrency/src/test/java/com/github/kuangcp/latch/CountDownLatchTest.java +++ b/concurrency/src/test/java/com/github/kuangcp/latch/CountDownLatchTest.java @@ -3,10 +3,7 @@ import lombok.extern.slf4j.Slf4j; import org.junit.Test; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; +import java.util.concurrent.*; /** * @author https://github.com/kuangcp on 2021-09-04 23:02 @@ -34,4 +31,25 @@ public void testFirstUse() throws Exception { latch.await(3, TimeUnit.SECONDS); } + + @Test + public void testSemaphore() throws Exception { + final ExecutorService pool = Executors.newFixedThreadPool(10); + Semaphore semaphore = new Semaphore(3, true); + for (int i = 0; i < 30; i++) { + pool.execute(() -> { + try { + semaphore.acquire(); + TimeUnit.SECONDS.sleep(1); + } catch (Exception e) { + log.error("", e); + } finally { + semaphore.release(); + } + log.info("run"); + }); + } + + Thread.currentThread().join(); + } } From fb47c6f1280b649c2e13c59ae6b6b0e437b0b5ec Mon Sep 17 00:00:00 2001 From: kuangcp Date: Wed, 31 Jan 2024 17:50:07 +0800 Subject: [PATCH 322/476] this escape --- .../kuangcp/volatiles/NeverStopThread.java | 79 ++++++++++--------- .../github/kuangcp/volatiles/ThisEscape.java | 36 +++++++++ .../kuangcp/latch/CountDownLatchTest.java | 3 + .../volatiles/NeverStopThreadTest.java | 4 +- .../kuangcp/volatiles/ThisEscapeTest.java | 28 +++++++ .../com/github/kuangcp/app/ftp/FtpClient.java | 2 +- .../kuangcp/app/ftp/FtpClientMockTest.java | 2 +- .../github/kuangcp/app/ftp/FtpClientTest.java | 2 +- 8 files changed, 112 insertions(+), 44 deletions(-) create mode 100644 concurrency/src/main/java/com/github/kuangcp/volatiles/ThisEscape.java create mode 100644 concurrency/src/test/java/com/github/kuangcp/volatiles/ThisEscapeTest.java diff --git a/concurrency/src/main/java/com/github/kuangcp/volatiles/NeverStopThread.java b/concurrency/src/main/java/com/github/kuangcp/volatiles/NeverStopThread.java index e706ce62..dbb4af98 100644 --- a/concurrency/src/main/java/com/github/kuangcp/volatiles/NeverStopThread.java +++ b/concurrency/src/main/java/com/github/kuangcp/volatiles/NeverStopThread.java @@ -1,60 +1,61 @@ package com.github.kuangcp.volatiles; -import java.util.concurrent.TimeUnit; import lombok.extern.slf4j.Slf4j; +import java.util.concurrent.TimeUnit; + /** * @author kuangcp on 2019-04-24 9:02 PM */ @Slf4j class NeverStopThread { - private boolean stop = false; + private boolean stop = false; - private boolean stopWithSleep = false; - private volatile boolean stopWithVolatile = false; + private boolean stopWithSleep = false; + private volatile boolean stopWithVolatile = false; - void neverStop() { - while (!stop) { - } + void neverStop() { + while (!stop) { + } - log.info("exit neverStop"); - } - - void stop() { - this.stop = true; - } - - // JVM 会尽力保证内存的可见性,即便这个变量没有加同步关键字。换句话说,只要 CPU 有时间,JVM 会尽力去保证变量值的更新。 - // 这种与 volatile 关键字的不同在于,volatile 关键字会强制的保证线程的可见性。 - // 使用 System.out.println(); 或者其他语句都是有可能达到效果, 只要能让CPU空闲下来 - void normalStopWithSleep() { - while (!stopWithSleep) { - try { - TimeUnit.MILLISECONDS.sleep(300); - } catch (InterruptedException e) { - log.error("", e); - } - log.info("run with sleep"); + log.info("exit neverStop"); } - log.info("exit normalStopWithSleep"); - } + void stop() { + this.stop = true; + } - void stopWithSleep() { - this.stopWithSleep = true; - } + // JVM 会尽力保证内存的可见性,即便这个变量没有加同步关键字。换句话说,只要 CPU 有时间,JVM 会尽力去保证变量值的更新。 + // 这种与 volatile 关键字的不同在于,volatile 关键字会强制的保证线程的可见性。 + // 使用 System.out.println(); 或者其他语句都是有可能达到效果, 只要能让CPU空闲下来 + void normalStopWithSleep() { + while (!stopWithSleep) { + try { + TimeUnit.MILLISECONDS.sleep(300); + } catch (InterruptedException e) { + log.error("", e); + } + log.info("run with sleep"); + } + + log.info("exit normalStopWithSleep"); + } - // 由于CPU调度的不可控性, 所以并不会在 stop 执行后就立马停掉 - void normalStopWithVolatile() { - while (!stopWithVolatile) { + void stopWithSleep() { + this.stopWithSleep = true; } - log.info("exit normalStopWithVolatile"); - } + // 由于CPU调度的不可控性, 所以并不会在 stop 执行后就立马停掉 + void normalStopWithVolatile() { + while (!stopWithVolatile) { + } + + log.info("exit normalStopWithVolatile"); + } - // 因为这个关键字保证了可见性, 所以能使得线程停下来 - void stopWithVolatile() { - this.stopWithVolatile = true; - } + // 因为这个关键字保证了可见性, 所以能使得线程停下来 + void stopWithVolatile() { + this.stopWithVolatile = true; + } } diff --git a/concurrency/src/main/java/com/github/kuangcp/volatiles/ThisEscape.java b/concurrency/src/main/java/com/github/kuangcp/volatiles/ThisEscape.java new file mode 100644 index 00000000..a3b47dbb --- /dev/null +++ b/concurrency/src/main/java/com/github/kuangcp/volatiles/ThisEscape.java @@ -0,0 +1,36 @@ +package com.github.kuangcp.volatiles; + +/** + * https://www.cnblogs.com/jian0110/p/9369096.html + * @author kuangcp + * 2024-01-31 17:31 + */ +public class ThisEscape { + + final int i; + int j; + + public ThisEscape() { + i = 1; + j = 1; + new Thread(new RunnableTest()).start(); + } + + // 内部类实现Runnable:引用外部类 + private class RunnableTest implements Runnable { + @Override + public void run() { + try { + System.out.print(ThisEscape.this.j); + } catch (NullPointerException e) { + System.out.println("发生空指针错误:普通变量j未被初始化"); + } + try { + System.out.print(ThisEscape.this.i); + } catch (NullPointerException e) { + System.out.println("发生空指针错误:final变量i未被初始化"); + } + ThisEscape.this.j += 1; + } + } +} \ No newline at end of file diff --git a/concurrency/src/test/java/com/github/kuangcp/latch/CountDownLatchTest.java b/concurrency/src/test/java/com/github/kuangcp/latch/CountDownLatchTest.java index 027d24bd..745068d8 100644 --- a/concurrency/src/test/java/com/github/kuangcp/latch/CountDownLatchTest.java +++ b/concurrency/src/test/java/com/github/kuangcp/latch/CountDownLatchTest.java @@ -49,6 +49,9 @@ public void testSemaphore() throws Exception { log.info("run"); }); } + Executors.newSingleThreadExecutor().execute(() -> { + + }); Thread.currentThread().join(); } diff --git a/concurrency/src/test/java/com/github/kuangcp/volatiles/NeverStopThreadTest.java b/concurrency/src/test/java/com/github/kuangcp/volatiles/NeverStopThreadTest.java index fbda9855..d71a4370 100644 --- a/concurrency/src/test/java/com/github/kuangcp/volatiles/NeverStopThreadTest.java +++ b/concurrency/src/test/java/com/github/kuangcp/volatiles/NeverStopThreadTest.java @@ -19,8 +19,8 @@ public void testNeverStop() throws Exception { log.info("prepare to stop"); demo.stop(); - thread.join(); - // 无法停止的原因是 JMM的原因 值在自己的缓存里, 所以这里改动了 stop 但是 thread 里的 stop 没有更新 + thread.join(5000); + // 无法立即停止的原因是 JMM的原因 值在自己的缓存里, 所以这里改动了 stop 但是 thread 里的 stop 没有更新 } @Test diff --git a/concurrency/src/test/java/com/github/kuangcp/volatiles/ThisEscapeTest.java b/concurrency/src/test/java/com/github/kuangcp/volatiles/ThisEscapeTest.java new file mode 100644 index 00000000..543ef3e1 --- /dev/null +++ b/concurrency/src/test/java/com/github/kuangcp/volatiles/ThisEscapeTest.java @@ -0,0 +1,28 @@ +package com.github.kuangcp.volatiles; + +import org.junit.Test; + +import java.util.concurrent.TimeUnit; + +/** + * + * @author kuangcp + * 2024-01-31 17:40 + */ +public class ThisEscapeTest { + + @Test + public void testOnce() throws Exception { + ThisEscape es = new ThisEscape(); + System.out.println(es.i + " " + es.j); + TimeUnit.SECONDS.sleep(1); + System.out.println(es.i + " " + es.j); + } + + @Test + public void testMain() throws Exception { + for (int i = 0; i < 90000; i++) { + new ThisEscape(); + } + } +} diff --git a/network/src/main/java/com/github/kuangcp/app/ftp/FtpClient.java b/network/src/main/java/com/github/kuangcp/app/ftp/FtpClient.java index 51aa6f04..c69c195c 100644 --- a/network/src/main/java/com/github/kuangcp/app/ftp/FtpClient.java +++ b/network/src/main/java/com/github/kuangcp/app/ftp/FtpClient.java @@ -35,7 +35,7 @@ public FtpClient(String server, int port, String user, String password) { } - public void enterLocalPassiveMode(){ + public void enterLocalPassiveMode() { ftp.enterLocalPassiveMode(); } diff --git a/network/src/test/java/com/github/kuangcp/app/ftp/FtpClientMockTest.java b/network/src/test/java/com/github/kuangcp/app/ftp/FtpClientMockTest.java index 7e20a11b..5db88ed9 100644 --- a/network/src/test/java/com/github/kuangcp/app/ftp/FtpClientMockTest.java +++ b/network/src/test/java/com/github/kuangcp/app/ftp/FtpClientMockTest.java @@ -15,7 +15,7 @@ /** * - * @author kuangchengping@sinohealth.cn + * @author kuangcp * 2023-12-19 14:41 */ public class FtpClientMockTest { diff --git a/network/src/test/java/com/github/kuangcp/app/ftp/FtpClientTest.java b/network/src/test/java/com/github/kuangcp/app/ftp/FtpClientTest.java index adf04c92..a8a5ebad 100644 --- a/network/src/test/java/com/github/kuangcp/app/ftp/FtpClientTest.java +++ b/network/src/test/java/com/github/kuangcp/app/ftp/FtpClientTest.java @@ -17,7 +17,7 @@ /** * - * @author kuangchengping@sinohealth.cn + * @author kuangcp * 2023-12-19 14:31 */ @Slf4j From d82295a0abc6a265071da5278133a5efe8ac9058 Mon Sep 17 00:00:00 2001 From: kuangcp Date: Fri, 2 Feb 2024 18:21:59 +0800 Subject: [PATCH 323/476] DirectMemory OOM --- .../main/java/jvm/oom/DirectMemoryOOM.java | 62 +++-- .../kuangcp/generic/inherit/InheritTest.java | 64 ++--- .../kuangcp/generic/simple/PairTest.java | 256 +++++++++--------- .../java/jvm/oom/DirectMemoryOOMTest.java | 20 +- netty/pom.xml | 10 +- .../netty/core/future/ChannelFutureTest.java | 42 +++ .../java/netty/timeServer/TimeClientTest.java | 6 +- .../java/netty/timeServer/TimeServerTest.java | 12 +- .../github/kuangcp/app/ftp/FtpClientTest.java | 4 +- 9 files changed, 268 insertions(+), 208 deletions(-) create mode 100644 netty/src/test/java/netty/core/future/ChannelFutureTest.java diff --git a/class/src/main/java/jvm/oom/DirectMemoryOOM.java b/class/src/main/java/jvm/oom/DirectMemoryOOM.java index cb515c5a..3e032570 100644 --- a/class/src/main/java/jvm/oom/DirectMemoryOOM.java +++ b/class/src/main/java/jvm/oom/DirectMemoryOOM.java @@ -1,8 +1,11 @@ package jvm.oom; +import sun.misc.Unsafe; + import java.lang.reflect.Field; import java.nio.ByteBuffer; -import sun.misc.Unsafe; +import java.util.ArrayList; +import java.util.List; /** * -XX:NativeMemoryTracking=detail @@ -19,33 +22,42 @@ */ public class DirectMemoryOOM { - private static final long memoryBlock = 1024 * 1024L; + private static final int mib = 1024 * 1024; - // -XX:MaxDirectMemorySize参数只对由DirectByteBuffer分配的内存有效,对Unsafe直接分配的内存无效 + // 注意: -XX:MaxDirectMemorySize参数只对由DirectByteBuffer分配的内存有效,对Unsafe直接分配的内存无效 - /** - * TODO 为什么 这里分配的是虚拟内存 - */ - static void byUnsafe() throws IllegalAccessException, InterruptedException { - Field field = Unsafe.class.getDeclaredFields()[0]; - field.setAccessible(true); + /** + * TODO 为什么 这里分配的是虚拟内存 + * + * C语言malloc申请的也是虚拟内存 + */ + static void byUnsafe() throws IllegalAccessException, InterruptedException { + Field field = Unsafe.class.getDeclaredFields()[0]; + field.setAccessible(true); - Unsafe unsafe = (Unsafe) field.get(null); - while (true) { - Thread.sleep(50); - unsafe.allocateMemory(memoryBlock); + int delta = 0; + Unsafe unsafe = (Unsafe) field.get(null); + while (true) { + Thread.sleep(100); + delta += 2; + System.out.println("now " + delta); + // byte + unsafe.allocateMemory(2 * mib); + } } - } - - /** - * TODO 为什么会进行回收 -XX:MaxDirectMemorySize - */ - static void byBuffer() throws InterruptedException { - while (true) { - Thread.sleep(10); - ByteBuffer buf = ByteBuffer.allocateDirect(1024 * 1024); - buf.putLong(2L); - buf.flip(); + + static void byBuffer() throws InterruptedException { + int delta = 0; + List buffers = new ArrayList<>(); + while (true) { + Thread.sleep(100); + delta += 2; + System.out.println("now " + delta); + // byte + ByteBuffer buf = ByteBuffer.allocateDirect(2 * mib); + buf.putLong(2L); + buf.flip(); + buffers.add(buf); + } } - } } diff --git a/class/src/test/java/com/github/kuangcp/generic/inherit/InheritTest.java b/class/src/test/java/com/github/kuangcp/generic/inherit/InheritTest.java index 2c33552c..b92e643e 100644 --- a/class/src/test/java/com/github/kuangcp/generic/inherit/InheritTest.java +++ b/class/src/test/java/com/github/kuangcp/generic/inherit/InheritTest.java @@ -1,7 +1,7 @@ package com.github.kuangcp.generic.inherit; -import com.github.kuangcp.common.Human; -import com.github.kuangcp.common.Student; +import com.github.kuangcp.generic.common.Human; +import com.github.kuangcp.generic.common.Student; import org.junit.Test; import java.util.Arrays; @@ -12,45 +12,45 @@ */ public class InheritTest { - // test Arrays.asList() method - @Test - public void testArray() { - // Type T on method signature is not unique, both String and Object satisfy generic rule - // generic rule is only exist at compile time - // but when generics are explicitly specified, the T will be identified + // test Arrays.asList() method + @Test + public void testArray() { + // Type T on method signature is not unique, both String and Object satisfy generic rule + // generic rule is only exist at compile time + // but when generics are explicitly specified, the T will be identified - List objectList = Arrays.asList("1", "2"); - List strings = Arrays.asList("1", "2"); + List objectList = Arrays.asList("1", "2"); + List strings = Arrays.asList("1", "2"); // x List objects = strings; - // 可以放任意值 返回值为原始类型, 但是没有警告?? - List data = Arrays.asList(1, "2", 'd', 1.0, new Human()); - } + // 可以放任意值 返回值为原始类型, 但是没有警告?? + List data = Arrays.asList(1, "2", 'd', 1.0, new Human()); + } - @Test - public void testAdd() { - Container humanContainer = new Container<>(); - humanContainer.add(new Human()); - humanContainer.add(new Student()); + @Test + public void testAdd() { + Container humanContainer = new Container<>(); + humanContainer.add(new Human()); + humanContainer.add(new Student()); - Container studentContainer = new Container<>(); + Container studentContainer = new Container<>(); // x studentContainer.add(new Human()); - studentContainer.add(new Student()); + studentContainer.add(new Student()); // x Container temp = humanContainer; // x Container temp = studentContainer; - } + } - @Test - public void testInit() { - Container a = Container.init(new Student()); + @Test + public void testInit() { + Container a = Container.init(new Student()); // x Container temp = a; - System.out.println(a.get(0)); - - // TODO why? - Container b = Container.init(new Student()); - Container temp = b; - System.out.println(b.get(0)); - System.out.println(temp.get(0)); - } + System.out.println(a.get(0)); + + // TODO why? + Container b = Container.init(new Student()); + Container temp = b; + System.out.println(b.get(0)); + System.out.println(temp.get(0)); + } } \ No newline at end of file diff --git a/class/src/test/java/com/github/kuangcp/generic/simple/PairTest.java b/class/src/test/java/com/github/kuangcp/generic/simple/PairTest.java index 62be346b..2deed6d3 100644 --- a/class/src/test/java/com/github/kuangcp/generic/simple/PairTest.java +++ b/class/src/test/java/com/github/kuangcp/generic/simple/PairTest.java @@ -1,8 +1,8 @@ package com.github.kuangcp.generic.simple; -import com.github.kuangcp.common.Human; -import com.github.kuangcp.common.Junior; -import com.github.kuangcp.common.Student; +import com.github.kuangcp.generic.common.Human; +import com.github.kuangcp.generic.common.Junior; +import com.github.kuangcp.generic.common.Student; import lombok.extern.slf4j.Slf4j; import org.junit.Test; @@ -17,154 +17,154 @@ @Slf4j public class PairTest { - // Junior -> Student -> Human - @Test - public void testBasicGeneric() { - Pair pair = new Pair<>(); - pair.setFirst(new Date()); - System.out.println(pair.getFirst()); + // Junior -> Student -> Human + @Test + public void testBasicGeneric() { + Pair pair = new Pair<>(); + pair.setFirst(new Date()); + System.out.println(pair.getFirst()); - // 编译期 泛型类型检查, Pair的泛型类型已经声明为Date, 所以以下编译通不过 + // 编译期 泛型类型检查, Pair的泛型类型已经声明为Date, 所以以下编译通不过 // x pair.setSecond(new Integer(3)); - } - - @Test - public void testUsePair() { - Float[] arrays = {2.1f, 4.2f, 3.5f, 5.5f, 2.11f}; - Pair pair = Pair.minAndMax(arrays); - log.info("min={} max={}", pair.getFirst(), pair.getSecond()); - - String[] data = {"ddd", "d", "aa"}; - log.info("result={}", Pair.minAndMax(data)); - log.info("result={}", Pair.middle(data)); - } - - /** - * 使用 ? extends XXX 声明的泛型容器 只能get 不能set(编译通不过) - * 且 适用于 方法的参数, 不适用于返回值 - * - * 因此如果你想从一个数据结构里获取数据,使用 ? extends 通配符 限定通配符总是包括自己 - */ - @Test - public void testExtends() { - Pair humans = new Pair<>(); - // Human 自身 以及 他的子类都能放入 - humans.setFirst(new Junior()); - humans.setFirst(new Human()); - humans.setSecond(new Student()); + } + + @Test + public void testUsePair() { + Float[] arrays = {2.1f, 4.2f, 3.5f, 5.5f, 2.11f}; + Pair pair = Pair.minAndMax(arrays); + log.info("min={} max={}", pair.getFirst(), pair.getSecond()); + + String[] data = {"ddd", "d", "aa"}; + log.info("result={}", Pair.minAndMax(data)); + log.info("result={}", Pair.middle(data)); + } + + /** + * 使用 ? extends XXX 声明的泛型容器 只能get 不能set(编译通不过) + * 且 适用于 方法的参数, 不适用于返回值 + * + * 因此如果你想从一个数据结构里获取数据,使用 ? extends 通配符 限定通配符总是包括自己 + */ + @Test + public void testExtends() { + Pair humans = new Pair<>(); + // Human 自身 以及 他的子类都能放入 + humans.setFirst(new Junior()); + humans.setFirst(new Human()); + humans.setSecond(new Student()); // x humans.setSecond(new Object()); - paramWithSuper(humans); + paramWithSuper(humans); - // Student 自身 以及 他的子类都能放入 - Pair classmates = new Pair<>(); - paramWithExtends(classmates); + // Student 自身 以及 他的子类都能放入 + Pair classmates = new Pair<>(); + paramWithExtends(classmates); - Pair result = returnWithExtends(); - log.info("result={}", result); - } + Pair result = returnWithExtends(); + log.info("result={}", result); + } - /** - * @param student ? extends Human的子类 都是能放入的 - */ - private void paramWithExtends(Pair student) { + /** + * @param student ? extends Human的子类 都是能放入的 + */ + private void paramWithExtends(Pair student) { // student.setFirst(new Human()); // student.setSecond(new Student()); // student.setFirst(new Junior()); // student.setSecond(new Object()); - log.info("first={} second={}", student.getFirst(), student.getSecond()); - } + log.info("first={} second={}", student.getFirst(), student.getSecond()); + } - private Pair returnWithExtends() { - Pair studentPair = new Pair<>(); - studentPair.setFirst(new Student()); - studentPair.setSecond(new Junior()); + private Pair returnWithExtends() { + Pair studentPair = new Pair<>(); + studentPair.setFirst(new Student()); + studentPair.setSecond(new Junior()); // Pair humanPair = new Pair<>(); // return humanPair; - return studentPair; - } - - /** - * ? super xxx 只能set, 不能get(丢失了泛型) - * 因此如果你想把对象写入一个数据结构里,使用 ? super 通配符。限定通配符总是包括自己 - */ - @Test - public void testSuper() { - Pair human = new Pair<>(); - human.setFirst(new Human()); - human.setSecond(new Junior()); - paramWithSuper(human); - - Pair juniorPair = new Pair<>(); + return studentPair; + } + + /** + * ? super xxx 只能set, 不能get(丢失了泛型) + * 因此如果你想把对象写入一个数据结构里,使用 ? super 通配符。限定通配符总是包括自己 + */ + @Test + public void testSuper() { + Pair human = new Pair<>(); + human.setFirst(new Human()); + human.setSecond(new Junior()); + paramWithSuper(human); + + Pair juniorPair = new Pair<>(); // paramWithSuper(juniorPair); - Pair result = returnWithSuper(); - // 因为 result 是约束为能放入Student以及他的超类,但是又是不明确的超类, 所以这个result能放入 Student子类, 因为多态 - // 但是不能放入 Human 因为不明确result约束的超类到底是哪个, 即使Human是Student超类, 不能放入 - result.setSecond(new Junior()); + Pair result = returnWithSuper(); + // 因为 result 是约束为能放入Student以及他的超类,但是又是不明确的超类, 所以这个result能放入 Student子类, 因为多态 + // 但是不能放入 Human 因为不明确result约束的超类到底是哪个, 即使Human是Student超类, 不能放入 + result.setSecond(new Junior()); // result.setSecond(new Human()); - log.info("result={}", result); - } + log.info("result={}", result); + } - private void paramWithSuper(Pair student) { - student.setFirst(new Junior()); + private void paramWithSuper(Pair student) { + student.setFirst(new Junior()); // student.setFirst(new Human()); // student.setFirst(new Object()); - log.info("{}", student); - } + log.info("{}", student); + } - private Pair returnWithSuper() { - Pair humanPair = new Pair<>(); - humanPair.setFirst(new Human()); + private Pair returnWithSuper() { + Pair humanPair = new Pair<>(); + humanPair.setFirst(new Human()); - Pair juniorPair = new Pair<>(); - juniorPair.setFirst(new Junior()); + Pair juniorPair = new Pair<>(); + juniorPair.setFirst(new Junior()); // return juniorPair; - return humanPair; - } - - /** - * 原始类型的坑 - */ - @Test - public void testPrimitiveType() { - Pair classmates = new Pair<>(); - ((Pair) classmates).setFirst("str"); - log.info("{}", classmates.getFirst()); - - ((Pair) classmates).setFirst(new Human("name")); - - // 虽然通过了编译,也运行正常,但是该泛型程序没有实现其需要的目的(类型约束) - // 原本应该是Human对象才能set,但是变成原始类型即多态后就能set任意对象了 - log.info("{}", classmates.getFirst()); - } - - // 无限定通配符 - @Test - public void testHasNull() { - Pair humanPair = new Pair<>(); - humanPair.setSecond(new Human("fds")); - - log.info("result={}", hasNull(humanPair)); - log.info("result={}", hasNulls(humanPair)); - } - - /** - * 判断 Pair 是否有属性为null - * - * @param p 泛型变量约束的类, 不需要实际的类型 - */ - private boolean hasNull(Pair p) { - // get方法返回值只能返回给Object, set方法不能被调用, 甚至不能用Object调用 - return p.getFirst() == null || p.getSecond() == null; - } - - /** - * 在这个场景下 T 和 ? 使用效果是一致的, 但是 ? 不能使用 set, T 可以 - */ - private boolean hasNulls(Pair p) { - return p.getSecond() == null || p.getFirst() == null; - } + return humanPair; + } + + /** + * 原始类型的坑 + */ + @Test + public void testPrimitiveType() { + Pair classmates = new Pair<>(); + ((Pair) classmates).setFirst("str"); + log.info("{}", classmates.getFirst()); + + ((Pair) classmates).setFirst(new Human("name")); + + // 虽然通过了编译,也运行正常,但是该泛型程序没有实现其需要的目的(类型约束) + // 原本应该是Human对象才能set,但是变成原始类型即多态后就能set任意对象了 + log.info("{}", classmates.getFirst()); + } + + // 无限定通配符 + @Test + public void testHasNull() { + Pair humanPair = new Pair<>(); + humanPair.setSecond(new Human("fds")); + + log.info("result={}", hasNull(humanPair)); + log.info("result={}", hasNulls(humanPair)); + } + + /** + * 判断 Pair 是否有属性为null + * + * @param p 泛型变量约束的类, 不需要实际的类型 + */ + private boolean hasNull(Pair p) { + // get方法返回值只能返回给Object, set方法不能被调用, 甚至不能用Object调用 + return p.getFirst() == null || p.getSecond() == null; + } + + /** + * 在这个场景下 T 和 ? 使用效果是一致的, 但是 ? 不能使用 set, T 可以 + */ + private boolean hasNulls(Pair p) { + return p.getSecond() == null || p.getFirst() == null; + } } \ No newline at end of file diff --git a/class/src/test/java/jvm/oom/DirectMemoryOOMTest.java b/class/src/test/java/jvm/oom/DirectMemoryOOMTest.java index 0d5fe3c4..37555265 100644 --- a/class/src/test/java/jvm/oom/DirectMemoryOOMTest.java +++ b/class/src/test/java/jvm/oom/DirectMemoryOOMTest.java @@ -7,13 +7,17 @@ */ public class DirectMemoryOOMTest { - @Test - public void testUnsafe() throws InterruptedException, IllegalAccessException { - DirectMemoryOOM.byUnsafe(); - } + @Test + public void testUnsafe() throws InterruptedException, IllegalAccessException { + DirectMemoryOOM.byUnsafe(); + } - @Test - public void testBuffer() throws InterruptedException { - DirectMemoryOOM.byBuffer(); - } + /** + * 1. -XX:MaxDirectMemorySize=100m 分配到100M就OOM + * 2. -Xmx260m分配到250M OOM 计算方式: xmx-Survivor + */ + @Test + public void testBuffer() throws InterruptedException { + DirectMemoryOOM.byBuffer(); + } } \ No newline at end of file diff --git a/netty/pom.xml b/netty/pom.xml index 81ca378f..bc124016 100644 --- a/netty/pom.xml +++ b/netty/pom.xml @@ -24,11 +24,11 @@ netty-all - - org.testng - testng - test - + + + + + diff --git a/netty/src/test/java/netty/core/future/ChannelFutureTest.java b/netty/src/test/java/netty/core/future/ChannelFutureTest.java new file mode 100644 index 00000000..e8038a85 --- /dev/null +++ b/netty/src/test/java/netty/core/future/ChannelFutureTest.java @@ -0,0 +1,42 @@ +package netty.core.future; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.Channel; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelFutureListener; +import io.netty.channel.local.LocalServerChannel; +import org.junit.Test; + +import java.net.InetSocketAddress; +import java.nio.charset.Charset; + +/** + * + * @author kuangchengping@sinohealth.cn + * 2024-02-01 10:43 + */ +public class ChannelFutureTest { + + @Test + public void testTcpConn() throws Exception { + Channel channel = new LocalServerChannel(); + + // 1 连接到远程 非阻塞操作 + ChannelFuture future = channel.connect(new InetSocketAddress("192.168.0.1", 25)); + // 2 注册Listener + future.addListener((ChannelFutureListener) f -> { + if (f.isSuccess()) { + // 3. 连接成功做操作 + ByteBuf buffer = Unpooled.copiedBuffer("Hello", Charset.defaultCharset()); + ChannelFuture wf = f.channel().writeAndFlush(buffer); + // ... + } else { + // 3. 处理异常 + Throwable cause = f.cause(); + cause.printStackTrace(); + } + }); + } + +} \ No newline at end of file diff --git a/netty/src/test/java/netty/timeServer/TimeClientTest.java b/netty/src/test/java/netty/timeServer/TimeClientTest.java index 20bd4244..b14949d6 100644 --- a/netty/src/test/java/netty/timeServer/TimeClientTest.java +++ b/netty/src/test/java/netty/timeServer/TimeClientTest.java @@ -1,7 +1,7 @@ package netty.timeServer; import lombok.extern.slf4j.Slf4j; -import org.testng.annotations.Test; +import org.junit.Test; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; @@ -15,8 +15,8 @@ public class TimeClientTest { private final TimeClient timeClient = new TimeClient(); - // @Test - @Test(threadPoolSize = 5, invocationCount = 20) + // @Test(threadPoolSize = 5, invocationCount = 20) + @Test public void testClient() throws Exception { startClient(); timeClient.sendMsg(Command.QUERY_TIME); diff --git a/netty/src/test/java/netty/timeServer/TimeServerTest.java b/netty/src/test/java/netty/timeServer/TimeServerTest.java index 68fea0c4..f7751193 100644 --- a/netty/src/test/java/netty/timeServer/TimeServerTest.java +++ b/netty/src/test/java/netty/timeServer/TimeServerTest.java @@ -2,7 +2,7 @@ import lombok.extern.slf4j.Slf4j; -import org.testng.annotations.Test; +import org.junit.Test; /** * @author kuangcp on 2019-04-23 10:58 AM @@ -10,10 +10,10 @@ @Slf4j public class TimeServerTest { - private final TimeServer timeServer = new TimeServer(); + private final TimeServer timeServer = new TimeServer(); - @Test - public void testServer() throws Exception { - timeServer.start(); - } + @Test + public void testServer() throws Exception { + timeServer.start(); + } } diff --git a/network/src/test/java/com/github/kuangcp/app/ftp/FtpClientTest.java b/network/src/test/java/com/github/kuangcp/app/ftp/FtpClientTest.java index a8a5ebad..f561c3f9 100644 --- a/network/src/test/java/com/github/kuangcp/app/ftp/FtpClientTest.java +++ b/network/src/test/java/com/github/kuangcp/app/ftp/FtpClientTest.java @@ -27,7 +27,8 @@ public class FtpClientTest { @Before public void setup() throws IOException { - ftpClient = new FtpClient("localhost", 2121, "test", "test"); +// ftpClient = new FtpClient("localhost", 2121, "test", "test"); + ftpClient = new FtpClient("192.168.16.157", 21, "test_fetch", "sinohealth"); ftpClient.open(); } @@ -64,6 +65,7 @@ public void testSearch() throws Exception { private AtomicInteger c = new AtomicInteger(); + @Test public void testSearchRecursive() throws Exception { ftpClient.enterLocalPassiveMode(); From 241cf56cc3270db5314840990dd1033fff3e68d5 Mon Sep 17 00:00:00 2001 From: kuangcp Date: Sun, 4 Feb 2024 15:36:07 +0800 Subject: [PATCH 324/476] fmt --- .../main/java/jvm/oom/DirectMemoryOOM.java | 5 +- .../java/netty/timeServer/TimeClient.java | 84 ++++++------ .../netty/timeServer/TimeClientHandler.java | 120 +++++++++--------- .../java/netty/timeServer/TimeServer.java | 78 ++++++------ .../netty/timeServer/TimeServerHandler.java | 97 +++++++------- 5 files changed, 194 insertions(+), 190 deletions(-) diff --git a/class/src/main/java/jvm/oom/DirectMemoryOOM.java b/class/src/main/java/jvm/oom/DirectMemoryOOM.java index 3e032570..1210ad3d 100644 --- a/class/src/main/java/jvm/oom/DirectMemoryOOM.java +++ b/class/src/main/java/jvm/oom/DirectMemoryOOM.java @@ -27,9 +27,10 @@ public class DirectMemoryOOM { // 注意: -XX:MaxDirectMemorySize参数只对由DirectByteBuffer分配的内存有效,对Unsafe直接分配的内存无效 /** - * TODO 为什么 这里分配的是虚拟内存 + * C语言malloc申请的也是虚拟内存,没有设置值的话操作系统不会分配物理内存 + * @see java.nio.DirectByteBuffer#DirectByteBuffer(int) * - * C语言malloc申请的也是虚拟内存 + * https://tech.meituan.com/2019/02/14/talk-about-java-magic-class-unsafe.html */ static void byUnsafe() throws IllegalAccessException, InterruptedException { Field field = Unsafe.class.getDeclaredFields()[0]; diff --git a/netty/src/main/java/netty/timeServer/TimeClient.java b/netty/src/main/java/netty/timeServer/TimeClient.java index 4e0fd3d9..a5fa4ade 100644 --- a/netty/src/main/java/netty/timeServer/TimeClient.java +++ b/netty/src/main/java/netty/timeServer/TimeClient.java @@ -14,56 +14,56 @@ @Slf4j class TimeClient { - private final TimeClientHandler timeClientHandler = new TimeClientHandler(); - private Channel channel; + private final TimeClientHandler timeClientHandler = new TimeClientHandler(); + private Channel channel; - void connectLocal(int port) throws Exception { - connect(port, "127.0.0.1"); - } + void connectLocal(int port) throws Exception { + connect(port, "127.0.0.1"); + } - void connect(int port, String host) throws Exception { - // 配置客户端NIO线程组 - EventLoopGroup group = new NioEventLoopGroup(); + void connect(int port, String host) throws Exception { + // 配置客户端NIO线程组 + EventLoopGroup group = new NioEventLoopGroup(); - try { - Bootstrap bootstrap = new Bootstrap(); - bootstrap.group(group).channel(NioSocketChannel.class) - .option(ChannelOption.TCP_NODELAY, true) - .handler(new ChannelInitializer() { - @Override - public void initChannel(SocketChannel ch) { - ch.pipeline().addLast(timeClientHandler); - log.info("add new client handler"); - } - }); + try { + Bootstrap bootstrap = new Bootstrap(); + bootstrap.group(group).channel(NioSocketChannel.class) + .option(ChannelOption.TCP_NODELAY, true) + .handler(new ChannelInitializer() { + @Override + public void initChannel(SocketChannel ch) { + ch.pipeline().addLast(timeClientHandler); + log.info("add new client handler"); + } + }); - // 发起异步连接操作 - ChannelFuture channelFuture = bootstrap.connect(host, port).sync(); + // 发起异步连接操作 + ChannelFuture channelFuture = bootstrap.connect(host, port).sync(); - // 当前客户端链路关闭 - this.channel = channelFuture.channel(); - channel.closeFuture().sync(); - } finally { - // 优雅退出,释放NIO线程组 - group.shutdownGracefully(); + // 当前客户端链路关闭 + this.channel = channelFuture.channel(); + channel.closeFuture().sync(); + } finally { + // 优雅退出,释放NIO线程组 + group.shutdownGracefully(); + } } - } - void sendMsg(String msg) { - timeClientHandler.sendMsg(msg); - } + void sendMsg(String msg) { + timeClientHandler.sendMsg(msg); + } - void flush() { - this.timeClientHandler.getCtx().flush(); - } + void flush() { + this.timeClientHandler.getCtx().flush(); + } - void stop() { - this.timeClientHandler.getCtx().flush(); - this.timeClientHandler.close(); - this.channel.close(); - } + void stop() { + this.timeClientHandler.getCtx().flush(); + this.timeClientHandler.close(); + this.channel.close(); + } - boolean isReady() { - return timeClientHandler.isConnected(); - } + boolean isReady() { + return timeClientHandler.isConnected(); + } } diff --git a/netty/src/main/java/netty/timeServer/TimeClientHandler.java b/netty/src/main/java/netty/timeServer/TimeClientHandler.java index 824186be..45ce4d9a 100644 --- a/netty/src/main/java/netty/timeServer/TimeClientHandler.java +++ b/netty/src/main/java/netty/timeServer/TimeClientHandler.java @@ -5,9 +5,10 @@ import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.util.internal.StringUtil; +import lombok.extern.slf4j.Slf4j; + import java.nio.charset.StandardCharsets; import java.util.Objects; -import lombok.extern.slf4j.Slf4j; /** * 客户端 业务代码 @@ -15,75 +16,76 @@ @Slf4j class TimeClientHandler extends SimpleChannelInboundHandler { - private ChannelHandlerContext ctx; + private ChannelHandlerContext ctx; - /** - * 当客户端和服务端成功建立连接后, 就会调用该方法 - */ - @Override - public void channelActive(ChannelHandlerContext ctx) { - log.info("connected"); - if (Objects.isNull(this.ctx)) { - this.ctx = ctx; + /** + * 当客户端和服务端成功建立连接后, 就会调用该方法 + */ + @Override + public void channelActive(ChannelHandlerContext ctx) { + log.info("connected"); + if (Objects.isNull(this.ctx)) { + this.ctx = ctx; + } } - } - public boolean isConnected (){ - return Objects.nonNull(ctx); - } - /** - * 当服务端回应消息时,该方法被调用 - */ - @Override - public void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception { - // 读取服务端发来的回应消息 - ByteBuf buf = (ByteBuf) msg; - byte[] req = new byte[buf.readableBytes()]; - buf.readBytes(req); - String body = new String(req, StandardCharsets.UTF_8); + public boolean isConnected() { + return Objects.nonNull(ctx); + } + + /** + * 当服务端回应消息时,该方法被调用 + */ + @Override + public void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception { + // 读取服务端发来的回应消息 + ByteBuf buf = (ByteBuf) msg; + byte[] req = new byte[buf.readableBytes()]; + buf.readBytes(req); + String body = new String(req, StandardCharsets.UTF_8); - log.info("client receive msg: {}", body); + log.info("client receive msg: {}", body); // // 收到消息就关闭连接 // ctx.close(); - } - - /** - * 当发生异常时,调用该方法 - */ - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { - // 释放资源 - log.warn("Unexpected exception from downstream: {}", cause.getMessage()); - ctx.close(); - } - - /** - * 这里就需要考虑和服务端协商 发送数据的正确反序列化问题了,也就是伪TCP问题: "拆包" - */ - public void sendMsg(String msg) { - if (StringUtil.isNullOrEmpty(msg)) { - return; } - if (Objects.isNull(ctx)) { - log.warn("not connect"); - return; + /** + * 当发生异常时,调用该方法 + */ + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { + // 释放资源 + log.warn("Unexpected exception from downstream: {}", cause.getMessage()); + ctx.close(); } - log.info("msg={}", msg); - byte[] req = msg.getBytes(); - ByteBuf firstMessage = Unpooled.buffer(req.length); - firstMessage.writeBytes(req); - // 将消息发送至服务端 - ctx.writeAndFlush(firstMessage); - } + /** + * 这里就需要考虑和服务端协商 发送数据的正确反序列化问题了,也就是伪TCP问题: "拆包" + */ + public void sendMsg(String msg) { + if (StringUtil.isNullOrEmpty(msg)) { + return; + } + + if (Objects.isNull(ctx)) { + log.warn("not connect"); + return; + } + + log.info("msg={}", msg); + byte[] req = msg.getBytes(); + ByteBuf firstMessage = Unpooled.buffer(req.length); + firstMessage.writeBytes(req); + // 将消息发送至服务端 + ctx.writeAndFlush(firstMessage); + } - public ChannelHandlerContext getCtx() { - return ctx; - } + public ChannelHandlerContext getCtx() { + return ctx; + } - public void close() { - this.ctx.close(); - } + public void close() { + this.ctx.close(); + } } diff --git a/netty/src/main/java/netty/timeServer/TimeServer.java b/netty/src/main/java/netty/timeServer/TimeServer.java index 35d4e182..9d79a83b 100644 --- a/netty/src/main/java/netty/timeServer/TimeServer.java +++ b/netty/src/main/java/netty/timeServer/TimeServer.java @@ -14,53 +14,53 @@ @Slf4j class TimeServer { - static final int port = 8080; - private Channel channel; + static final int port = 8080; + private Channel channel; - public void start() throws Exception { - // NIO 结合两个线程池 - EventLoopGroup bossGroup = new NioEventLoopGroup(); - EventLoopGroup workerGroup = new NioEventLoopGroup(); + public void start() throws Exception { + // NIO 结合两个线程池 + EventLoopGroup bossGroup = new NioEventLoopGroup(); + EventLoopGroup workerGroup = new NioEventLoopGroup(); - try { - ServerBootstrap serverBootstrap = new ServerBootstrap(); - // boss/acceptor 用于接受客户端的连接 worker 处理SocketChannel的 read write 然后交由对应的Handler处理 - // 可以看 group 的 doc - serverBootstrap.group(bossGroup, workerGroup) - .channel(NioServerSocketChannel.class) - .option(ChannelOption.SO_BACKLOG, 512) - .childHandler(new ChannelInit(this)); + try { + ServerBootstrap serverBootstrap = new ServerBootstrap(); + // boss/acceptor 用于接受客户端的连接 worker 处理SocketChannel的 read write 然后交由对应的Handler处理 + // 可以看 group 的 doc + serverBootstrap.group(bossGroup, workerGroup) + .channel(NioServerSocketChannel.class) + .option(ChannelOption.SO_BACKLOG, 512) + .childHandler(new ChannelInit(this)); - // 绑定端口,同步等待成功 返回一个ChannelFuture, 用于异步操作的通知回调 - ChannelFuture future = serverBootstrap.bind(port).sync(); - this.channel = future.channel(); - // 等待服务端监听端口关闭 - future.channel().closeFuture().sync(); - } finally { - // 优雅退出,释放线程池资源 - bossGroup.shutdownGracefully(); - workerGroup.shutdownGracefully(); + // 绑定端口,同步等待成功 返回一个ChannelFuture, 用于异步操作的通知回调 + ChannelFuture future = serverBootstrap.bind(port).sync(); + this.channel = future.channel(); + // 等待服务端监听端口关闭 + future.channel().closeFuture().sync(); + } finally { + // 优雅退出,释放线程池资源 + bossGroup.shutdownGracefully(); + workerGroup.shutdownGracefully(); + } } - } - public void stop() { - log.info("stop server"); - this.channel.close(); - } + public void stop() { + log.info("stop server"); + this.channel.close(); + } - @Slf4j - private static class ChannelInit extends ChannelInitializer { + @Slf4j + private static class ChannelInit extends ChannelInitializer { - TimeServerHandler handler = new TimeServerHandler(); + TimeServerHandler handler = new TimeServerHandler(); - public ChannelInit(TimeServer timeServer) { - log.info("init a initializer"); - handler.setTimeServer(timeServer); - } + public ChannelInit(TimeServer timeServer) { + log.info("init a initializer"); + handler.setTimeServer(timeServer); + } - @Override - protected void initChannel(SocketChannel arg0) { - arg0.pipeline().addLast(handler); + @Override + protected void initChannel(SocketChannel arg0) { + arg0.pipeline().addLast(handler); + } } - } } diff --git a/netty/src/main/java/netty/timeServer/TimeServerHandler.java b/netty/src/main/java/netty/timeServer/TimeServerHandler.java index b5830dd6..72d5606a 100644 --- a/netty/src/main/java/netty/timeServer/TimeServerHandler.java +++ b/netty/src/main/java/netty/timeServer/TimeServerHandler.java @@ -5,73 +5,74 @@ import io.netty.channel.ChannelHandler.Sharable; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; +import lombok.extern.slf4j.Slf4j; + import java.nio.charset.StandardCharsets; import java.time.LocalDateTime; import java.util.concurrent.atomic.AtomicInteger; -import lombok.extern.slf4j.Slf4j; /** * 服务端的业务代码 继承自 5: ChannelHandlerAdapter 4: SimpleChannelInboundHandler */ @Slf4j @Sharable - // 可被注册到多个 pipeline + // 可被注册到多个 pipeline class TimeServerHandler extends SimpleChannelInboundHandler { - private TimeServer timeServer; + private TimeServer timeServer; - public TimeServerHandler() { - log.warn("init a instance"); - } + public TimeServerHandler() { + log.warn("init a instance"); + } - private static final AtomicInteger counter = new AtomicInteger(); + private static final AtomicInteger counter = new AtomicInteger(); - /** - * 成功建立连接后, 读取服务端的消息 - */ - @Override - public void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception { - ByteBuf buf = (ByteBuf) msg; - byte[] req = new byte[buf.readableBytes()]; - buf.readBytes(req); - String body = new String(req, StandardCharsets.UTF_8); + /** + * 成功建立连接后, 读取服务端的消息 + */ + @Override + public void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception { + ByteBuf buf = (ByteBuf) msg; + byte[] req = new byte[buf.readableBytes()]; + buf.readBytes(req); + String body = new String(req, StandardCharsets.UTF_8); - log.info("server receive msg: body={} count={}", body, counter.incrementAndGet()); + log.info("server receive msg: body={} count={}", body, counter.incrementAndGet()); - String result; - if (Command.QUERY_TIME.equalsIgnoreCase(body)) { - result = LocalDateTime.now().toString(); - } else if (Command.STOP_SERVER.equalsIgnoreCase(body)) { - timeServer.stop(); - result = Command.STOP_SERVER; - } else { - result = "BAD ORDER"; + String result; + if (Command.QUERY_TIME.equalsIgnoreCase(body)) { + result = LocalDateTime.now().toString(); + } else if (Command.STOP_SERVER.equalsIgnoreCase(body)) { + timeServer.stop(); + result = Command.STOP_SERVER; + } else { + result = "BAD ORDER"; + } + ByteBuf response = Unpooled.copiedBuffer(result.getBytes()); + ctx.write(response); } - ByteBuf response = Unpooled.copiedBuffer(result.getBytes()); - ctx.write(response); - } - /** - * 因为是将消息队列中的消息放到缓冲数组中, 那么需要调用这个flush方法将消息发送至客户端 - */ - @Override - public void channelReadComplete(ChannelHandlerContext ctx) { - ctx.flush(); - } + /** + * 因为是将消息队列中的消息放到缓冲数组中, 那么需要调用这个flush方法将消息发送至客户端 + */ + @Override + public void channelReadComplete(ChannelHandlerContext ctx) { + ctx.flush(); + } - /** - * 发生异常就调用该方法 - */ - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { - ctx.close(); - } + /** + * 发生异常就调用该方法 + */ + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { + ctx.close(); + } - public TimeServer getTimeServer() { - return timeServer; - } + public TimeServer getTimeServer() { + return timeServer; + } - public void setTimeServer(TimeServer timeServer) { - this.timeServer = timeServer; - } + public void setTimeServer(TimeServer timeServer) { + this.timeServer = timeServer; + } } From 024489205218ee01d634d3d3e382bff8f1ce6e8b Mon Sep 17 00:00:00 2001 From: kuangcp Date: Sun, 4 Feb 2024 17:54:46 +0800 Subject: [PATCH 325/476] fix: buffer error .. --- .../main/java/com/github/kuangcp/io/CopyFile.java | 14 ++++++++------ .../java/com/github/kuangcp/io/CopyFileTest.java | 11 +++++------ 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/network/src/main/java/com/github/kuangcp/io/CopyFile.java b/network/src/main/java/com/github/kuangcp/io/CopyFile.java index e72a8d23..ea29164f 100644 --- a/network/src/main/java/com/github/kuangcp/io/CopyFile.java +++ b/network/src/main/java/com/github/kuangcp/io/CopyFile.java @@ -14,9 +14,9 @@ * 注意: 如果输出流尚未关闭 内存的数据尚未刷新到硬盘上, 会导致不一致的情况 */ @Slf4j -class CopyFile { +public class CopyFile { - // 字节流 字节流转换为字符流 缓冲字符流 -> + // 将字节流转换为字符流 按行缓冲处理字符流 void copyFileWithSixChannel(String from, String dest) { InputStream inputStream = null; InputStreamReader inputStreamReader = null; @@ -59,7 +59,7 @@ void copyFileWithSixChannel(String from, String dest) { } } - // 字节流 + // 字节流 void copyFileByByte(String from, String dest) { FileInputStream inputStream = null; FileOutputStream outputStream = null; @@ -69,9 +69,11 @@ void copyFileByByte(String from, String dest) { outputStream = new FileOutputStream(dest); // 字节缓冲 数组 - byte[] buffer = new byte[1024]; - while (inputStream.read(buffer) != -1) { - outputStream.write(buffer); + byte[] buffer = new byte[8192]; + int bytesRead; + while ((bytesRead = inputStream.read(buffer)) != -1) { + // + outputStream.write(buffer, 0, bytesRead); } } catch (Exception e) { log.error("", e); diff --git a/network/src/test/java/com/github/kuangcp/io/CopyFileTest.java b/network/src/test/java/com/github/kuangcp/io/CopyFileTest.java index 1d7d297a..ce949148 100644 --- a/network/src/test/java/com/github/kuangcp/io/CopyFileTest.java +++ b/network/src/test/java/com/github/kuangcp/io/CopyFileTest.java @@ -9,7 +9,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.Optional; +import java.util.stream.Collectors; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; @@ -30,7 +30,7 @@ public class CopyFileTest { private final Path fromPath = Paths.get(from); private final Path destPath = Paths.get(dest); - private final String content = "hello world"; + private final String content = "hello world\n123\n456"; @Before public void createFile() throws IOException { @@ -79,9 +79,8 @@ public void testCopyByFiles() throws IOException { } private void validateResultFile() throws IOException { - Optional fileContent = Files.lines(destPath).reduce(String::concat); - assert fileContent.isPresent(); - log.debug("validate: content={}", fileContent.get()); - assertThat(fileContent.get(), equalTo(content)); + String fileContent = Files.lines(destPath).collect(Collectors.joining("\n")); + log.debug("validate: content={}", fileContent); + assertThat(fileContent, equalTo(content)); } } \ No newline at end of file From 02e1b5b4f99d519d04f8d8f1deea39e34dc47ab1 Mon Sep 17 00:00:00 2001 From: kuangcp Date: Mon, 19 Feb 2024 14:25:39 +0800 Subject: [PATCH 326/476] copy --- .../com/github/kuangcp/distri/Snowflake.java | 174 ++++++++++++++++++ .../github/kuangcp/recursion/Fibonacci.java | 93 +++++----- .../github/kuangcp/distri/SnowflakeTest.java | 21 +++ .../kuangcp/found/BinarySearchTest.java | 58 +++--- .../netty/core/future/ChannelFutureTest.java | 2 +- 5 files changed, 271 insertions(+), 77 deletions(-) create mode 100644 algorithms/src/main/java/com/github/kuangcp/distri/Snowflake.java create mode 100644 algorithms/src/test/java/com/github/kuangcp/distri/SnowflakeTest.java diff --git a/algorithms/src/main/java/com/github/kuangcp/distri/Snowflake.java b/algorithms/src/main/java/com/github/kuangcp/distri/Snowflake.java new file mode 100644 index 00000000..a3fee18c --- /dev/null +++ b/algorithms/src/main/java/com/github/kuangcp/distri/Snowflake.java @@ -0,0 +1,174 @@ +package com.github.kuangcp.distri; + +/** + * https://pdai.tech/md/algorithm/alg-domain-id-snowflake.html + * + * SnowFlake的结构如下(每部分用-分开):
+ * 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000
+ * 1位标识,由于long基本类型在Java中是带符号的,最高位是符号位,正数是0,负数是1,所以id一般是正数,最高位是0
+ * 41位时间截(毫秒级),注意,41位时间截不是存储当前时间的时间截,而是存储时间截的差值(当前时间截 - 开始时间截) + * 得到的值),这里的的开始时间截,一般是我们的id生成器开始使用的时间,由我们程序来指定的(如下下面程序IdWorker类的startTime属性)。41位的时间截,可以使用69年,年T = (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69
+ * 10位的数据机器位,可以部署在1024个节点,包括5位datacenterId和5位workerId
+ * 12位序列,毫秒内的计数,12位的计数顺序号支持每个节点每毫秒(同一机器,同一时间截)产生4096个ID序号
+ * 加起来刚好64位,为一个Long型。
+ * SnowFlake的优点是,整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由数据中心ID和机器ID作区分),并且效率较高,经测试,SnowFlake每秒能够产生26万ID左右。 + * + * @author Kuangcp + * 2024-02-19 14:18 + */ +public class Snowflake { + + + // ==============================Fields=========================================== + /** + * 开始时间截 (2015-01-01) + */ + private final long twepoch = 1420041600000L; + + /** + * 机器id所占的位数 + */ + private final long workerIdBits = 5L; + + /** + * 数据标识id所占的位数 + */ + private final long datacenterIdBits = 5L; + + /** + * 支持的最大机器id,结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数) + */ + private final long maxWorkerId = -1L ^ (-1L << workerIdBits); + + /** + * 支持的最大数据标识id,结果是31 + */ + private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits); + + /** + * 序列在id中占的位数 + */ + private final long sequenceBits = 12L; + + /** + * 机器ID向左移12位 + */ + private final long workerIdShift = sequenceBits; + + /** + * 数据标识id向左移17位(12+5) + */ + private final long datacenterIdShift = sequenceBits + workerIdBits; + + /** + * 时间截向左移22位(5+5+12) + */ + private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits; + + /** + * 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095) + */ + private final long sequenceMask = -1L ^ (-1L << sequenceBits); + + /** + * 工作机器ID(0~31) + */ + private long workerId; + + /** + * 数据中心ID(0~31) + */ + private long datacenterId; + + /** + * 毫秒内序列(0~4095) + */ + private long sequence = 0L; + + /** + * 上次生成ID的时间截 + */ + private long lastTimestamp = -1L; + + //==============================Constructors===================================== + + /** + * 构造函数 + * + * @param workerId 工作ID (0~31) + * @param datacenterId 数据中心ID (0~31) + */ + public Snowflake(long workerId, long datacenterId) { + if (workerId > maxWorkerId || workerId < 0) { + throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId)); + } + if (datacenterId > maxDatacenterId || datacenterId < 0) { + throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId)); + } + this.workerId = workerId; + this.datacenterId = datacenterId; + } + + // ==============================Methods========================================== + + /** + * 获得下一个ID (该方法是线程安全的) + * + * @return SnowflakeId + */ + public synchronized long nextId() { + long timestamp = timeGen(); + + //如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常 + if (timestamp < lastTimestamp) { + throw new RuntimeException(String.format("Clock moved backwards. " + + "Refusing to generate id for %d milliseconds", lastTimestamp - timestamp)); + } + + //如果是同一时间生成的,则进行毫秒内序列 + if (lastTimestamp == timestamp) { + sequence = (sequence + 1) & sequenceMask; + //毫秒内序列溢出 + if (sequence == 0) { + //阻塞到下一个毫秒,获得新的时间戳 + timestamp = tilNextMillis(lastTimestamp); + } + } + //时间戳改变,毫秒内序列重置 + else { + sequence = 0L; + } + + //上次生成ID的时间截 + lastTimestamp = timestamp; + + //移位并通过或运算拼到一起组成64位的ID + return ((timestamp - twepoch) << timestampLeftShift) // + | (datacenterId << datacenterIdShift) // + | (workerId << workerIdShift) // + | sequence; + } + + /** + * 阻塞到下一个毫秒,直到获得新的时间戳 + * + * @param lastTimestamp 上次生成ID的时间截 + * @return 当前时间戳 + */ + protected long tilNextMillis(long lastTimestamp) { + long timestamp = timeGen(); + while (timestamp <= lastTimestamp) { + timestamp = timeGen(); + } + return timestamp; + } + + /** + * 返回以毫秒为单位的当前时间 + * + * @return 当前时间(毫秒) + */ + protected long timeGen() { + return System.currentTimeMillis(); + } +} diff --git a/algorithms/src/main/java/com/github/kuangcp/recursion/Fibonacci.java b/algorithms/src/main/java/com/github/kuangcp/recursion/Fibonacci.java index e930352f..431f3d81 100644 --- a/algorithms/src/main/java/com/github/kuangcp/recursion/Fibonacci.java +++ b/algorithms/src/main/java/com/github/kuangcp/recursion/Fibonacci.java @@ -5,56 +5,55 @@ */ class Fibonacci { - /** - * https://bbs.pediy.com/thread-123051.htm - * 递归解法, 重复计算太多 时间复杂度: 2^(N/2) < T(N) < 2^(N) - */ - static int recursiveOne(int num) { -// System.out.println("run"); - if (num < 0) { - return 0; + /** + * https://bbs.pediy.com/thread-123051.htm + * 递归解法, 重复计算太多 时间复杂度: 2^(N/2) < T(N) < 2^(N) + */ + static int recursiveOne(int num) { + if (num < 0) { + return 0; + } + + if (num == 1) { + return 1; + } + + return recursiveOne(num - 1) + recursiveOne(num - 2); } - if (num == 1) { - return 1; + /** + * 迭代方式, 一直缓存两个值 当前值 前一个值, 并依次后移 + * 时间复杂度 O(N) + */ + static int loopOne(int num) { + if (num <= 0) { + return 0; + } + if (num == 1) { + return 1; + } + + int pre = 1; + int cur = 1; + + for (int i = 0; i < num - 2; i++) { + int temp = pre; + pre = cur; + cur = temp + cur; + } + + return cur; } - return recursiveOne(num - 1) + recursiveOne(num - 2); - } - - /** - * 迭代方式, 一直缓存两个值 当前值 前一个值, 并依次后移 - * 时间复杂度 O(N) - */ - static int loopOne(int num) { - if (num <= 0) { - return 0; - } - if (num == 1) { - return 1; - } - - int pre = 1; - int cur = 1; - - for (int i = 0; i < num - 2; i++) { - int temp = pre; - pre = cur; - cur = temp + cur; + /** + * 构造等比数列, 线性代数, 特征方程, 母函数法 等方式可以求解通项公式 + * + * fi(n) = + * + * 性能最好 + */ + static int generalTermFormula(int num) { + double temp = Math.sqrt(5); + return (int) (1 / temp * (Math.pow((1 + temp) / 2, num) - Math.pow((1 - temp) / 2, num))); } - - return cur; - } - - /** - * 构造等比数列, 线性代数, 特征方程, 母函数法 等方式可以求解通项公式 - * - * fi(n) = - * - * 性能最好 - */ - static int generalTermFormula(int num) { - double temp = Math.sqrt(5); - return (int) (1 / temp * (Math.pow((1 + temp) / 2, num) - Math.pow((1 - temp) / 2, num))); - } } diff --git a/algorithms/src/test/java/com/github/kuangcp/distri/SnowflakeTest.java b/algorithms/src/test/java/com/github/kuangcp/distri/SnowflakeTest.java new file mode 100644 index 00000000..10a31e73 --- /dev/null +++ b/algorithms/src/test/java/com/github/kuangcp/distri/SnowflakeTest.java @@ -0,0 +1,21 @@ +package com.github.kuangcp.distri; + +import org.junit.Test; + +/** + * + * @author Kuangcp + * 2024-02-19 14:20 + */ +public class SnowflakeTest { + + @Test + public void test() throws Exception { + Snowflake idWorker = new Snowflake(0, 0); + for (int i = 0; i < 1000; i++) { + long id = idWorker.nextId(); +// System.out.println(Long.toBinaryString(id)); + System.out.println(id); + } + } +} \ No newline at end of file diff --git a/algorithms/src/test/java/com/github/kuangcp/found/BinarySearchTest.java b/algorithms/src/test/java/com/github/kuangcp/found/BinarySearchTest.java index 676ac26d..8a93df95 100644 --- a/algorithms/src/test/java/com/github/kuangcp/found/BinarySearchTest.java +++ b/algorithms/src/test/java/com/github/kuangcp/found/BinarySearchTest.java @@ -12,34 +12,34 @@ @Slf4j public class BinarySearchTest { - private int dataBaseValue = 10; - private int dataRange = 90; - private int dataScale = 100; - - @Test - public void testFind() { - BinarySearch s = new BinarySearch(); - int[] dat = new int[dataScale]; - - for (int i = 0; i < dat.length; i++) { - dat[i] = (int) (Math.random() * dataRange + dataBaseValue); - } - - Insert.INSTANCE.sort(dat); - - for (int i = 0; i < dat.length; i++) { //将数组遍历一下 - System.out.print(dat[i] + " "); - if ((i + 1) % 10 == 0) { - System.out.println(); - } - } - - int randomValue = (int) (Math.random() * dataRange + dataBaseValue); - int result = s.find(dat, randomValue); - if (result != -1) { - log.debug("你要找的数据是第 {} 个数字 {}", result, randomValue); - } else { - log.debug("该数据不存在,查找失败!value={}", randomValue); + private int dataBaseValue = 10; + private int dataRange = 90; + private int dataScale = 100; + + @Test + public void testFind() { + BinarySearch s = new BinarySearch(); + int[] dat = new int[dataScale]; + + for (int i = 0; i < dat.length; i++) { + dat[i] = (int) (Math.random() * dataRange + dataBaseValue); + } + + Insert.INSTANCE.sort(dat); + + for (int i = 0; i < dat.length; i++) { //将数组遍历一下 + System.out.print(dat[i] + " "); + if ((i + 1) % 10 == 0) { + System.out.println(); + } + } + + int randomValue = (int) (Math.random() * dataRange + dataBaseValue); + int result = s.find(dat, randomValue); + if (result != -1) { + log.debug("你要找的数据是第 {} 个数字 {}", result, randomValue); + } else { + log.debug("该数据不存在,查找失败!value={}", randomValue); + } } - } } \ No newline at end of file diff --git a/netty/src/test/java/netty/core/future/ChannelFutureTest.java b/netty/src/test/java/netty/core/future/ChannelFutureTest.java index e8038a85..5d0b0448 100644 --- a/netty/src/test/java/netty/core/future/ChannelFutureTest.java +++ b/netty/src/test/java/netty/core/future/ChannelFutureTest.java @@ -13,7 +13,7 @@ /** * - * @author kuangchengping@sinohealth.cn + * @author Kuangcp * 2024-02-01 10:43 */ public class ChannelFutureTest { From 6d9ec112b1560e80e487c57c801d0b727dc192d3 Mon Sep 17 00:00:00 2001 From: kuangcp Date: Tue, 20 Feb 2024 17:24:06 +0800 Subject: [PATCH 327/476] fmt --- .../java/com/github/kuangcp/aop/Readme.md | 1 + .../instantiation/ComplexConstructor.java | 30 +++++++------- .../InstantiationAndConstructor.java | 39 ++++++++++--------- .../instantiation/StaticFieldInit.java | 15 +++---- 4 files changed, 44 insertions(+), 41 deletions(-) create mode 100644 class/src/main/java/com/github/kuangcp/aop/Readme.md diff --git a/class/src/main/java/com/github/kuangcp/aop/Readme.md b/class/src/main/java/com/github/kuangcp/aop/Readme.md new file mode 100644 index 00000000..16e2edef --- /dev/null +++ b/class/src/main/java/com/github/kuangcp/aop/Readme.md @@ -0,0 +1 @@ +字节码增强实现 代理模式 AOP \ No newline at end of file diff --git a/class/src/main/java/com/github/kuangcp/instantiation/ComplexConstructor.java b/class/src/main/java/com/github/kuangcp/instantiation/ComplexConstructor.java index 947f287d..29edf30b 100644 --- a/class/src/main/java/com/github/kuangcp/instantiation/ComplexConstructor.java +++ b/class/src/main/java/com/github/kuangcp/instantiation/ComplexConstructor.java @@ -8,30 +8,30 @@ @Slf4j class ComplexConstructor { - static class Server extends AbstractServer { + static class Server extends AbstractServer { - private int port = 8080; + private int port = 8080; - Server(int port) { - this.port = port; - } + Server(int port) { + this.port = port; + } - @Override - int getPort() { - return port; + @Override + int getPort() { + return port; + } } - } } @Slf4j abstract class AbstractServer { - int actualPort; + int actualPort; - AbstractServer() { - actualPort = getPort(); - log.info("actualPort={}", actualPort); - } + AbstractServer() { + actualPort = getPort(); + log.info("actualPort={}", actualPort); + } - abstract int getPort(); + abstract int getPort(); } \ No newline at end of file diff --git a/class/src/main/java/com/github/kuangcp/instantiation/InstantiationAndConstructor.java b/class/src/main/java/com/github/kuangcp/instantiation/InstantiationAndConstructor.java index 5b9ba32d..b78e0308 100644 --- a/class/src/main/java/com/github/kuangcp/instantiation/InstantiationAndConstructor.java +++ b/class/src/main/java/com/github/kuangcp/instantiation/InstantiationAndConstructor.java @@ -1,9 +1,10 @@ package com.github.kuangcp.instantiation; -import java.io.Serializable; import lombok.Data; import lombok.extern.slf4j.Slf4j; +import java.io.Serializable; + /** * 测试 实例化对象和构造器之间的关系 * @@ -13,27 +14,27 @@ @Slf4j class InstantiationAndConstructor implements Serializable, Cloneable { - private String name; + private String name; - static { - log.info("invoke static init block"); - } + static { + log.info("invoke static init block"); + } - { - log.info("invoke init block"); - } + { + log.info("invoke init block"); + } - public InstantiationAndConstructor() { - log.warn("invoke empty constructor"); - } + public InstantiationAndConstructor() { + log.warn("invoke empty constructor"); + } - public InstantiationAndConstructor(String name) { - this.name = name; - log.warn("invoke constructor(name): name={}", name); - } + public InstantiationAndConstructor(String name) { + this.name = name; + log.warn("invoke constructor(name): name={}", name); + } - @Override - protected InstantiationAndConstructor clone() throws CloneNotSupportedException { - return (InstantiationAndConstructor) super.clone(); - } + @Override + protected InstantiationAndConstructor clone() throws CloneNotSupportedException { + return (InstantiationAndConstructor) super.clone(); + } } diff --git a/class/src/main/java/com/github/kuangcp/instantiation/StaticFieldInit.java b/class/src/main/java/com/github/kuangcp/instantiation/StaticFieldInit.java index d7cecdff..f57cbab9 100644 --- a/class/src/main/java/com/github/kuangcp/instantiation/StaticFieldInit.java +++ b/class/src/main/java/com/github/kuangcp/instantiation/StaticFieldInit.java @@ -9,14 +9,15 @@ */ public class StaticFieldInit { - static int num = 1; // 这个值被覆盖 + static int num = 1; // 这个值被覆盖 - static { - num = 2; - count = 2; // 这个值被覆盖, 看起来似乎是先使用再声明 - // 由于按顺序执行, 所以 count = 2 count = 1, 最终为1 - } + static { + num = 2; + count = 2; + // 这个值被覆盖, 看起来似乎是先使用再声明 + // 由于按顺序执行, 所以 count = 2 count = 1, 最终为1 + } - static int count = 1; + static int count = 1; } From dbaaa018caf5ff32dd7edc3d971549148f70f3c9 Mon Sep 17 00:00:00 2001 From: kuangcp Date: Thu, 22 Feb 2024 13:44:17 +0800 Subject: [PATCH 328/476] jmh --- test/pom.xml | 13 + .../simple/customer/domain/Person.java | 14 - .../java/com/github/kuangcp/util/StrUtil.java | 58 ++ test/src/test/java/jmh/GuideTest.java | 79 +++ .../jmh/samples/JMHSample_01_HelloWorld.java | 104 +++ .../samples/JMHSample_02_BenchmarkModes.java | 181 ++++++ .../java/jmh/samples/JMHSample_03_States.java | 126 ++++ .../samples/JMHSample_04_DefaultState.java | 84 +++ .../samples/JMHSample_05_StateFixtures.java | 132 ++++ .../samples/JMHSample_06_FixtureLevel.java | 106 ++++ .../JMHSample_07_FixtureLevelInvocation.java | 168 +++++ .../jmh/samples/JMHSample_08_DeadCode.java | 104 +++ .../jmh/samples/JMHSample_09_Blackholes.java | 133 ++++ .../samples/JMHSample_10_ConstantFold.java | 117 ++++ .../java/jmh/samples/JMHSample_11_Loops.java | 158 +++++ .../jmh/samples/JMHSample_12_Forking.java | 185 ++++++ .../jmh/samples/JMHSample_13_RunToRun.java | 129 ++++ .../jmh/samples/JMHSample_15_Asymmetric.java | 127 ++++ .../samples/JMHSample_16_CompilerControl.java | 146 +++++ .../samples/JMHSample_17_SyncIterations.java | 129 ++++ .../jmh/samples/JMHSample_18_Control.java | 105 +++ .../jmh/samples/JMHSample_20_Annotations.java | 100 +++ .../jmh/samples/JMHSample_21_ConsumeCPU.java | 148 +++++ .../samples/JMHSample_22_FalseSharing.java | 268 ++++++++ .../jmh/samples/JMHSample_23_AuxCounters.java | 131 ++++ .../jmh/samples/JMHSample_24_Inheritance.java | 133 ++++ .../java/jmh/samples/JMHSample_25_API_GA.java | 332 ++++++++++ .../jmh/samples/JMHSample_26_BatchSize.java | 141 ++++ .../java/jmh/samples/JMHSample_27_Params.java | 103 +++ .../JMHSample_28_BlackholeHelpers.java | 150 +++++ .../jmh/samples/JMHSample_29_StatesDAG.java | 171 +++++ .../jmh/samples/JMHSample_30_Interrupts.java | 123 ++++ .../jmh/samples/JMHSample_31_InfraParams.java | 141 ++++ .../jmh/samples/JMHSample_32_BulkWarmup.java | 151 +++++ .../samples/JMHSample_33_SecurityManager.java | 139 ++++ .../jmh/samples/JMHSample_34_SafeLooping.java | 203 ++++++ .../jmh/samples/JMHSample_35_Profilers.java | 600 ++++++++++++++++++ .../JMHSample_36_BranchPrediction.java | 152 +++++ .../jmh/samples/JMHSample_37_CacheAccess.java | 142 +++++ .../samples/JMHSample_38_PerInvokeSetup.java | 178 ++++++ 40 files changed, 5890 insertions(+), 14 deletions(-) delete mode 100644 test/src/main/java/com/github/kuangcp/simple/customer/domain/Person.java create mode 100644 test/src/main/java/com/github/kuangcp/util/StrUtil.java create mode 100644 test/src/test/java/jmh/GuideTest.java create mode 100644 test/src/test/java/jmh/samples/JMHSample_01_HelloWorld.java create mode 100644 test/src/test/java/jmh/samples/JMHSample_02_BenchmarkModes.java create mode 100644 test/src/test/java/jmh/samples/JMHSample_03_States.java create mode 100644 test/src/test/java/jmh/samples/JMHSample_04_DefaultState.java create mode 100644 test/src/test/java/jmh/samples/JMHSample_05_StateFixtures.java create mode 100644 test/src/test/java/jmh/samples/JMHSample_06_FixtureLevel.java create mode 100644 test/src/test/java/jmh/samples/JMHSample_07_FixtureLevelInvocation.java create mode 100644 test/src/test/java/jmh/samples/JMHSample_08_DeadCode.java create mode 100644 test/src/test/java/jmh/samples/JMHSample_09_Blackholes.java create mode 100644 test/src/test/java/jmh/samples/JMHSample_10_ConstantFold.java create mode 100644 test/src/test/java/jmh/samples/JMHSample_11_Loops.java create mode 100644 test/src/test/java/jmh/samples/JMHSample_12_Forking.java create mode 100644 test/src/test/java/jmh/samples/JMHSample_13_RunToRun.java create mode 100644 test/src/test/java/jmh/samples/JMHSample_15_Asymmetric.java create mode 100644 test/src/test/java/jmh/samples/JMHSample_16_CompilerControl.java create mode 100644 test/src/test/java/jmh/samples/JMHSample_17_SyncIterations.java create mode 100644 test/src/test/java/jmh/samples/JMHSample_18_Control.java create mode 100644 test/src/test/java/jmh/samples/JMHSample_20_Annotations.java create mode 100644 test/src/test/java/jmh/samples/JMHSample_21_ConsumeCPU.java create mode 100644 test/src/test/java/jmh/samples/JMHSample_22_FalseSharing.java create mode 100644 test/src/test/java/jmh/samples/JMHSample_23_AuxCounters.java create mode 100644 test/src/test/java/jmh/samples/JMHSample_24_Inheritance.java create mode 100644 test/src/test/java/jmh/samples/JMHSample_25_API_GA.java create mode 100644 test/src/test/java/jmh/samples/JMHSample_26_BatchSize.java create mode 100644 test/src/test/java/jmh/samples/JMHSample_27_Params.java create mode 100644 test/src/test/java/jmh/samples/JMHSample_28_BlackholeHelpers.java create mode 100644 test/src/test/java/jmh/samples/JMHSample_29_StatesDAG.java create mode 100644 test/src/test/java/jmh/samples/JMHSample_30_Interrupts.java create mode 100644 test/src/test/java/jmh/samples/JMHSample_31_InfraParams.java create mode 100644 test/src/test/java/jmh/samples/JMHSample_32_BulkWarmup.java create mode 100644 test/src/test/java/jmh/samples/JMHSample_33_SecurityManager.java create mode 100644 test/src/test/java/jmh/samples/JMHSample_34_SafeLooping.java create mode 100644 test/src/test/java/jmh/samples/JMHSample_35_Profilers.java create mode 100644 test/src/test/java/jmh/samples/JMHSample_36_BranchPrediction.java create mode 100644 test/src/test/java/jmh/samples/JMHSample_37_CacheAccess.java create mode 100644 test/src/test/java/jmh/samples/JMHSample_38_PerInvokeSetup.java diff --git a/test/pom.xml b/test/pom.xml index bbdc7284..f85713d2 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -19,6 +19,19 @@ + + org.apache.commons + commons-lang3 + + + org.openjdk.jmh + jmh-core + + + org.openjdk.jmh + jmh-generator-annprocess + + diff --git a/test/src/main/java/com/github/kuangcp/simple/customer/domain/Person.java b/test/src/main/java/com/github/kuangcp/simple/customer/domain/Person.java deleted file mode 100644 index 7449880b..00000000 --- a/test/src/main/java/com/github/kuangcp/simple/customer/domain/Person.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.github.kuangcp.simple.customer.domain; - -import lombok.Data; - -/** - * Created by https://github.com/kuangcp - * - * @author kuangcp - */ -@Data -public class Person { - - private String name; -} diff --git a/test/src/main/java/com/github/kuangcp/util/StrUtil.java b/test/src/main/java/com/github/kuangcp/util/StrUtil.java new file mode 100644 index 00000000..b0c74870 --- /dev/null +++ b/test/src/main/java/com/github/kuangcp/util/StrUtil.java @@ -0,0 +1,58 @@ +package com.github.kuangcp.util; + +import org.apache.commons.lang3.StringUtils; + +import java.util.Arrays; +import java.util.List; +import java.util.Random; +import java.util.concurrent.ThreadLocalRandom; +import java.util.stream.Stream; + +/** + * @author kuangchengping@sinohealth.cn + * 2023-06-20 18:42 + */ +public class StrUtil { + + private static final List LIST = Arrays.asList("A", "a", "B", "b", "C", "c", "D", "d", "E", "e", "F", "f", + "G", "g", "H", "h", "I", "i", "J", "j", "K", "k", "L", "l", "M", "m", "N", "n", "O", "o", "P", "p", "Q", "q", + "R", "r", "S", "s", "T", "t", "U", "u", "V", "v", "W", "w", "X", "x", "Y", "y", "Z", "z"); + + private static final String[] ALPHA = new String[]{"A", "a", "B", "b", "C", "c", "D", "d", "E", "e", "F", "f", + "G", "g", "H", "h", "I", "i", "J", "j", "K", "k", "L", "l", "M", "m", "N", "n", "O", "o", "P", "p", "Q", "q", + "R", "r", "S", "s", "T", "t", "U", "u", "V", "v", "W", "w", "X", "x", "Y", "y", "Z", "z"}; + + + public static String firstNotBlankStr(String... names) { + return Stream.of(names).filter(StringUtils::isNotBlank).findFirst().orElse(""); + } + + public static String randomAlpha(int len) { + Random random = new Random(); + random.setSeed(System.nanoTime()); + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < len; i++) { + builder.append(LIST.get(random.nextInt(LIST.size()))); + } + return builder.toString(); + } + + public static String randomAlphaA(int len) { + Random random = new Random(); + random.setSeed(System.nanoTime()); + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < len; i++) { + builder.append(ALPHA[random.nextInt(LIST.size())]); + } + return builder.toString(); + } + + public static String randomAlphaAL(int len) { + ThreadLocalRandom random = ThreadLocalRandom.current(); + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < len; i++) { + builder.append(ALPHA[random.nextInt(LIST.size())]); + } + return builder.toString(); + } +} diff --git a/test/src/test/java/jmh/GuideTest.java b/test/src/test/java/jmh/GuideTest.java new file mode 100644 index 00000000..184a055b --- /dev/null +++ b/test/src/test/java/jmh/GuideTest.java @@ -0,0 +1,79 @@ +package jmh; + +import com.github.kuangcp.util.StrUtil; +import org.junit.Test; +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +/** + * + * @author Kuangcp + * 2024-02-22 09:39 + */ +@BenchmarkMode(Mode.Throughput) +@Warmup(iterations = 1) +@Measurement(iterations = 1, time = 1) +@Threads(2) +@OutputTimeUnit(TimeUnit.MILLISECONDS) +public class GuideTest { + + + // @Benchmark + public void uuid() { + UUID.randomUUID().toString(); + } + + @Benchmark + public void rand() { + StrUtil.randomAlpha(32); + } + + // arrayList 本身是对数组的封装,性能影响不大 + @Benchmark + public void randArray() { + StrUtil.randomAlphaA(32); + } + + // 省去对象创建,效率更好 + @Benchmark + public void randArrayLocal() { + StrUtil.randomAlphaAL(32); + } + + @Test + public void testGuide() throws Exception { + Options options = new OptionsBuilder() + .include(this.getClass().getSimpleName()) + .output("/tmp/jmh-" + this.getClass().getSimpleName() + ".log").build(); + new Runner(options).run(); + } + + @Test + public void testCompare() throws Exception { + System.out.println(UUID.randomUUID()); + System.out.println(StrUtil.randomAlpha(32)); + } + + @Test + public void testDiff() throws Exception { + Set re = new HashSet<>(); + for (int i = 0; i < 10000; i++) { + re.add(StrUtil.randomAlphaAL(4)); + } + System.out.println(re.size()); + + Set re2 = new HashSet<>(); + for (int i = 0; i < 10000; i++) { + re2.add(StrUtil.randomAlphaA(4)); + } + System.out.println(re2.size()); + + } +} diff --git a/test/src/test/java/jmh/samples/JMHSample_01_HelloWorld.java b/test/src/test/java/jmh/samples/JMHSample_01_HelloWorld.java new file mode 100644 index 00000000..55ad2a81 --- /dev/null +++ b/test/src/test/java/jmh/samples/JMHSample_01_HelloWorld.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2014, Oracle America, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of Oracle nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package jmh.samples; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +public class JMHSample_01_HelloWorld { + + /* + * This is our first benchmark method. + * + * JMH works as follows: users annotate the methods with @Benchmark, and + * then JMH produces the generated code to run this particular benchmark as + * reliably as possible. In general one might think about @Benchmark methods + * as the benchmark "payload", the things we want to measure. The + * surrounding infrastructure is provided by the harness itself. + * + * Read the Javadoc for @Benchmark annotation for complete semantics and + * restrictions. At this point we only note that the methods names are + * non-essential, and it only matters that the methods are marked with + * @Benchmark. You can have multiple benchmark methods within the same + * class. + * + * Note: if the benchmark method never finishes, then JMH run never finishes + * as well. If you throw an exception from the method body the JMH run ends + * abruptly for this benchmark and JMH will run the next benchmark down the + * list. + * + * Although this benchmark measures "nothing" it is a good showcase for the + * overheads the infrastructure bear on the code you measure in the method. + * There are no magical infrastructures which incur no overhead, and it is + * important to know what are the infra overheads you are dealing with. You + * might find this thought unfolded in future examples by having the + * "baseline" measurements to compare against. + */ + + @Benchmark + public void wellHelloThere() { + // this method was intentionally left blank. + } + + /* + * ============================== HOW TO RUN THIS TEST: ==================================== + * + * You are expected to see the run with large number of iterations, and + * very large throughput numbers. You can see that as the estimate of the + * harness overheads per method call. In most of our measurements, it is + * down to several cycles per call. + * + * a) Via command-line: + * $ mvn clean install + * $ java -jar target/benchmarks.jar JMHSample_01 + * + * JMH generates self-contained JARs, bundling JMH together with it. + * The runtime options for the JMH are available with "-h": + * $ java -jar target/benchmarks.jar -h + * + * b) Via the Java API: + * (see the JMH homepage for possible caveats when running from IDE: + * http://openjdk.java.net/projects/code-tools/jmh/) + */ + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(JMHSample_01_HelloWorld.class.getSimpleName()) + .forks(1) + .build(); + + new Runner(opt).run(); + } + +} diff --git a/test/src/test/java/jmh/samples/JMHSample_02_BenchmarkModes.java b/test/src/test/java/jmh/samples/JMHSample_02_BenchmarkModes.java new file mode 100644 index 00000000..fcd14599 --- /dev/null +++ b/test/src/test/java/jmh/samples/JMHSample_02_BenchmarkModes.java @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2014, Oracle America, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of Oracle nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package jmh.samples; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +import java.util.concurrent.TimeUnit; + +public class JMHSample_02_BenchmarkModes { + + /* + * JMH generates lots of synthetic code for the benchmarks for you during + * the benchmark compilation. JMH can measure the benchmark methods in lots + * of modes. Users may select the default benchmark mode with a special + * annotation, or select/override the mode via the runtime options. + * + * With this scenario, we start to measure something useful. Note that our + * payload code potentially throws exceptions, and we can just declare them + * to be thrown. If the code throws the actual exception, the benchmark + * execution will stop with an error. + * + * When you are puzzled with some particular behavior, it usually helps to + * look into the generated code. You might see the code is doing not + * something you intend it to do. Good experiments always follow up on the + * experimental setup, and cross-checking the generated code is an important + * part of that follow up. + * + * The generated code for this particular sample is somewhere at + * target/generated-sources/annotations/.../JMHSample_02_BenchmarkModes.java + */ + + /* + * Mode.Throughput, as stated in its Javadoc, measures the raw throughput by + * continuously calling the benchmark method in a time-bound iteration, and + * counting how many times we executed the method. + * + * We are using the special annotation to select the units to measure in, + * although you can use the default. + */ + + @Benchmark + @BenchmarkMode(Mode.Throughput) + @OutputTimeUnit(TimeUnit.SECONDS) + public void measureThroughput() throws InterruptedException { + TimeUnit.MILLISECONDS.sleep(100); + } + + /* + * Mode.AverageTime measures the average execution time, and it does it + * in the way similar to Mode.Throughput. + * + * Some might say it is the reciprocal throughput, and it really is. + * There are workloads where measuring times is more convenient though. + */ + + @Benchmark + @BenchmarkMode(Mode.AverageTime) + @OutputTimeUnit(TimeUnit.MICROSECONDS) + public void measureAvgTime() throws InterruptedException { + TimeUnit.MILLISECONDS.sleep(100); + } + + /* + * Mode.SampleTime samples the execution time. With this mode, we are + * still running the method in a time-bound iteration, but instead of + * measuring the total time, we measure the time spent in *some* of + * the benchmark method calls. + * + * This allows us to infer the distributions, percentiles, etc. + * + * JMH also tries to auto-adjust sampling frequency: if the method + * is long enough, you will end up capturing all the samples. + */ + @Benchmark + @BenchmarkMode(Mode.SampleTime) + @OutputTimeUnit(TimeUnit.MICROSECONDS) + public void measureSamples() throws InterruptedException { + TimeUnit.MILLISECONDS.sleep(100); + } + + /* + * Mode.SingleShotTime measures the single method invocation time. As the Javadoc + * suggests, we do only the single benchmark method invocation. The iteration + * time is meaningless in this mode: as soon as benchmark method stops, the + * iteration is over. + * + * This mode is useful to do cold startup tests, when you specifically + * do not want to call the benchmark method continuously. + */ + @Benchmark + @BenchmarkMode(Mode.SingleShotTime) + @OutputTimeUnit(TimeUnit.MICROSECONDS) + public void measureSingleShot() throws InterruptedException { + TimeUnit.MILLISECONDS.sleep(100); + } + + /* + * We can also ask for multiple benchmark modes at once. All the tests + * above can be replaced with just a single test like this: + */ + @Benchmark + @BenchmarkMode({Mode.Throughput, Mode.AverageTime, Mode.SampleTime, Mode.SingleShotTime}) + @OutputTimeUnit(TimeUnit.MICROSECONDS) + public void measureMultiple() throws InterruptedException { + TimeUnit.MILLISECONDS.sleep(100); + } + + /* + * Or even... + */ + + @Benchmark + @BenchmarkMode(Mode.All) + @OutputTimeUnit(TimeUnit.MICROSECONDS) + public void measureAll() throws InterruptedException { + TimeUnit.MILLISECONDS.sleep(100); + } + + /* + * ============================== HOW TO RUN THIS TEST: ==================================== + * + * You are expected to see the different run modes for the same benchmark. + * Note the units are different, scores are consistent with each other. + * + * You can run this test: + * + * a) Via the command line: + * $ mvn clean install + * $ java -jar target/benchmarks.jar JMHSample_02 -f 1 + * (we requested a single fork; there are also other options, see -h) + * + * b) Via the Java API: + * (see the JMH homepage for possible caveats when running from IDE: + * http://openjdk.java.net/projects/code-tools/jmh/) + */ + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(JMHSample_02_BenchmarkModes.class.getSimpleName()) + .forks(1) + .build(); + + new Runner(opt).run(); + } + +} diff --git a/test/src/test/java/jmh/samples/JMHSample_03_States.java b/test/src/test/java/jmh/samples/JMHSample_03_States.java new file mode 100644 index 00000000..1dda74d8 --- /dev/null +++ b/test/src/test/java/jmh/samples/JMHSample_03_States.java @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2014, Oracle America, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of Oracle nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package jmh.samples; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +public class JMHSample_03_States { + + /* + * Most of the time, you need to maintain some state while the benchmark is + * running. Since JMH is heavily used to build concurrent benchmarks, we + * opted for an explicit notion of state-bearing objects. + * + * Below are two state objects. Their class names are not essential, it + * matters they are marked with @State. These objects will be instantiated + * on demand, and reused during the entire benchmark trial. + * + * The important property is that state is always instantiated by one of + * those benchmark threads which will then have the access to that state. + * That means you can initialize the fields as if you do that in worker + * threads (ThreadLocals are yours, etc). + */ + + @State(Scope.Benchmark) + public static class BenchmarkState { + volatile double x = Math.PI; + } + + @State(Scope.Thread) + public static class ThreadState { + volatile double x = Math.PI; + } + + /* + * Benchmark methods can reference the states, and JMH will inject the + * appropriate states while calling these methods. You can have no states at + * all, or have only one state, or have multiple states referenced. This + * makes building multi-threaded benchmark a breeze. + * + * For this exercise, we have two methods. + */ + + @Benchmark + public void measureUnshared(ThreadState state) { + // All benchmark threads will call in this method. + // + // However, since ThreadState is the Scope.Thread, each thread + // will have it's own copy of the state, and this benchmark + // will measure unshared case. + state.x++; + } + + @Benchmark + public void measureShared(BenchmarkState state) { + // All benchmark threads will call in this method. + // + // Since BenchmarkState is the Scope.Benchmark, all threads + // will share the state instance, and we will end up measuring + // shared case. + state.x++; + } + + /* + * ============================== HOW TO RUN THIS TEST: ==================================== + * + * You are expected to see the drastic difference in shared and unshared cases, + * because you either contend for single memory location, or not. This effect + * is more articulated on large machines. + * + * You can run this test: + * + * a) Via the command line: + * $ mvn clean install + * $ java -jar target/benchmarks.jar JMHSample_03 -t 4 -f 1 + * (we requested 4 threads, single fork; there are also other options, see -h) + * + * b) Via the Java API: + * (see the JMH homepage for possible caveats when running from IDE: + * http://openjdk.java.net/projects/code-tools/jmh/) + */ + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(JMHSample_03_States.class.getSimpleName()) + .threads(4) + .forks(1) + .build(); + + new Runner(opt).run(); + } + +} diff --git a/test/src/test/java/jmh/samples/JMHSample_04_DefaultState.java b/test/src/test/java/jmh/samples/JMHSample_04_DefaultState.java new file mode 100644 index 00000000..c74a7a4f --- /dev/null +++ b/test/src/test/java/jmh/samples/JMHSample_04_DefaultState.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2014, Oracle America, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of Oracle nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package jmh.samples; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +/* + * Fortunately, in many cases you just need a single state object. + * In that case, we can mark the benchmark instance itself to be + * the @State. Then, we can reference its own fields as any + * Java program does. + */ + +@State(Scope.Thread) +public class JMHSample_04_DefaultState { + + double x = Math.PI; + + @Benchmark + public void measure() { + x++; + } + + /* + * ============================== HOW TO RUN THIS TEST: ==================================== + * + * You can see the benchmark runs as usual. + * + * You can run this test: + * + * a) Via the command line: + * $ mvn clean install + * $ java -jar target/benchmarks.jar JMHSample_04 -f 1 + * (we requested single fork; there are also other options, see -h) + * + * b) Via the Java API: + * (see the JMH homepage for possible caveats when running from IDE: + * http://openjdk.java.net/projects/code-tools/jmh/) + */ + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(JMHSample_04_DefaultState.class.getSimpleName()) + .forks(1) + .build(); + + new Runner(opt).run(); + } + +} diff --git a/test/src/test/java/jmh/samples/JMHSample_05_StateFixtures.java b/test/src/test/java/jmh/samples/JMHSample_05_StateFixtures.java new file mode 100644 index 00000000..20fdde12 --- /dev/null +++ b/test/src/test/java/jmh/samples/JMHSample_05_StateFixtures.java @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2014, Oracle America, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of Oracle nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package jmh.samples; + +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +@State(Scope.Thread) +public class JMHSample_05_StateFixtures { + + double x; + + /* + * Since @State objects are kept around during the lifetime of the + * benchmark, it helps to have the methods which do state housekeeping. + * These are usual fixture methods, you are probably familiar with them from + * JUnit and TestNG. + * + * Fixture methods make sense only on @State objects, and JMH will fail to + * compile the test otherwise. + * + * As with the State, fixture methods are only called by those benchmark + * threads which are using the state. That means you can operate in the + * thread-local context, and (not) use synchronization as if you are + * executing in the context of benchmark thread. + * + * Note: fixture methods can also work with static fields, although the + * semantics of these operations fall back out of State scope, and obey + * usual Java rules (i.e. one static field per class). + */ + + /* + * Ok, let's prepare our benchmark: + */ + + @Setup + public void prepare() { + x = Math.PI; + } + + /* + * And, check the benchmark went fine afterwards: + */ + + @TearDown + public void check() { + assert x > Math.PI : "Nothing changed?"; + } + + /* + * This method obviously does the right thing, incrementing the field x + * in the benchmark state. check() will never fail this way, because + * we are always guaranteed to have at least one benchmark call. + */ + + @Benchmark + public void measureRight() { + x++; + } + + /* + * This method, however, will fail the check(), because we deliberately + * have the "typo", and increment only the local variable. This should + * not pass the check, and JMH will fail the run. + */ + + @Benchmark + public void measureWrong() { + double x = 0; + x++; + } + + /* + * ============================== HOW TO RUN THIS TEST: ==================================== + * + * You can see measureRight() yields the result, and measureWrong() fires + * the assert at the end of the run. + * + * You can run this test: + * + * a) Via the command line: + * $ mvn clean install + * $ java -ea -jar target/benchmarks.jar JMHSample_05 -f 1 + * (we requested single fork; there are also other options, see -h) + * + * b) Via the Java API: + * (see the JMH homepage for possible caveats when running from IDE: + * http://openjdk.java.net/projects/code-tools/jmh/) + */ + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(JMHSample_05_StateFixtures.class.getSimpleName()) + .forks(1) + .jvmArgs("-ea") + .build(); + + new Runner(opt).run(); + } + +} diff --git a/test/src/test/java/jmh/samples/JMHSample_06_FixtureLevel.java b/test/src/test/java/jmh/samples/JMHSample_06_FixtureLevel.java new file mode 100644 index 00000000..b2fdf565 --- /dev/null +++ b/test/src/test/java/jmh/samples/JMHSample_06_FixtureLevel.java @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2014, Oracle America, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of Oracle nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package jmh.samples; + +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +@State(Scope.Thread) +public class JMHSample_06_FixtureLevel { + + double x; + + /* + * Fixture methods have different levels to control when they should be run. + * There are at least three Levels available to the user. These are, from + * top to bottom: + * + * Level.Trial: before or after the entire benchmark run (the sequence of iterations) + * Level.Iteration: before or after the benchmark iteration (the sequence of invocations) + * Level.Invocation; before or after the benchmark method invocation (WARNING: read the Javadoc before using) + * + * Time spent in fixture methods does not count into the performance + * metrics, so you can use this to do some heavy-lifting. + */ + + @TearDown(Level.Iteration) + public void check() { + assert x > Math.PI : "Nothing changed?"; + } + + @Benchmark + public void measureRight() { + x++; + } + + @Benchmark + public void measureWrong() { + double x = 0; + x++; + } + + /* + * ============================== HOW TO RUN THIS TEST: ==================================== + * + * You can see measureRight() yields the result, and measureWrong() fires + * the assert at the end of first iteration! This will not generate the results + * for measureWrong(). You can also prevent JMH for proceeding further by + * requiring "fail on error". + * + * You can run this test: + * + * a) Via the command line: + * $ mvn clean install + * $ java -ea -jar target/benchmarks.jar JMHSample_06 -f 1 + * (we requested single fork; there are also other options, see -h) + * + * You can optionally supply -foe to fail the complete run. + * + * b) Via the Java API: + * (see the JMH homepage for possible caveats when running from IDE: + * http://openjdk.java.net/projects/code-tools/jmh/) + */ + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(JMHSample_06_FixtureLevel.class.getSimpleName()) + .forks(1) + .jvmArgs("-ea") + .shouldFailOnError(false) // switch to "true" to fail the complete run + .build(); + + new Runner(opt).run(); + } + +} diff --git a/test/src/test/java/jmh/samples/JMHSample_07_FixtureLevelInvocation.java b/test/src/test/java/jmh/samples/JMHSample_07_FixtureLevelInvocation.java new file mode 100644 index 00000000..52a475c6 --- /dev/null +++ b/test/src/test/java/jmh/samples/JMHSample_07_FixtureLevelInvocation.java @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2014, Oracle America, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of Oracle nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package jmh.samples; + +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +import java.util.concurrent.*; + +/** + * Fixtures have different Levels to control when they are about to run. + * Level.Invocation is useful sometimes to do some per-invocation work + * which should not count as payload (e.g. sleep for some time to emulate + * think time) + */ +@OutputTimeUnit(TimeUnit.MICROSECONDS) +public class JMHSample_07_FixtureLevelInvocation { + + /* + * Fixtures have different Levels to control when they are about to run. + * Level.Invocation is useful sometimes to do some per-invocation work, + * which should not count as payload. PLEASE NOTE the timestamping and + * synchronization for Level.Invocation helpers might significantly offset + * the measurement, use with care. See Level.Invocation javadoc for further + * discussion. + * + * Consider this sample: + */ + + /* + * This state handles the executor. + * Note we create and shutdown executor with Level.Trial, so + * it is kept around the same across all iterations. + */ + + @State(Scope.Benchmark) + public static class NormalState { + ExecutorService service; + + @Setup(Level.Trial) + public void up() { + service = Executors.newCachedThreadPool(); + } + + @TearDown(Level.Trial) + public void down() { + service.shutdown(); + } + + } + + /* + * This is the *extension* of the basic state, which also + * has the Level.Invocation fixture method, sleeping for some time. + */ + + public static class LaggingState extends NormalState { + public static final int SLEEP_TIME = Integer.getInteger("sleepTime", 10); + + @Setup(Level.Invocation) + public void lag() throws InterruptedException { + TimeUnit.MILLISECONDS.sleep(SLEEP_TIME); + } + } + + /* + * This allows us to formulate the task: measure the task turnaround in + * "hot" mode when we are not sleeping between the submits, and "cold" mode, + * when we are sleeping. + */ + + @Benchmark + @BenchmarkMode(Mode.AverageTime) + public double measureHot(NormalState e, final Scratch s) throws ExecutionException, InterruptedException { + return e.service.submit(new Task(s)).get(); + } + + @Benchmark + @BenchmarkMode(Mode.AverageTime) + public double measureCold(LaggingState e, final Scratch s) throws ExecutionException, InterruptedException { + return e.service.submit(new Task(s)).get(); + } + + /* + * This is our scratch state which will handle the work. + */ + + @State(Scope.Thread) + public static class Scratch { + private double p; + public double doWork() { + p = Math.log(p); + return p; + } + } + + public static class Task implements Callable { + private Scratch s; + + public Task(Scratch s) { + this.s = s; + } + + @Override + public Double call() { + return s.doWork(); + } + } + + /* + * ============================== HOW TO RUN THIS TEST: ==================================== + * + * You can see the cold scenario is running longer, because we pay for + * thread wakeups. + * + * You can run this test: + * + * a) Via the command line: + * $ mvn clean install + * $ java -jar target/benchmarks.jar JMHSample_07 -f 1 + * (we requested single fork; there are also other options, see -h) + * + * b) Via the Java API: + * (see the JMH homepage for possible caveats when running from IDE: + * http://openjdk.java.net/projects/code-tools/jmh/) + */ + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(JMHSample_07_FixtureLevelInvocation.class.getSimpleName()) + .forks(1) + .build(); + + new Runner(opt).run(); + } + +} diff --git a/test/src/test/java/jmh/samples/JMHSample_08_DeadCode.java b/test/src/test/java/jmh/samples/JMHSample_08_DeadCode.java new file mode 100644 index 00000000..d56d626a --- /dev/null +++ b/test/src/test/java/jmh/samples/JMHSample_08_DeadCode.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2014, Oracle America, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of Oracle nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package jmh.samples; + +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +import java.util.concurrent.TimeUnit; + +@State(Scope.Thread) +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +public class JMHSample_08_DeadCode { + + /* + * The downfall of many benchmarks is Dead-Code Elimination (DCE): compilers + * are smart enough to deduce some computations are redundant and eliminate + * them completely. If the eliminated part was our benchmarked code, we are + * in trouble. + * + * Fortunately, JMH provides the essential infrastructure to fight this + * where appropriate: returning the result of the computation will ask JMH + * to deal with the result to limit dead-code elimination (returned results + * are implicitly consumed by Blackholes, see JMHSample_09_Blackholes). + */ + + private double x = Math.PI; + + @Benchmark + public void baseline() { + // do nothing, this is a baseline + } + + @Benchmark + public void measureWrong() { + // This is wrong: result is not used and the entire computation is optimized away. + Math.log(x); + } + + @Benchmark + public double measureRight() { + // This is correct: the result is being used. + return Math.log(x); + } + + /* + * ============================== HOW TO RUN THIS TEST: ==================================== + * + * You can see the unrealistically fast calculation in with measureWrong(), + * while realistic measurement with measureRight(). + * + * You can run this test: + * + * a) Via the command line: + * $ mvn clean install + * $ java -jar target/benchmarks.jar JMHSample_08 -f 1 + * (we requested single fork; there are also other options, see -h) + * + * b) Via the Java API: + * (see the JMH homepage for possible caveats when running from IDE: + * http://openjdk.java.net/projects/code-tools/jmh/) + */ + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(JMHSample_08_DeadCode.class.getSimpleName()) + .forks(1) + .build(); + + new Runner(opt).run(); + } + +} diff --git a/test/src/test/java/jmh/samples/JMHSample_09_Blackholes.java b/test/src/test/java/jmh/samples/JMHSample_09_Blackholes.java new file mode 100644 index 00000000..369497c3 --- /dev/null +++ b/test/src/test/java/jmh/samples/JMHSample_09_Blackholes.java @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2014, Oracle America, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of Oracle nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package jmh.samples; + +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.infra.Blackhole; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +import java.util.concurrent.TimeUnit; + +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Thread) +public class JMHSample_09_Blackholes { + + /* + * Should your benchmark require returning multiple results, you have to + * consider two options (detailed below). + * + * NOTE: If you are only producing a single result, it is more readable to + * use the implicit return, as in JMHSample_08_DeadCode. Do not make your benchmark + * code less readable with explicit Blackholes! + */ + + double x1 = Math.PI; + double x2 = Math.PI * 2; + + /* + * Baseline measurement: how much single Math.log costs. + */ + + @Benchmark + public double baseline() { + return Math.log(x1); + } + + /* + * While the Math.log(x2) computation is intact, Math.log(x1) + * is redundant and optimized out. + */ + + @Benchmark + public double measureWrong() { + Math.log(x1); + return Math.log(x2); + } + + /* + * This demonstrates Option A: + * + * Merge multiple results into one and return it. + * This is OK when is computation is relatively heavyweight, and merging + * the results does not offset the results much. + */ + + @Benchmark + public double measureRight_1() { + return Math.log(x1) + Math.log(x2); + } + + /* + * This demonstrates Option B: + * + * Use explicit Blackhole objects, and sink the values there. + * (Background: Blackhole is just another @State object, bundled with JMH). + */ + + @Benchmark + public void measureRight_2(Blackhole bh) { + bh.consume(Math.log(x1)); + bh.consume(Math.log(x2)); + } + + /* + * ============================== HOW TO RUN THIS TEST: ==================================== + * + * You will see measureWrong() running on-par with baseline(). + * Both measureRight() are measuring twice the baseline, so the logs are intact. + * + * You can run this test: + * + * a) Via the command line: + * $ mvn clean install + * $ java -jar target/benchmarks.jar JMHSample_09 -f 1 + * (we requested single fork; there are also other options, see -h) + * + * b) Via the Java API: + * (see the JMH homepage for possible caveats when running from IDE: + * http://openjdk.java.net/projects/code-tools/jmh/) + */ + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(JMHSample_09_Blackholes.class.getSimpleName()) + .forks(1) + .build(); + + new Runner(opt).run(); + } + + +} diff --git a/test/src/test/java/jmh/samples/JMHSample_10_ConstantFold.java b/test/src/test/java/jmh/samples/JMHSample_10_ConstantFold.java new file mode 100644 index 00000000..65e9888f --- /dev/null +++ b/test/src/test/java/jmh/samples/JMHSample_10_ConstantFold.java @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2014, Oracle America, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of Oracle nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package jmh.samples; + +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +import java.util.concurrent.TimeUnit; + +@State(Scope.Thread) +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +public class JMHSample_10_ConstantFold { + + /* + * The flip side of dead-code elimination is constant-folding. + * + * If JVM realizes the result of the computation is the same no matter what, + * it can cleverly optimize it. In our case, that means we can move the + * computation outside of the internal JMH loop. + * + * This can be prevented by always reading the inputs from non-final + * instance fields of @State objects, computing the result based on those + * values, and follow the rules to prevent DCE. + */ + + // IDEs will say "Oh, you can convert this field to local variable". Don't. Trust. Them. + // (While this is normally fine advice, it does not work in the context of measuring correctly.) + private double x = Math.PI; + + // IDEs will probably also say "Look, it could be final". Don't. Trust. Them. Either. + // (While this is normally fine advice, it does not work in the context of measuring correctly.) + private final double wrongX = Math.PI; + + @Benchmark + public double baseline() { + // simply return the value, this is a baseline + return Math.PI; + } + + @Benchmark + public double measureWrong_1() { + // This is wrong: the source is predictable, and computation is foldable. + return Math.log(Math.PI); + } + + @Benchmark + public double measureWrong_2() { + // This is wrong: the source is predictable, and computation is foldable. + return Math.log(wrongX); + } + + @Benchmark + public double measureRight() { + // This is correct: the source is not predictable. + return Math.log(x); + } + + /* + * ============================== HOW TO RUN THIS TEST: ==================================== + * + * You can see the unrealistically fast calculation in with measureWrong_*(), + * while realistic measurement with measureRight(). + * + * You can run this test: + * + * a) Via the command line: + * $ mvn clean install + * $ java -jar target/benchmarks.jar JMHSample_10 -i 5 -f 1 + * (we requested single fork; there are also other options, see -h) + * + * b) Via the Java API: + * (see the JMH homepage for possible caveats when running from IDE: + * http://openjdk.java.net/projects/code-tools/jmh/) + */ + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(JMHSample_10_ConstantFold.class.getSimpleName()) + .forks(1) + .build(); + + new Runner(opt).run(); + } + +} diff --git a/test/src/test/java/jmh/samples/JMHSample_11_Loops.java b/test/src/test/java/jmh/samples/JMHSample_11_Loops.java new file mode 100644 index 00000000..7471c812 --- /dev/null +++ b/test/src/test/java/jmh/samples/JMHSample_11_Loops.java @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2014, Oracle America, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of Oracle nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package jmh.samples; + +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +import java.util.concurrent.TimeUnit; + +@State(Scope.Thread) +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +public class JMHSample_11_Loops { + + /* + * It would be tempting for users to do loops within the benchmarked method. + * (This is the bad thing Caliper taught everyone). These tests explain why + * this is a bad idea. + * + * Looping is done in the hope of minimizing the overhead of calling the + * test method, by doing the operations inside the loop instead of inside + * the method call. Don't buy this argument; you will see there is more + * magic happening when we allow optimizers to merge the loop iterations. + */ + + /* + * Suppose we want to measure how much it takes to sum two integers: + */ + + int x = 1; + int y = 2; + + /* + * This is what you do with JMH. + */ + + @Benchmark + public int measureRight() { + return (x + y); + } + + /* + * The following tests emulate the naive looping. + * This is the Caliper-style benchmark. + */ + private int reps(int reps) { + int s = 0; + for (int i = 0; i < reps; i++) { + s += (x + y); + } + return s; + } + + /* + * We would like to measure this with different repetitions count. + * Special annotation is used to get the individual operation cost. + */ + + @Benchmark + @OperationsPerInvocation(1) + public int measureWrong_1() { + return reps(1); + } + + @Benchmark + @OperationsPerInvocation(10) + public int measureWrong_10() { + return reps(10); + } + + @Benchmark + @OperationsPerInvocation(100) + public int measureWrong_100() { + return reps(100); + } + + @Benchmark + @OperationsPerInvocation(1_000) + public int measureWrong_1000() { + return reps(1_000); + } + + @Benchmark + @OperationsPerInvocation(10_000) + public int measureWrong_10000() { + return reps(10_000); + } + + @Benchmark + @OperationsPerInvocation(100_000) + public int measureWrong_100000() { + return reps(100_000); + } + + /* + * ============================== HOW TO RUN THIS TEST: ==================================== + * + * You might notice the larger the repetitions count, the lower the "perceived" + * cost of the operation being measured. Up to the point we do each addition with 1/20 ns, + * well beyond what hardware can actually do. + * + * This happens because the loop is heavily unrolled/pipelined, and the operation + * to be measured is hoisted from the loop. Morale: don't overuse loops, rely on JMH + * to get the measurement right. + * + * You can run this test: + * + * a) Via the command line: + * $ mvn clean install + * $ java -jar target/benchmarks.jar JMHSample_11 -f 1 + * (we requested single fork; there are also other options, see -h) + * + * b) Via the Java API: + * (see the JMH homepage for possible caveats when running from IDE: + * http://openjdk.java.net/projects/code-tools/jmh/) + */ + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(JMHSample_11_Loops.class.getSimpleName()) + .forks(1) + .build(); + + new Runner(opt).run(); + } + +} diff --git a/test/src/test/java/jmh/samples/JMHSample_12_Forking.java b/test/src/test/java/jmh/samples/JMHSample_12_Forking.java new file mode 100644 index 00000000..14c98665 --- /dev/null +++ b/test/src/test/java/jmh/samples/JMHSample_12_Forking.java @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2014, Oracle America, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of Oracle nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package jmh.samples; + +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +import java.util.concurrent.TimeUnit; + +@State(Scope.Thread) +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +public class JMHSample_12_Forking { + + /* + * JVMs are notoriously good at profile-guided optimizations. This is bad + * for benchmarks, because different tests can mix their profiles together, + * and then render the "uniformly bad" code for every test. Forking (running + * in a separate process) each test can help to evade this issue. + * + * JMH will fork the tests by default. + */ + + /* + * Suppose we have this simple counter interface, and two implementations. + * Even though those are semantically the same, from the JVM standpoint, + * those are distinct classes. + */ + + public interface Counter { + int inc(); + } + + public static class Counter1 implements Counter { + private int x; + + @Override + public int inc() { + return x++; + } + } + + public static class Counter2 implements Counter { + private int x; + + @Override + public int inc() { + return x++; + } + } + + /* + * And this is how we measure it. + * Note this is susceptible for same issue with loops we mention in previous examples. + */ + + public int measure(Counter c) { + int s = 0; + for (int i = 0; i < 10; i++) { + s += c.inc(); + } + return s; + } + + /* + * These are two counters. + */ + Counter c1 = new Counter1(); + Counter c2 = new Counter2(); + + /* + * We first measure the Counter1 alone... + * Fork(0) helps to run in the same JVM. + */ + + @Benchmark + @Fork(0) + public int measure_1_c1() { + return measure(c1); + } + + /* + * Then Counter2... + */ + + @Benchmark + @Fork(0) + public int measure_2_c2() { + return measure(c2); + } + + /* + * Then Counter1 again... + */ + + @Benchmark + @Fork(0) + public int measure_3_c1_again() { + return measure(c1); + } + + /* + * These two tests have explicit @Fork annotation. + * JMH takes this annotation as the request to run the test in the forked JVM. + * It's even simpler to force this behavior for all the tests via the command + * line option "-f". The forking is default, but we still use the annotation + * for the consistency. + * + * This is the test for Counter1. + */ + + @Benchmark + @Fork(1) + public int measure_4_forked_c1() { + return measure(c1); + } + + /* + * ...and this is the test for Counter2. + */ + + @Benchmark + @Fork(1) + public int measure_5_forked_c2() { + return measure(c2); + } + + /* + * ============================== HOW TO RUN THIS TEST: ==================================== + * + * Note that C1 is faster, C2 is slower, but the C1 is slow again! This is because + * the profiles for C1 and C2 had merged together. Notice how flawless the measurement + * is for forked runs. + * + * You can run this test: + * + * a) Via the command line: + * $ mvn clean install + * $ java -jar target/benchmarks.jar JMHSample_12 + * + * b) Via the Java API: + * (see the JMH homepage for possible caveats when running from IDE: + * http://openjdk.java.net/projects/code-tools/jmh/) + */ + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(JMHSample_12_Forking.class.getSimpleName()) + .build(); + + new Runner(opt).run(); + } + +} diff --git a/test/src/test/java/jmh/samples/JMHSample_13_RunToRun.java b/test/src/test/java/jmh/samples/JMHSample_13_RunToRun.java new file mode 100644 index 00000000..325c8ea6 --- /dev/null +++ b/test/src/test/java/jmh/samples/JMHSample_13_RunToRun.java @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2014, Oracle America, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of Oracle nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package jmh.samples; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +import java.util.concurrent.TimeUnit; + +@State(Scope.Thread) +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.MILLISECONDS) +public class JMHSample_13_RunToRun { + + /* + * Forking also allows to estimate run-to-run variance. + * + * JVMs are complex systems, and the non-determinism is inherent for them. + * This requires us to always account the run-to-run variance as the one + * of the effects in our experiments. + * + * Luckily, forking aggregates the results across several JVM launches. + */ + + /* + * In order to introduce readily measurable run-to-run variance, we build + * the workload which performance differs from run to run. Note that many workloads + * will have the similar behavior, but we do that artificially to make a point. + */ + + @State(Scope.Thread) + public static class SleepyState { + public long sleepTime; + + @Setup + public void setup() { + sleepTime = (long) (Math.random() * 1000); + } + } + + /* + * Now, we will run this different number of times. + */ + + @Benchmark + @Fork(1) + public void baseline(SleepyState s) throws InterruptedException { + TimeUnit.MILLISECONDS.sleep(s.sleepTime); + } + + @Benchmark + @Fork(5) + public void fork_1(SleepyState s) throws InterruptedException { + TimeUnit.MILLISECONDS.sleep(s.sleepTime); + } + + @Benchmark + @Fork(20) + public void fork_2(SleepyState s) throws InterruptedException { + TimeUnit.MILLISECONDS.sleep(s.sleepTime); + } + + /* + * ============================== HOW TO RUN THIS TEST: ==================================== + * + * Note the baseline is random within [0..1000] msec; and both forked runs + * are estimating the average 500 msec with some confidence. + * + * You can run this test: + * + * a) Via the command line: + * $ mvn clean install + * $ java -jar target/benchmarks.jar JMHSample_13 -wi 0 -i 3 + * (we requested no warmup, 3 measurement iterations; there are also other options, see -h) + * + * b) Via the Java API: + * (see the JMH homepage for possible caveats when running from IDE: + * http://openjdk.java.net/projects/code-tools/jmh/) + */ + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(JMHSample_13_RunToRun.class.getSimpleName()) + .warmupIterations(0) + .measurementIterations(3) + .build(); + + new Runner(opt).run(); + } + +} diff --git a/test/src/test/java/jmh/samples/JMHSample_15_Asymmetric.java b/test/src/test/java/jmh/samples/JMHSample_15_Asymmetric.java new file mode 100644 index 00000000..3bc5d9d5 --- /dev/null +++ b/test/src/test/java/jmh/samples/JMHSample_15_Asymmetric.java @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2014, Oracle America, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of Oracle nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package jmh.samples; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Group; +import org.openjdk.jmh.annotations.GroupThreads; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +@State(Scope.Group) +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +public class JMHSample_15_Asymmetric { + + /* + * So far all the tests were symmetric: the same code was executed in all the threads. + * At times, you need the asymmetric test. JMH provides this with the notion of @Group, + * which can bind several methods together, and all the threads are distributed among + * the test methods. + * + * Each execution group contains of one or more threads. Each thread within a particular + * execution group executes one of @Group-annotated @Benchmark methods. Multiple execution + * groups may participate in the run. The total thread count in the run is rounded to the + * execution group size, which will only allow the full execution groups. + * + * Note that two state scopes: Scope.Benchmark and Scope.Thread are not covering all + * the use cases here -- you either share everything in the state, or share nothing. + * To break this, we have the middle ground Scope.Group, which marks the state to be + * shared within the execution group, but not among the execution groups. + * + * Putting this all together, the example below means: + * a) define the execution group "g", with 3 threads executing inc(), and 1 thread + * executing get(), 4 threads per group in total; + * b) if we run this test case with 4 threads, then we will have a single execution + * group. Generally, running with 4*N threads will create N execution groups, etc.; + * c) each execution group has one @State instance to share: that is, execution groups + * share the counter within the group, but not across the groups. + */ + + private AtomicInteger counter; + + @Setup + public void up() { + counter = new AtomicInteger(); + } + + @Benchmark + @Group("g") + @GroupThreads(3) + public int inc() { + return counter.incrementAndGet(); + } + + @Benchmark + @Group("g") + @GroupThreads(1) + public int get() { + return counter.get(); + } + + /* + * ============================== HOW TO RUN THIS TEST: ==================================== + * + * You will have the distinct metrics for inc() and get() from this run. + * + * You can run this test: + * + * a) Via the command line: + * $ mvn clean install + * $ java -jar target/benchmarks.jar JMHSample_15 -f 1 + * (we requested single fork; there are also other options, see -h) + * + * b) Via the Java API: + * (see the JMH homepage for possible caveats when running from IDE: + * http://openjdk.java.net/projects/code-tools/jmh/) + */ + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(JMHSample_15_Asymmetric.class.getSimpleName()) + .forks(1) + .build(); + + new Runner(opt).run(); + } + +} diff --git a/test/src/test/java/jmh/samples/JMHSample_16_CompilerControl.java b/test/src/test/java/jmh/samples/JMHSample_16_CompilerControl.java new file mode 100644 index 00000000..b650f2d5 --- /dev/null +++ b/test/src/test/java/jmh/samples/JMHSample_16_CompilerControl.java @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2014, Oracle America, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of Oracle nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package jmh.samples; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.CompilerControl; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +import java.util.concurrent.TimeUnit; + +@State(Scope.Thread) +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +public class JMHSample_16_CompilerControl { + + /* + * We can use HotSpot-specific functionality to tell the compiler what + * do we want to do with particular methods. To demonstrate the effects, + * we end up with 3 methods in this sample. + */ + + /** + * These are our targets: + * - first method is prohibited from inlining + * - second method is forced to inline + * - third method is prohibited from compiling + * + * We might even place the annotations directly to the benchmarked + * methods, but this expresses the intent more clearly. + */ + + public void target_blank() { + // this method was intentionally left blank + } + + @CompilerControl(CompilerControl.Mode.DONT_INLINE) + public void target_dontInline() { + // this method was intentionally left blank + } + + @CompilerControl(CompilerControl.Mode.INLINE) + public void target_inline() { + // this method was intentionally left blank + } + + @CompilerControl(CompilerControl.Mode.EXCLUDE) + public void target_exclude() { + // this method was intentionally left blank + } + + /* + * These method measures the calls performance. + */ + + @Benchmark + public void baseline() { + // this method was intentionally left blank + } + + @Benchmark + public void blank() { + target_blank(); + } + + @Benchmark + public void dontinline() { + target_dontInline(); + } + + @Benchmark + public void inline() { + target_inline(); + } + + @Benchmark + public void exclude() { + target_exclude(); + } + + /* + * ============================== HOW TO RUN THIS TEST: ==================================== + * + * Note the performance of the baseline, blank, and inline methods are the same. + * dontinline differs a bit, because we are making the proper call. + * exclude is severely slower, becase we are not compiling it at all. + * + * You can run this test: + * + * a) Via the command line: + * $ mvn clean install + * $ java -jar target/benchmarks.jar JMHSample_16 -wi 0 -i 3 -f 1 + * (we requested no warmup iterations, 3 iterations, single fork; there are also other options, see -h) + * + * b) Via the Java API: + * (see the JMH homepage for possible caveats when running from IDE: + * http://openjdk.java.net/projects/code-tools/jmh/) + */ + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(JMHSample_16_CompilerControl.class.getSimpleName()) + .warmupIterations(0) + .measurementIterations(3) + .forks(1) + .build(); + + new Runner(opt).run(); + } + +} diff --git a/test/src/test/java/jmh/samples/JMHSample_17_SyncIterations.java b/test/src/test/java/jmh/samples/JMHSample_17_SyncIterations.java new file mode 100644 index 00000000..84a959fc --- /dev/null +++ b/test/src/test/java/jmh/samples/JMHSample_17_SyncIterations.java @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2014, Oracle America, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of Oracle nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package jmh.samples; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; +import org.openjdk.jmh.runner.options.TimeValue; + +import java.util.concurrent.TimeUnit; + +@State(Scope.Thread) +@OutputTimeUnit(TimeUnit.MILLISECONDS) +public class JMHSample_17_SyncIterations { + + /* + * This is the another thing that is enabled in JMH by default. + * + * Suppose we have this simple benchmark. + */ + + private double src; + + @Benchmark + public double test() { + double s = src; + for (int i = 0; i < 1000; i++) { + s = Math.sin(s); + } + return s; + } + + /* + * It turns out if you run the benchmark with multiple threads, + * the way you start and stop the worker threads seriously affects + * performance. + * + * The natural way would be to park all the threads on some sort + * of barrier, and the let them go "at once". However, that does + * not work: there are no guarantees the worker threads will start + * at the same time, meaning other worker threads are working + * in better conditions, skewing the result. + * + * The better solution would be to introduce bogus iterations, + * ramp up the threads executing the iterations, and then atomically + * shift the system to measuring stuff. The same thing can be done + * during the rampdown. This sounds complicated, but JMH already + * handles that for you. + * + + */ + + /* + * ============================== HOW TO RUN THIS TEST: ==================================== + * + * You will need to oversubscribe the system to make this effect + * clearly visible; however, this effect can also be shown on the + * unsaturated systems.* + * + * Note the performance of -si false version is more flaky, even + * though it is "better". This is the false improvement, granted by + * some of the threads executing in solo. The -si true version more stable + * and coherent. + * + * -si true is enabled by default. + * + * Say, $CPU is the number of CPUs on your machine. + * + * You can run this test with: + * + * a) Via the command line: + * $ mvn clean install + * $ java -jar target/benchmarks.jar JMHSample_17 \ + * -w 1s -r 1s -f 1 -t ${CPU*16} -si {true|false} + * (we requested shorter warmup/measurement iterations, single fork, + * lots of threads, and changeable "synchronize iterations" option) + * + * b) Via the Java API: + * (see the JMH homepage for possible caveats when running from IDE: + * http://openjdk.java.net/projects/code-tools/jmh/) + */ + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(JMHSample_17_SyncIterations.class.getSimpleName()) + .warmupTime(TimeValue.seconds(1)) + .measurementTime(TimeValue.seconds(1)) + .threads(Runtime.getRuntime().availableProcessors()*16) + .forks(1) + .syncIterations(true) // try to switch to "false" + .build(); + + new Runner(opt).run(); + } + +} diff --git a/test/src/test/java/jmh/samples/JMHSample_18_Control.java b/test/src/test/java/jmh/samples/JMHSample_18_Control.java new file mode 100644 index 00000000..9aecad9d --- /dev/null +++ b/test/src/test/java/jmh/samples/JMHSample_18_Control.java @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2014, Oracle America, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of Oracle nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package jmh.samples; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.Group; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.infra.Control; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +import java.util.concurrent.atomic.AtomicBoolean; + +@State(Scope.Group) +public class JMHSample_18_Control { + + /* + * Sometimes you need the tap into the harness mind to get the info + * on the transition change. For this, we have the experimental state object, + * Control, which is updated by JMH as we go. + */ + + /* + * In this example, we want to estimate the ping-pong speed for the simple + * AtomicBoolean. Unfortunately, doing that in naive manner will livelock + * one of the threads, because the executions of ping/pong are not paired + * perfectly. We need the escape hatch to terminate the loop if threads + * are about to leave the measurement. + */ + + public final AtomicBoolean flag = new AtomicBoolean(); + + @Benchmark + @Group("pingpong") + public void ping(Control cnt) { + while (!cnt.stopMeasurement && !flag.compareAndSet(false, true)) { + // this body is intentionally left blank + } + } + + @Benchmark + @Group("pingpong") + public void pong(Control cnt) { + while (!cnt.stopMeasurement && !flag.compareAndSet(true, false)) { + // this body is intentionally left blank + } + } + + /* + * ============================== HOW TO RUN THIS TEST: ==================================== + * + * You can run this test: + * + * a) Via the command line: + * $ mvn clean install + * $ java -jar target/benchmarks.jar JMHSample_18 -t 2 -f 1 + * (we requested 2 threads and single fork; there are also other options, see -h) + * + * b) Via the Java API: + * (see the JMH homepage for possible caveats when running from IDE: + * http://openjdk.java.net/projects/code-tools/jmh/) + */ + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(JMHSample_18_Control.class.getSimpleName()) + .threads(2) + .forks(1) + .build(); + + new Runner(opt).run(); + } + +} diff --git a/test/src/test/java/jmh/samples/JMHSample_20_Annotations.java b/test/src/test/java/jmh/samples/JMHSample_20_Annotations.java new file mode 100644 index 00000000..7d77d78b --- /dev/null +++ b/test/src/test/java/jmh/samples/JMHSample_20_Annotations.java @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2014, Oracle America, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of Oracle nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package jmh.samples; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +import java.util.concurrent.TimeUnit; + +@State(Scope.Thread) +@OutputTimeUnit(TimeUnit.MICROSECONDS) +@Fork(1) +public class JMHSample_20_Annotations { + + double x1 = Math.PI; + + /* + * In addition to all the command line options usable at run time, + * we have the annotations which can provide the reasonable defaults + * for the some of the benchmarks. This is very useful when you are + * dealing with lots of benchmarks, and some of them require + * special treatment. + * + * Annotation can also be placed on class, to have the effect over + * all the benchmark methods in the same class. The rule is, the + * annotation in the closest scope takes the precedence: i.e. + * the method-based annotation overrides class-based annotation, + * etc. + */ + + @Benchmark + @Warmup(iterations = 5, time = 100, timeUnit = TimeUnit.MILLISECONDS) + @Measurement(iterations = 5, time = 100, timeUnit = TimeUnit.MILLISECONDS) + public double measure() { + return Math.log(x1); + } + + /* + * ============================== HOW TO RUN THIS TEST: ==================================== + * + * Note JMH honors the default annotation settings. You can always override + * the defaults via the command line or API. + * + * You can run this test: + * + * a) Via the command line: + * $ mvn clean install + * $ java -jar target/benchmarks.jar JMHSample_20 + * + * b) Via the Java API: + * (see the JMH homepage for possible caveats when running from IDE: + * http://openjdk.java.net/projects/code-tools/jmh/) + */ + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(JMHSample_20_Annotations.class.getSimpleName()) + .build(); + + new Runner(opt).run(); + } + +} diff --git a/test/src/test/java/jmh/samples/JMHSample_21_ConsumeCPU.java b/test/src/test/java/jmh/samples/JMHSample_21_ConsumeCPU.java new file mode 100644 index 00000000..8d2a53f7 --- /dev/null +++ b/test/src/test/java/jmh/samples/JMHSample_21_ConsumeCPU.java @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2014, Oracle America, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of Oracle nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package jmh.samples; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.infra.Blackhole; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +import java.util.concurrent.TimeUnit; + +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +public class JMHSample_21_ConsumeCPU { + + /* + * At times you require the test to burn some of the cycles doing nothing. + * In many cases, you *do* want to burn the cycles instead of waiting. + * + * For these occasions, we have the infrastructure support. Blackholes + * can not only consume the values, but also the time! Run this test + * to get familiar with this part of JMH. + * + * (Note we use static method because most of the use cases are deep + * within the testing code, and propagating blackholes is tedious). + */ + + @Benchmark + public void consume_0000() { + Blackhole.consumeCPU(0); + } + + @Benchmark + public void consume_0001() { + Blackhole.consumeCPU(1); + } + + @Benchmark + public void consume_0002() { + Blackhole.consumeCPU(2); + } + + @Benchmark + public void consume_0004() { + Blackhole.consumeCPU(4); + } + + @Benchmark + public void consume_0008() { + Blackhole.consumeCPU(8); + } + + @Benchmark + public void consume_0016() { + Blackhole.consumeCPU(16); + } + + @Benchmark + public void consume_0032() { + Blackhole.consumeCPU(32); + } + + @Benchmark + public void consume_0064() { + Blackhole.consumeCPU(64); + } + + @Benchmark + public void consume_0128() { + Blackhole.consumeCPU(128); + } + + @Benchmark + public void consume_0256() { + Blackhole.consumeCPU(256); + } + + @Benchmark + public void consume_0512() { + Blackhole.consumeCPU(512); + } + + @Benchmark + public void consume_1024() { + Blackhole.consumeCPU(1024); + } + + /* + * ============================== HOW TO RUN THIS TEST: ==================================== + * + * Note the single token is just a few cycles, and the more tokens + * you request, then more work is spent (almost linearly) + * + * You can run this test: + * + * a) Via the command line: + * $ mvn clean install + * $ java -jar target/benchmarks.jar JMHSample_21 -f 1 + * (we requested single fork; there are also other options, see -h) + * + * b) Via the Java API: + * (see the JMH homepage for possible caveats when running from IDE: + * http://openjdk.java.net/projects/code-tools/jmh/) + */ + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(JMHSample_21_ConsumeCPU.class.getSimpleName()) + .forks(1) + .build(); + + new Runner(opt).run(); + } + +} diff --git a/test/src/test/java/jmh/samples/JMHSample_22_FalseSharing.java b/test/src/test/java/jmh/samples/JMHSample_22_FalseSharing.java new file mode 100644 index 00000000..57066845 --- /dev/null +++ b/test/src/test/java/jmh/samples/JMHSample_22_FalseSharing.java @@ -0,0 +1,268 @@ +/* + * Copyright (c) 2014, Oracle America, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of Oracle nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package jmh.samples; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Group; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +import java.util.concurrent.TimeUnit; + +@BenchmarkMode(Mode.Throughput) +@OutputTimeUnit(TimeUnit.MICROSECONDS) +@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) +@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) +@Fork(5) +public class JMHSample_22_FalseSharing { + + /* + * One of the unusual thing that can bite you back is false sharing. + * If two threads access (and possibly modify) the adjacent values + * in memory, chances are, they are modifying the values on the same + * cache line. This can yield significant (artificial) slowdowns. + * + * JMH helps you to alleviate this: @States are automatically padded. + * This padding does not extend to the State internals though, + * as we will see in this example. You have to take care of this on + * your own. + */ + + /* + * Suppose we have two threads: + * a) innocuous reader which blindly reads its own field + * b) furious writer which updates its own field + */ + + /* + * BASELINE EXPERIMENT: + * Because of the false sharing, both reader and writer will experience + * penalties. + */ + + @State(Scope.Group) + public static class StateBaseline { + int readOnly; + int writeOnly; + } + + @Benchmark + @Group("baseline") + public int reader(StateBaseline s) { + return s.readOnly; + } + + @Benchmark + @Group("baseline") + public void writer(StateBaseline s) { + s.writeOnly++; + } + + /* + * APPROACH 1: PADDING + * + * We can try to alleviate some of the effects with padding. + * This is not versatile because JVMs can freely rearrange the + * field order, even of the same type. + */ + + @State(Scope.Group) + public static class StatePadded { + int readOnly; + int p01, p02, p03, p04, p05, p06, p07, p08; + int p11, p12, p13, p14, p15, p16, p17, p18; + int writeOnly; + int q01, q02, q03, q04, q05, q06, q07, q08; + int q11, q12, q13, q14, q15, q16, q17, q18; + } + + @Benchmark + @Group("padded") + public int reader(StatePadded s) { + return s.readOnly; + } + + @Benchmark + @Group("padded") + public void writer(StatePadded s) { + s.writeOnly++; + } + + /* + * APPROACH 2: CLASS HIERARCHY TRICK + * + * We can alleviate false sharing with this convoluted hierarchy trick, + * using the fact that superclass fields are usually laid out first. + * In this construction, the protected field will be squashed between + * paddings. + + * It is important to use the smallest data type, so that layouter would + * not generate any gaps that can be taken by later protected subclasses + * fields. Depending on the actual field layout of classes that bear the + * protected fields, we might need more padding to account for "lost" + * padding fields pulled into in their superclass gaps. + */ + + public static class StateHierarchy_1 { + int readOnly; + } + + public static class StateHierarchy_2 extends StateHierarchy_1 { + byte p01, p02, p03, p04, p05, p06, p07, p08; + byte p11, p12, p13, p14, p15, p16, p17, p18; + byte p21, p22, p23, p24, p25, p26, p27, p28; + byte p31, p32, p33, p34, p35, p36, p37, p38; + byte p41, p42, p43, p44, p45, p46, p47, p48; + byte p51, p52, p53, p54, p55, p56, p57, p58; + byte p61, p62, p63, p64, p65, p66, p67, p68; + byte p71, p72, p73, p74, p75, p76, p77, p78; + } + + public static class StateHierarchy_3 extends StateHierarchy_2 { + int writeOnly; + } + + public static class StateHierarchy_4 extends StateHierarchy_3 { + byte q01, q02, q03, q04, q05, q06, q07, q08; + byte q11, q12, q13, q14, q15, q16, q17, q18; + byte q21, q22, q23, q24, q25, q26, q27, q28; + byte q31, q32, q33, q34, q35, q36, q37, q38; + byte q41, q42, q43, q44, q45, q46, q47, q48; + byte q51, q52, q53, q54, q55, q56, q57, q58; + byte q61, q62, q63, q64, q65, q66, q67, q68; + byte q71, q72, q73, q74, q75, q76, q77, q78; + } + + @State(Scope.Group) + public static class StateHierarchy extends StateHierarchy_4 { + } + + @Benchmark + @Group("hierarchy") + public int reader(StateHierarchy s) { + return s.readOnly; + } + + @Benchmark + @Group("hierarchy") + public void writer(StateHierarchy s) { + s.writeOnly++; + } + + /* + * APPROACH 3: ARRAY TRICK + * + * This trick relies on the contiguous allocation of an array. + * Instead of placing the fields in the class, we mangle them + * into the array at very sparse offsets. + */ + + @State(Scope.Group) + public static class StateArray { + int[] arr = new int[128]; + } + + @Benchmark + @Group("sparse") + public int reader(StateArray s) { + return s.arr[0]; + } + + @Benchmark + @Group("sparse") + public void writer(StateArray s) { + s.arr[64]++; + } + + /* + * APPROACH 4: + * + * @Contended (since JDK 8): + * Uncomment the annotation if building with JDK 8. + * Remember to flip -XX:-RestrictContended to enable. + */ + + @State(Scope.Group) + public static class StateContended { + int readOnly; + +// @sun.misc.Contended + int writeOnly; + } + + @Benchmark + @Group("contended") + public int reader(StateContended s) { + return s.readOnly; + } + + @Benchmark + @Group("contended") + public void writer(StateContended s) { + s.writeOnly++; + } + + /* + * ============================== HOW TO RUN THIS TEST: ==================================== + * + * Note the slowdowns. + * + * You can run this test: + * + * a) Via the command line: + * $ mvn clean install + * $ java -jar target/benchmarks.jar JMHSample_22 -t $CPU + * + * b) Via the Java API: + * (see the JMH homepage for possible caveats when running from IDE: + * http://openjdk.java.net/projects/code-tools/jmh/) + */ + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(JMHSample_22_FalseSharing.class.getSimpleName()) + .threads(Runtime.getRuntime().availableProcessors()) + .build(); + + new Runner(opt).run(); + } + +} diff --git a/test/src/test/java/jmh/samples/JMHSample_23_AuxCounters.java b/test/src/test/java/jmh/samples/JMHSample_23_AuxCounters.java new file mode 100644 index 00000000..5d37ad7a --- /dev/null +++ b/test/src/test/java/jmh/samples/JMHSample_23_AuxCounters.java @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2014, Oracle America, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of Oracle nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package jmh.samples; + +import org.openjdk.jmh.annotations.AuxCounters; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +import java.util.concurrent.TimeUnit; + +@OutputTimeUnit(TimeUnit.SECONDS) +@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) +@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) +@Fork(1) +public class JMHSample_23_AuxCounters { + + /* + * In some weird cases you need to get the separate throughput/time + * metrics for the benchmarked code depending on the outcome of the + * current code. Trying to accommodate the cases like this, JMH optionally + * provides the special annotation which treats @State objects + * as the object bearing user counters. See @AuxCounters javadoc for + * the limitations. + */ + + @State(Scope.Thread) + @AuxCounters(AuxCounters.Type.OPERATIONS) + public static class OpCounters { + // These fields would be counted as metrics + public int case1; + public int case2; + + // This accessor will also produce a metric + public int total() { + return case1 + case2; + } + } + + @State(Scope.Thread) + @AuxCounters(AuxCounters.Type.EVENTS) + public static class EventCounters { + // This field would be counted as metric + public int wows; + } + + /* + * This code measures the "throughput" in two parts of the branch. + * The @AuxCounters state above holds the counters which we increment + * ourselves, and then let JMH to use their values in the performance + * calculations. + */ + + @Benchmark + public void splitBranch(OpCounters counters) { + if (Math.random() < 0.1) { + counters.case1++; + } else { + counters.case2++; + } + } + + @Benchmark + public void runSETI(EventCounters counters) { + float random = (float) Math.random(); + float wowSignal = (float) Math.PI / 4; + if (random == wowSignal) { + // WOW, that's unusual. + counters.wows++; + } + } + + /* + * ============================== HOW TO RUN THIS TEST: ==================================== + * + * You can run this test: + * + * a) Via the command line: + * $ mvn clean install + * $ java -jar target/benchmarks.jar JMHSample_23 + * + * b) Via the Java API: + * (see the JMH homepage for possible caveats when running from IDE: + * http://openjdk.java.net/projects/code-tools/jmh/) + */ + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(JMHSample_23_AuxCounters.class.getSimpleName()) + .build(); + + new Runner(opt).run(); + } + +} diff --git a/test/src/test/java/jmh/samples/JMHSample_24_Inheritance.java b/test/src/test/java/jmh/samples/JMHSample_24_Inheritance.java new file mode 100644 index 00000000..24567bc9 --- /dev/null +++ b/test/src/test/java/jmh/samples/JMHSample_24_Inheritance.java @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2014, Oracle America, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of Oracle nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package jmh.samples; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +import java.util.concurrent.TimeUnit; + +public class JMHSample_24_Inheritance { + + /* + * In very special circumstances, you might want to provide the benchmark + * body in the (abstract) superclass, and specialize it with the concrete + * pieces in the subclasses. + * + * The rule of thumb is: if some class has @Benchmark method, then all the subclasses + * are also having the "synthetic" @Benchmark method. The caveat is, because we only + * know the type hierarchy during the compilation, it is only possible during + * the same compilation session. That is, mixing in the subclass extending your + * benchmark class *after* the JMH compilation would have no effect. + * + * Note how annotations now have two possible places. The closest annotation + * in the hierarchy wins. + */ + + @BenchmarkMode(Mode.AverageTime) + @Fork(1) + @State(Scope.Thread) + @OutputTimeUnit(TimeUnit.NANOSECONDS) + public static abstract class AbstractBenchmark { + int x; + + @Setup + public void setup() { + x = 42; + } + + @Benchmark + @Warmup(iterations = 5, time = 100, timeUnit = TimeUnit.MILLISECONDS) + @Measurement(iterations = 5, time = 100, timeUnit = TimeUnit.MILLISECONDS) + public double bench() { + return doWork() * doWork(); + } + + protected abstract double doWork(); + } + + public static class BenchmarkLog extends AbstractBenchmark { + @Override + protected double doWork() { + return Math.log(x); + } + } + + public static class BenchmarkSin extends AbstractBenchmark { + @Override + protected double doWork() { + return Math.sin(x); + } + } + + public static class BenchmarkCos extends AbstractBenchmark { + @Override + protected double doWork() { + return Math.cos(x); + } + } + + /* + * ============================== HOW TO RUN THIS TEST: ==================================== + * + * You can run this test, and observe the three distinct benchmarks running the squares + * of Math.log, Math.sin, and Math.cos, accordingly. + * + * a) Via the command line: + * $ mvn clean install + * $ java -jar target/benchmarks.jar JMHSample_24 + * + * b) Via the Java API: + * (see the JMH homepage for possible caveats when running from IDE: + * http://openjdk.java.net/projects/code-tools/jmh/) + */ + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(JMHSample_24_Inheritance.class.getSimpleName()) + .build(); + + new Runner(opt).run(); + } + +} diff --git a/test/src/test/java/jmh/samples/JMHSample_25_API_GA.java b/test/src/test/java/jmh/samples/JMHSample_25_API_GA.java new file mode 100644 index 00000000..5b9c56b4 --- /dev/null +++ b/test/src/test/java/jmh/samples/JMHSample_25_API_GA.java @@ -0,0 +1,332 @@ +/* + * Copyright (c) 2014, Oracle America, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of Oracle nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package jmh.samples; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.results.Result; +import org.openjdk.jmh.results.RunResult; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; +import org.openjdk.jmh.runner.options.TimeValue; +import org.openjdk.jmh.runner.options.VerboseMode; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +@State(Scope.Thread) +public class JMHSample_25_API_GA { + + /** + * This example shows the rather convoluted, but fun way to exploit + * JMH API in complex scenarios. Up to this point, we haven't consumed + * the results programmatically, and hence we are missing all the fun. + * + * Let's consider this naive code, which obviously suffers from the + * performance anomalies, since current HotSpot is resistant to make + * the tail-call optimizations. + */ + + private int v; + + @Benchmark + public int test() { + return veryImportantCode(1000, v); + } + + public int veryImportantCode(int d, int v) { + if (d == 0) { + return v; + } else { + return veryImportantCode(d - 1, v); + } + } + + /* + * We could probably make up for the absence of TCO with better inlining + * policy. But hand-tuning the policy requires knowing a lot about VM + * internals. Let's instead construct the layman's genetic algorithm + * which sifts through inlining settings trying to find the better policy. + * + * If you are not familiar with the concept of Genetic Algorithms, + * read the Wikipedia article first: + * http://en.wikipedia.org/wiki/Genetic_algorithm + * + * VM experts can guess which option should be tuned to get the max + * performance. Try to run the sample and see if it improves performance. + */ + + public static void main(String[] args) throws RunnerException { + // These are our base options. We will mix these options into the + // measurement runs. That is, all measurement runs will inherit these, + // see how it's done below. + Options baseOpts = new OptionsBuilder() + .include(JMHSample_25_API_GA.class.getName()) + .warmupTime(TimeValue.milliseconds(200)) + .measurementTime(TimeValue.milliseconds(200)) + .warmupIterations(5) + .measurementIterations(5) + .forks(1) + .verbosity(VerboseMode.SILENT) + .build(); + + // Initial population + Population pop = new Population(); + final int POPULATION = 10; + for (int c = 0; c < POPULATION; c++) { + pop.addChromosome(new Chromosome(baseOpts)); + } + + // Make a few rounds of optimization: + final int GENERATIONS = 100; + for (int g = 0; g < GENERATIONS; g++) { + System.out.println("Entering generation " + g); + + // Get the baseline score. + // We opt to remeasure it in order to get reliable current estimate. + RunResult runner = new Runner(baseOpts).runSingle(); + Result baseResult = runner.getPrimaryResult(); + + // Printing a nice table... + System.out.println("---------------------------------------"); + System.out.printf("Baseline score: %10.2f %s%n", + baseResult.getScore(), + baseResult.getScoreUnit() + ); + + for (Chromosome c : pop.getAll()) { + System.out.printf("%10.2f %s (%+10.2f%%) %s%n", + c.getScore(), + baseResult.getScoreUnit(), + (c.getScore() / baseResult.getScore() - 1) * 100, + c.toString() + ); + } + System.out.println(); + + Population newPop = new Population(); + + // Copy out elite solutions + final int ELITE = 2; + for (Chromosome c : pop.getAll().subList(0, ELITE)) { + newPop.addChromosome(c); + } + + // Cross-breed the rest of new population + while (newPop.size() < pop.size()) { + Chromosome p1 = pop.selectToBreed(); + Chromosome p2 = pop.selectToBreed(); + + newPop.addChromosome(p1.crossover(p2).mutate()); + newPop.addChromosome(p2.crossover(p1).mutate()); + } + + pop = newPop; + } + + } + + /** + * Population. + */ + public static class Population { + private final List list = new ArrayList<>(); + + public void addChromosome(Chromosome c) { + list.add(c); + Collections.sort(list); + } + + /** + * Select the breeding material. + * Solutions with better score have better chance to be selected. + * @return breed + */ + public Chromosome selectToBreed() { + double totalScore = 0D; + for (Chromosome c : list) { + totalScore += c.score(); + } + + double thresh = Math.random() * totalScore; + for (Chromosome c : list) { + if (thresh < 0) return c; + thresh =- c.score(); + } + + throw new IllegalStateException("Can not choose"); + } + + public int size() { + return list.size(); + } + + public List getAll() { + return list; + } + } + + /** + * Chromosome: encodes solution. + */ + public static class Chromosome implements Comparable { + + // Current score is not yet computed. + double score = Double.NEGATIVE_INFINITY; + + // Base options to mix in + final Options baseOpts; + + // These are current HotSpot defaults. + int freqInlineSize = 325; + int inlineSmallCode = 1000; + int maxInlineLevel = 9; + int maxInlineSize = 35; + int maxRecursiveInlineLevel = 1; + int minInliningThreshold = 250; + + public Chromosome(Options baseOpts) { + this.baseOpts = baseOpts; + } + + public double score() { + if (score != Double.NEGATIVE_INFINITY) { + // Already got the score, shortcutting + return score; + } + + try { + // Add the options encoded by this solution: + // a) Mix in base options. + // b) Add JVM arguments: we opt to parse the + // stringly representation to make the example + // shorter. There are, of course, cleaner ways + // to do this. + Options theseOpts = new OptionsBuilder() + .parent(baseOpts) + .jvmArgs(toString().split("[ ]")) + .build(); + + // Run through JMH and get the result back. + RunResult runResult = new Runner(theseOpts).runSingle(); + score = runResult.getPrimaryResult().getScore(); + } catch (RunnerException e) { + // Something went wrong, the solution is defective + score = Double.MIN_VALUE; + } + + return score; + } + + @Override + public int compareTo(Chromosome o) { + // Order by score, descending. + return -Double.compare(score(), o.score()); + } + + @Override + public String toString() { + return "-XX:FreqInlineSize=" + freqInlineSize + + " -XX:InlineSmallCode=" + inlineSmallCode + + " -XX:MaxInlineLevel=" + maxInlineLevel + + " -XX:MaxInlineSize=" + maxInlineSize + + " -XX:MaxRecursiveInlineLevel=" + maxRecursiveInlineLevel + + " -XX:MinInliningThreshold=" + minInliningThreshold; + } + + public Chromosome crossover(Chromosome other) { + // Perform crossover: + // While this is a very naive way to perform crossover, it still works. + + final double CROSSOVER_PROB = 0.1; + + Chromosome result = new Chromosome(baseOpts); + + result.freqInlineSize = (Math.random() < CROSSOVER_PROB) ? + this.freqInlineSize : other.freqInlineSize; + + result.inlineSmallCode = (Math.random() < CROSSOVER_PROB) ? + this.inlineSmallCode : other.inlineSmallCode; + + result.maxInlineLevel = (Math.random() < CROSSOVER_PROB) ? + this.maxInlineLevel : other.maxInlineLevel; + + result.maxInlineSize = (Math.random() < CROSSOVER_PROB) ? + this.maxInlineSize : other.maxInlineSize; + + result.maxRecursiveInlineLevel = (Math.random() < CROSSOVER_PROB) ? + this.maxRecursiveInlineLevel : other.maxRecursiveInlineLevel; + + result.minInliningThreshold = (Math.random() < CROSSOVER_PROB) ? + this.minInliningThreshold : other.minInliningThreshold; + + return result; + } + + public Chromosome mutate() { + // Perform mutation: + // Again, this is a naive way to do mutation, but it still works. + + Chromosome result = new Chromosome(baseOpts); + + result.freqInlineSize = (int) randomChange(freqInlineSize); + result.inlineSmallCode = (int) randomChange(inlineSmallCode); + result.maxInlineLevel = (int) randomChange(maxInlineLevel); + result.maxInlineSize = (int) randomChange(maxInlineSize); + result.maxRecursiveInlineLevel = (int) randomChange(maxRecursiveInlineLevel); + result.minInliningThreshold = (int) randomChange(minInliningThreshold); + + return result; + } + + private double randomChange(double v) { + final double MUTATE_PROB = 0.5; + if (Math.random() < MUTATE_PROB) { + if (Math.random() < 0.5) { + return v / (Math.random() * 2); + } else { + return v * (Math.random() * 2); + } + } else { + return v; + } + } + + public double getScore() { + return score; + } + } + +} diff --git a/test/src/test/java/jmh/samples/JMHSample_26_BatchSize.java b/test/src/test/java/jmh/samples/JMHSample_26_BatchSize.java new file mode 100644 index 00000000..1b4dea9e --- /dev/null +++ b/test/src/test/java/jmh/samples/JMHSample_26_BatchSize.java @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2014, Oracle America, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of Oracle nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package jmh.samples; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Level; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +import java.util.LinkedList; +import java.util.List; + +@State(Scope.Thread) +public class JMHSample_26_BatchSize { + + /* + * Sometimes you need to evaluate operation which doesn't have + * the steady state. The cost of a benchmarked operation may + * significantly vary from invocation to invocation. + * + * In this case, using the timed measurements is not a good idea, + * and the only acceptable benchmark mode is a single shot. On the + * other hand, the operation may be too small for reliable single + * shot measurement. + * + * We can use "batch size" parameter to describe the number of + * @Benchmark invocations to do per one "shot" without looping the method + * manually and protect from problems described in JMHSample_11_Loops. + * If there are any @Setup/@TearDown(Level.Invocation), they still run + * per each @Benchmark invocation. + */ + + /* + * Suppose we want to measure insertion in the middle of the list. + */ + + List list = new LinkedList<>(); + + @Benchmark + @Warmup(iterations = 5, time = 1) + @Measurement(iterations = 5, time = 1) + @BenchmarkMode(Mode.AverageTime) + public List measureWrong_1() { + list.add(list.size() / 2, "something"); + return list; + } + + @Benchmark + @Warmup(iterations = 5, time = 5) + @Measurement(iterations = 5, time = 5) + @BenchmarkMode(Mode.AverageTime) + public List measureWrong_5() { + list.add(list.size() / 2, "something"); + return list; + } + + /* + * This is what you do with JMH. + */ + @Benchmark + @Warmup(iterations = 5, batchSize = 5000) + @Measurement(iterations = 5, batchSize = 5000) + @BenchmarkMode(Mode.SingleShotTime) + public List measureRight() { + list.add(list.size() / 2, "something"); + return list; + } + + @Setup(Level.Iteration) + public void setup(){ + list.clear(); + } + + /* + * ============================== HOW TO RUN THIS TEST: ==================================== + * + * You can see completely different results for measureWrong_1 and measureWrong_5; this + * is because the workload has no steady state. The result of the workload is dependent + * on the measurement time. measureRight does not have this drawback, because it measures + * the N invocations of the test method and measures it's time. + * + * We measure batch of 5000 invocations and consider the batch as the single operation. + * + * You can run this test: + * + * a) Via the command line: + * $ mvn clean install + * $ java -jar target/benchmarks.jar JMHSample_26 -f 1 + * + * b) Via the Java API: + * (see the JMH homepage for possible caveats when running from IDE: + * http://openjdk.java.net/projects/code-tools/jmh/) + */ + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(JMHSample_26_BatchSize.class.getSimpleName()) + .forks(1) + .build(); + + new Runner(opt).run(); + } + +} diff --git a/test/src/test/java/jmh/samples/JMHSample_27_Params.java b/test/src/test/java/jmh/samples/JMHSample_27_Params.java new file mode 100644 index 00000000..4b2ca850 --- /dev/null +++ b/test/src/test/java/jmh/samples/JMHSample_27_Params.java @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2014, Oracle America, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of Oracle nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package jmh.samples; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +import java.math.BigInteger; +import java.util.concurrent.TimeUnit; + +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) +@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) +@Fork(1) +@State(Scope.Benchmark) +public class JMHSample_27_Params { + + /** + * In many cases, the experiments require walking the configuration space + * for a benchmark. This is needed for additional control, or investigating + * how the workload performance changes with different settings. + */ + + @Param({"1", "31", "65", "101", "103"}) + public int arg; + + @Param({"0", "1", "2", "4", "8", "16", "32"}) + public int certainty; + + @Benchmark + public boolean bench() { + return BigInteger.valueOf(arg).isProbablePrime(certainty); + } + + /* + * ============================== HOW TO RUN THIS TEST: ==================================== + * + * Note the performance is different with different parameters. + * + * You can run this test: + * + * a) Via the command line: + * $ mvn clean install + * $ java -jar target/benchmarks.jar JMHSample_27 + * + * You can juggle parameters through the command line, e.g. with "-p arg=41,42" + * + * b) Via the Java API: + * (see the JMH homepage for possible caveats when running from IDE: + * http://openjdk.java.net/projects/code-tools/jmh/) + */ + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(JMHSample_27_Params.class.getSimpleName()) +// .param("arg", "41", "42") // Use this to selectively constrain/override parameters + .build(); + + new Runner(opt).run(); + } + +} diff --git a/test/src/test/java/jmh/samples/JMHSample_28_BlackholeHelpers.java b/test/src/test/java/jmh/samples/JMHSample_28_BlackholeHelpers.java new file mode 100644 index 00000000..2fecce4b --- /dev/null +++ b/test/src/test/java/jmh/samples/JMHSample_28_BlackholeHelpers.java @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2014, Oracle America, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of Oracle nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package jmh.samples; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.infra.Blackhole; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +import java.util.concurrent.TimeUnit; + +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) +@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) +@Fork(1) +@State(Scope.Thread) +public class JMHSample_28_BlackholeHelpers { + + /** + * Sometimes you need the black hole not in @Benchmark method, but in + * helper methods, because you want to pass it through to the concrete + * implementation which is instantiated in helper methods. In this case, + * you can request the black hole straight in the helper method signature. + * This applies to both @Setup and @TearDown methods, and also to other + * JMH infrastructure objects, like Control. + * + * Below is the variant of {@link jmh.samples.JMHSample_08_DeadCode} + * test, but wrapped in the anonymous classes. + */ + + public interface Worker { + void work(); + } + + private Worker workerBaseline; + private Worker workerRight; + private Worker workerWrong; + + @Setup + public void setup(final Blackhole bh) { + workerBaseline = new Worker() { + double x; + + @Override + public void work() { + // do nothing + } + }; + + workerWrong = new Worker() { + double x; + + @Override + public void work() { + Math.log(x); + } + }; + + workerRight = new Worker() { + double x; + + @Override + public void work() { + bh.consume(Math.log(x)); + } + }; + + } + + @Benchmark + public void baseline() { + workerBaseline.work(); + } + + @Benchmark + public void measureWrong() { + workerWrong.work(); + } + + @Benchmark + public void measureRight() { + workerRight.work(); + } + + /* + * ============================== HOW TO RUN THIS TEST: ==================================== + * + * You will see measureWrong() running on-par with baseline(). + * Both measureRight() are measuring twice the baseline, so the logs are intact. + * + * You can run this test: + * + * a) Via the command line: + * $ mvn clean install + * $ java -jar target/benchmarks.jar JMHSample_28 + * + * b) Via the Java API: + * (see the JMH homepage for possible caveats when running from IDE: + * http://openjdk.java.net/projects/code-tools/jmh/) + */ + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(JMHSample_28_BlackholeHelpers.class.getSimpleName()) + .build(); + + new Runner(opt).run(); + } + +} diff --git a/test/src/test/java/jmh/samples/JMHSample_29_StatesDAG.java b/test/src/test/java/jmh/samples/JMHSample_29_StatesDAG.java new file mode 100644 index 00000000..16fd61f4 --- /dev/null +++ b/test/src/test/java/jmh/samples/JMHSample_29_StatesDAG.java @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2014, Oracle America, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of Oracle nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package jmh.samples; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.TearDown; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; +import java.util.concurrent.TimeUnit; + +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) +@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) +@Fork(1) +@State(Scope.Thread) +public class JMHSample_29_StatesDAG { + + /** + * WARNING: + * THIS IS AN EXPERIMENTAL FEATURE, BE READY FOR IT BECOME REMOVED WITHOUT NOTICE! + */ + + /** + * There are weird cases when the benchmark state is more cleanly described + * by the set of @States, and those @States reference each other. JMH allows + * linking @States in directed acyclic graphs (DAGs) by referencing @States + * in helper method signatures. (Note that {@link jmh.samples.JMHSample_28_BlackholeHelpers} + * is just a special case of that. + * + * Following the interface for @Benchmark calls, all @Setups for + * referenced @State-s are fired before it becomes accessible to current @State. + * Similarly, no @TearDown methods are fired for referenced @State before + * current @State is done with it. + */ + + /* + * This is a model case, and it might not be a good benchmark. + * // TODO: Replace it with the benchmark which does something useful. + */ + + public static class Counter { + int x; + + public int inc() { + return x++; + } + + public void dispose() { + // pretend this is something really useful + } + } + + /* + * Shared state maintains the set of Counters, and worker threads should + * poll their own instances of Counter to work with. However, it should only + * be done once, and therefore, Local state caches it after requesting the + * counter from Shared state. + */ + + @State(Scope.Benchmark) + public static class Shared { + List all; + Queue available; + + @Setup + public synchronized void setup() { + all = new ArrayList<>(); + for (int c = 0; c < 10; c++) { + all.add(new Counter()); + } + + available = new LinkedList<>(); + available.addAll(all); + } + + @TearDown + public synchronized void tearDown() { + for (Counter c : all) { + c.dispose(); + } + } + + public synchronized Counter getMine() { + return available.poll(); + } + } + + @State(Scope.Thread) + public static class Local { + Counter cnt; + + @Setup + public void setup(Shared shared) { + cnt = shared.getMine(); + } + } + + @Benchmark + public int test(Local local) { + return local.cnt.inc(); + } + + /* + * ============================== HOW TO RUN THIS TEST: ==================================== + * + * You can run this test: + * + * a) Via the command line: + * $ mvn clean install + * $ java -jar target/benchmarks.jar JMHSample_29 + * + * b) Via the Java API: + * (see the JMH homepage for possible caveats when running from IDE: + * http://openjdk.java.net/projects/code-tools/jmh/) + */ + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(JMHSample_29_StatesDAG.class.getSimpleName()) + .build(); + + new Runner(opt).run(); + } + + +} diff --git a/test/src/test/java/jmh/samples/JMHSample_30_Interrupts.java b/test/src/test/java/jmh/samples/JMHSample_30_Interrupts.java new file mode 100644 index 00000000..1b33befc --- /dev/null +++ b/test/src/test/java/jmh/samples/JMHSample_30_Interrupts.java @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2014, Oracle America, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of Oracle nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package jmh.samples; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Group; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; +import org.openjdk.jmh.runner.options.TimeValue; + +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.TimeUnit; + +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Group) +public class JMHSample_30_Interrupts { + + /* + * JMH can also detect when threads are stuck in the benchmarks, and try + * to forcefully interrupt the benchmark thread. JMH tries to do that + * when it is arguably sure it would not affect the measurement. + */ + + /* + * In this example, we want to measure the simple performance characteristics + * of the ArrayBlockingQueue. Unfortunately, doing that without a harness + * support will deadlock one of the threads, because the executions of + * take/put are not paired perfectly. Fortunately for us, both methods react + * to interrupts well, and therefore we can rely on JMH to terminate the + * measurement for us. JMH will notify users about the interrupt actions + * nevertheless, so users can see if those interrupts affected the measurement. + * JMH will start issuing interrupts after the default or user-specified timeout + * had been reached. + * + * This is a variant of jmh.samples.JMHSample_18_Control, but without + * the explicit control objects. This example is suitable for the methods which + * react to interrupts gracefully. + */ + + private BlockingQueue q; + + @Setup + public void setup() { + q = new ArrayBlockingQueue<>(1); + } + + @Group("Q") + @Benchmark + public Integer take() throws InterruptedException { + return q.take(); + } + + @Group("Q") + @Benchmark + public void put() throws InterruptedException { + q.put(42); + } + + /* + * ============================== HOW TO RUN THIS TEST: ==================================== + * + * You can run this test: + * + * a) Via the command line: + * $ mvn clean install + * $ java -jar target/benchmarks.jar JMHSample_30 -t 2 -f 5 -to 10 + * (we requested 2 threads, 5 forks, and 10 sec timeout) + * + * b) Via the Java API: + * (see the JMH homepage for possible caveats when running from IDE: + * http://openjdk.java.net/projects/code-tools/jmh/) + */ + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(JMHSample_30_Interrupts.class.getSimpleName()) + .threads(2) + .forks(5) + .timeout(TimeValue.seconds(10)) + .build(); + + new Runner(opt).run(); + } + +} diff --git a/test/src/test/java/jmh/samples/JMHSample_31_InfraParams.java b/test/src/test/java/jmh/samples/JMHSample_31_InfraParams.java new file mode 100644 index 00000000..ea3dbe5f --- /dev/null +++ b/test/src/test/java/jmh/samples/JMHSample_31_InfraParams.java @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2014, Oracle America, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of Oracle nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package jmh.samples; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.infra.BenchmarkParams; +import org.openjdk.jmh.infra.ThreadParams; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; + +@BenchmarkMode(Mode.Throughput) +@OutputTimeUnit(TimeUnit.SECONDS) +@State(Scope.Benchmark) +public class JMHSample_31_InfraParams { + + /* + * There is a way to query JMH about the current running mode. This is + * possible with three infrastructure objects we can request to be injected: + * - BenchmarkParams: covers the benchmark-global configuration + * - IterationParams: covers the current iteration configuration + * - ThreadParams: covers the specifics about threading + * + * Suppose we want to check how the ConcurrentHashMap scales under different + * parallelism levels. We can put concurrencyLevel in @Param, but it sometimes + * inconvenient if, say, we want it to follow the @Threads count. Here is + * how we can query JMH about how many threads was requested for the current run, + * and put that into concurrencyLevel argument for CHM constructor. + */ + + static final int THREAD_SLICE = 1000; + + private ConcurrentHashMap mapSingle; + private ConcurrentHashMap mapFollowThreads; + + @Setup + public void setup(BenchmarkParams params) { + int capacity = 16 * THREAD_SLICE * params.getThreads(); + mapSingle = new ConcurrentHashMap<>(capacity, 0.75f, 1); + mapFollowThreads = new ConcurrentHashMap<>(capacity, 0.75f, params.getThreads()); + } + + /* + * Here is another neat trick. Generate the distinct set of keys for all threads: + */ + + @State(Scope.Thread) + public static class Ids { + private List ids; + + @Setup + public void setup(ThreadParams threads) { + ids = new ArrayList<>(); + for (int c = 0; c < THREAD_SLICE; c++) { + ids.add("ID" + (THREAD_SLICE * threads.getThreadIndex() + c)); + } + } + } + + @Benchmark + public void measureDefault(Ids ids) { + for (String s : ids.ids) { + mapSingle.remove(s); + mapSingle.put(s, s); + } + } + + @Benchmark + public void measureFollowThreads(Ids ids) { + for (String s : ids.ids) { + mapFollowThreads.remove(s); + mapFollowThreads.put(s, s); + } + } + + /* + * ============================== HOW TO RUN THIS TEST: ==================================== + * + * You can run this test: + * + * a) Via the command line: + * $ mvn clean install + * $ java -jar target/benchmarks.jar JMHSample_31 -t 4 -f 5 + * (we requested 4 threads, and 5 forks; there are also other options, see -h) + * + * b) Via the Java API: + * (see the JMH homepage for possible caveats when running from IDE: + * http://openjdk.java.net/projects/code-tools/jmh/) + */ + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(JMHSample_31_InfraParams.class.getSimpleName()) + .threads(4) + .forks(5) + .build(); + + new Runner(opt).run(); + } + +} diff --git a/test/src/test/java/jmh/samples/JMHSample_32_BulkWarmup.java b/test/src/test/java/jmh/samples/JMHSample_32_BulkWarmup.java new file mode 100644 index 00000000..4257f028 --- /dev/null +++ b/test/src/test/java/jmh/samples/JMHSample_32_BulkWarmup.java @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2014, Oracle America, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of Oracle nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package jmh.samples; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.CompilerControl; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; +import org.openjdk.jmh.runner.options.WarmupMode; + +import java.util.concurrent.TimeUnit; + +@State(Scope.Thread) +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +public class JMHSample_32_BulkWarmup { + + /* + * This is an addendum to JMHSample_12_Forking test. + * + * Sometimes you want an opposite configuration: instead of separating the profiles + * for different benchmarks, you want to mix them together to test the worst-case + * scenario. + * + * JMH has a bulk warmup feature for that: it does the warmups for all the tests + * first, and then measures them. JMH still forks the JVM for each test, but once the + * new JVM has started, all the warmups are being run there, before running the + * measurement. This helps to dodge the type profile skews, as each test is still + * executed in a different JVM, and we only "mix" the warmup code we want. + */ + + /* + * These test classes are borrowed verbatim from JMHSample_12_Forking. + */ + + public interface Counter { + int inc(); + } + + public static class Counter1 implements Counter { + private int x; + + @Override + public int inc() { + return x++; + } + } + + public static class Counter2 implements Counter { + private int x; + + @Override + public int inc() { + return x++; + } + } + + Counter c1 = new Counter1(); + Counter c2 = new Counter2(); + + /* + * And this is our test payload. Notice we have to break the inlining of the payload, + * so that in could not be inlined in either measure_c1() or measure_c2() below, and + * specialized for that only call. + */ + + @CompilerControl(CompilerControl.Mode.DONT_INLINE) + public int measure(Counter c) { + int s = 0; + for (int i = 0; i < 10; i++) { + s += c.inc(); + } + return s; + } + + @Benchmark + public int measure_c1() { + return measure(c1); + } + + @Benchmark + public int measure_c2() { + return measure(c2); + } + + /* + * ============================== HOW TO RUN THIS TEST: ==================================== + * + * Note how JMH runs the warmups first, and only then a given test. Note how JMH re-warmups + * the JVM for each test. The scores for C1 and C2 cases are equally bad, compare them to + * the scores from JMHSample_12_Forking. + * + * You can run this test: + * + * a) Via the command line: + * $ mvn clean install + * $ java -jar target/benchmarks.jar JMHSample_32 -f 1 -wm BULK + * (we requested a single fork, and bulk warmup mode; there are also other options, see -h) + * + * b) Via the Java API: + * (see the JMH homepage for possible caveats when running from IDE: + * http://openjdk.java.net/projects/code-tools/jmh/) + */ + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(JMHSample_32_BulkWarmup.class.getSimpleName()) + // .includeWarmup(...) <-- this may include other benchmarks into warmup + .warmupMode(WarmupMode.BULK) // see other WarmupMode.* as well + .forks(1) + .build(); + + new Runner(opt).run(); + } + +} diff --git a/test/src/test/java/jmh/samples/JMHSample_33_SecurityManager.java b/test/src/test/java/jmh/samples/JMHSample_33_SecurityManager.java new file mode 100644 index 00000000..8f71b7ad --- /dev/null +++ b/test/src/test/java/jmh/samples/JMHSample_33_SecurityManager.java @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2014, Oracle America, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of Oracle nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package jmh.samples; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.TearDown; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.security.NoSuchAlgorithmException; +import java.security.Policy; +import java.security.URIParameter; +import java.util.concurrent.TimeUnit; + +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +public class JMHSample_33_SecurityManager { + + /* + * Some targeted tests may care about SecurityManager being installed. + * Since JMH itself needs to do privileged actions, it is not enough + * to blindly install the SecurityManager, as JMH infrastructure will fail. + */ + + /* + * In this example, we want to measure the performance of System.getProperty + * with SecurityManager installed or not. To do this, we have two state classes + * with helper methods. One that reads the default JMH security policy (we ship one + * with JMH), and installs the security manager; another one that makes sure + * the SecurityManager is not installed. + * + * If you need a restricted security policy for the tests, you are advised to + * get /jmh-security-minimal.policy, that contains the minimal permissions + * required for JMH benchmark to run, merge the new permissions there, produce new + * policy file in a temporary location, and load that policy file instead. + * There is also /jmh-security-minimal-runner.policy, that contains the minimal + * permissions for the JMH harness to run, if you want to use JVM args to arm + * the SecurityManager. + */ + + @State(Scope.Benchmark) + public static class SecurityManagerInstalled { + @Setup + public void setup() throws IOException, NoSuchAlgorithmException, URISyntaxException { + URI policyFile = JMHSample_33_SecurityManager.class.getResource("/jmh-security.policy").toURI(); + Policy.setPolicy(Policy.getInstance("JavaPolicy", new URIParameter(policyFile))); + System.setSecurityManager(new SecurityManager()); + } + + @TearDown + public void tearDown() { + System.setSecurityManager(null); + } + } + + @State(Scope.Benchmark) + public static class SecurityManagerEmpty { + @Setup + public void setup() throws IOException, NoSuchAlgorithmException, URISyntaxException { + System.setSecurityManager(null); + } + } + + @Benchmark + public String testWithSM(SecurityManagerInstalled s) throws InterruptedException { + return System.getProperty("java.home"); + } + + @Benchmark + public String testWithoutSM(SecurityManagerEmpty s) throws InterruptedException { + return System.getProperty("java.home"); + } + + /* + * ============================== HOW TO RUN THIS TEST: ==================================== + * + * You can run this test: + * + * a) Via the command line: + * $ mvn clean install + * $ java -jar target/benchmarks.jar JMHSample_33 -f 1 + * (we requested 5 warmup iterations, 5 forks; there are also other options, see -h)) + * + * b) Via the Java API: + * (see the JMH homepage for possible caveats when running from IDE: + * http://openjdk.java.net/projects/code-tools/jmh/) + */ + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(JMHSample_33_SecurityManager.class.getSimpleName()) + .warmupIterations(5) + .measurementIterations(5) + .forks(1) + .build(); + + new Runner(opt).run(); + } + +} diff --git a/test/src/test/java/jmh/samples/JMHSample_34_SafeLooping.java b/test/src/test/java/jmh/samples/JMHSample_34_SafeLooping.java new file mode 100644 index 00000000..ac6666fc --- /dev/null +++ b/test/src/test/java/jmh/samples/JMHSample_34_SafeLooping.java @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2014, Oracle America, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of Oracle nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package jmh.samples; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.CompilerControl; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.infra.Blackhole; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +import java.util.concurrent.TimeUnit; + +@State(Scope.Thread) +@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) +@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) +@Fork(3) +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +public class JMHSample_34_SafeLooping { + + /* + * JMHSample_11_Loops warns about the dangers of using loops in @Benchmark methods. + * Sometimes, however, one needs to traverse through several elements in a dataset. + * This is hard to do without loops, and therefore we need to devise a scheme for + * safe looping. + */ + + /* + * Suppose we want to measure how much it takes to execute work() with different + * arguments. This mimics a frequent use case when multiple instances with the same + * implementation, but different data, is measured. + */ + + static final int BASE = 42; + + static int work(int x) { + return BASE + x; + } + + /* + * Every benchmark requires control. We do a trivial control for our benchmarks + * by checking the benchmark costs are growing linearly with increased task size. + * If it doesn't, then something wrong is happening. + */ + + @Param({"1", "10", "100", "1000"}) + int size; + + int[] xs; + + @Setup + public void setup() { + xs = new int[size]; + for (int c = 0; c < size; c++) { + xs[c] = c; + } + } + + /* + * First, the obviously wrong way: "saving" the result into a local variable would not + * work. A sufficiently smart compiler will inline work(), and figure out only the last + * work() call needs to be evaluated. Indeed, if you run it with varying $size, the score + * will stay the same! + */ + + @Benchmark + public int measureWrong_1() { + int acc = 0; + for (int x : xs) { + acc = work(x); + } + return acc; + } + + /* + * Second, another wrong way: "accumulating" the result into a local variable. While + * it would force the computation of each work() method, there are software pipelining + * effects in action, that can merge the operations between two otherwise distinct work() + * bodies. This will obliterate the benchmark setup. + * + * In this example, HotSpot does the unrolled loop, merges the $BASE operands into a single + * addition to $acc, and then does a bunch of very tight stores of $x-s. The final performance + * depends on how much of the loop unrolling happened *and* how much data is available to make + * the large strides. + */ + + @Benchmark + public int measureWrong_2() { + int acc = 0; + for (int x : xs) { + acc += work(x); + } + return acc; + } + + /* + * Now, let's see how to measure these things properly. A very straight-forward way to + * break the merging is to sink each result to Blackhole. This will force runtime to compute + * every work() call in full. (We would normally like to care about several concurrent work() + * computations at once, but the memory effects from Blackhole.consume() prevent those optimization + * on most runtimes). + */ + + @Benchmark + public void measureRight_1(Blackhole bh) { + for (int x : xs) { + bh.consume(work(x)); + } + } + + /* + * DANGEROUS AREA, PLEASE READ THE DESCRIPTION BELOW. + * + * Sometimes, the cost of sinking the value into a Blackhole is dominating the nano-benchmark score. + * In these cases, one may try to do a make-shift "sinker" with non-inlineable method. This trick is + * *very* VM-specific, and can only be used if you are verifying the generated code (that's a good + * strategy when dealing with nano-benchmarks anyway). + * + * You SHOULD NOT use this trick in most cases. Apply only where needed. + */ + + @Benchmark + public void measureRight_2() { + for (int x : xs) { + sink(work(x)); + } + } + + @CompilerControl(CompilerControl.Mode.DONT_INLINE) + public static void sink(int v) { + // IT IS VERY IMPORTANT TO MATCH THE SIGNATURE TO AVOID AUTOBOXING. + // The method intentionally does nothing. + } + + + /* + * ============================== HOW TO RUN THIS TEST: ==================================== + * + * You might notice measureWrong_1 does not depend on $size, measureWrong_2 has troubles with + * linearity, and otherwise much faster than both measureRight_*. You can also see measureRight_2 + * is marginally faster than measureRight_1. + * + * You can run this test: + * + * a) Via the command line: + * $ mvn clean install + * $ java -jar target/benchmarks.jar JMHSample_34 + * + * b) Via the Java API: + * (see the JMH homepage for possible caveats when running from IDE: + * http://openjdk.java.net/projects/code-tools/jmh/) + */ + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(JMHSample_34_SafeLooping.class.getSimpleName()) + .forks(3) + .build(); + + new Runner(opt).run(); + } + +} diff --git a/test/src/test/java/jmh/samples/JMHSample_35_Profilers.java b/test/src/test/java/jmh/samples/JMHSample_35_Profilers.java new file mode 100644 index 00000000..31a7520f --- /dev/null +++ b/test/src/test/java/jmh/samples/JMHSample_35_Profilers.java @@ -0,0 +1,600 @@ +/* + * Copyright (c) 2014, Oracle America, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of Oracle nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package jmh.samples; + +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.infra.Blackhole; +import org.openjdk.jmh.profile.ClassloaderProfiler; +import org.openjdk.jmh.profile.DTraceAsmProfiler; +import org.openjdk.jmh.profile.LinuxPerfProfiler; +import org.openjdk.jmh.profile.StackProfiler; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +import java.net.URL; +import java.net.URLClassLoader; +import java.util.HashMap; +import java.util.Map; +import java.util.TreeMap; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; + +public class JMHSample_35_Profilers { + + /* + * This sample serves as the profiler overview. + * + * JMH has a few very handy profilers that help to understand your benchmarks. While + * these profilers are not the substitute for full-fledged external profilers, in many + * cases, these are handy to quickly dig into the benchmark behavior. When you are + * doing many cycles of tuning up the benchmark code itself, it is important to have + * a quick turnaround for the results. + * + * Use -lprof to list the profilers. There are quite a few profilers, and this sample + * would expand on a handful of most useful ones. Many profilers have their own options, + * usually accessible via -prof :help. + * + * Since profilers are reporting on different things, it is hard to construct a single + * benchmark sample that will show all profilers in action. Therefore, we have a couple + * of benchmarks in this sample. + */ + + /* + * ================================ MAPS BENCHMARK ================================ + */ + + @State(Scope.Thread) + @Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) + @Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) + @Fork(3) + @BenchmarkMode(Mode.AverageTime) + @OutputTimeUnit(TimeUnit.NANOSECONDS) + public static class Maps { + private Map map; + + @Param({"hashmap", "treemap"}) + private String type; + + private int begin; + private int end; + + @Setup + public void setup() { + switch (type) { + case "hashmap": + map = new HashMap<>(); + break; + case "treemap": + map = new TreeMap<>(); + break; + default: + throw new IllegalStateException("Unknown type: " + type); + } + + begin = 1; + end = 256; + for (int i = begin; i < end; i++) { + map.put(i, i); + } + } + + @Benchmark + public void test(Blackhole bh) { + for (int i = begin; i < end; i++) { + bh.consume(map.get(i)); + } + } + + /* + * ============================== HOW TO RUN THIS TEST: ==================================== + * + * You can run this test: + * + * a) Via the command line: + * $ mvn clean install + * $ java -jar target/benchmarks.jar JMHSample_35.*Maps -prof stack + * $ java -jar target/benchmarks.jar JMHSample_35.*Maps -prof gc + * + * b) Via the Java API: + * (see the JMH homepage for possible caveats when running from IDE: + * http://openjdk.java.net/projects/code-tools/jmh/) + */ + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(JMHSample_35_Profilers.Maps.class.getSimpleName()) + .addProfiler(StackProfiler.class) +// .addProfiler(GCProfiler.class) + .build(); + + new Runner(opt).run(); + } + + /* + Running this benchmark will yield something like: + + Benchmark (type) Mode Cnt Score Error Units + JMHSample_35_Profilers.Maps.test hashmap avgt 5 1553.201 ± 6.199 ns/op + JMHSample_35_Profilers.Maps.test treemap avgt 5 5177.065 ± 361.278 ns/op + + Running with -prof stack will yield: + + ....[Thread state: RUNNABLE]........................................................................ + 99.0% 99.0% jmh.samples.JMHSample_35_Profilers$Maps.test + 0.4% 0.4% jmh.samples.generated.JMHSample_35_Profilers_Maps_test.test_avgt_jmhStub + 0.2% 0.2% sun.reflect.NativeMethodAccessorImpl.invoke0 + 0.2% 0.2% java.lang.Integer.valueOf + 0.2% 0.2% sun.misc.Unsafe.compareAndSwapInt + + ....[Thread state: RUNNABLE]........................................................................ + 78.0% 78.0% java.util.TreeMap.getEntry + 21.2% 21.2% jmh.samples.JMHSample_35_Profilers$Maps.test + 0.4% 0.4% java.lang.Integer.valueOf + 0.2% 0.2% sun.reflect.NativeMethodAccessorImpl.invoke0 + 0.2% 0.2% jmh.samples.generated.JMHSample_35_Profilers_Maps_test.test_avgt_jmhStub + + Stack profiler is useful to quickly see if the code we are stressing actually executes. As many other + sampling profilers, it is susceptible for sampling bias: it can fail to notice quickly executing methods, + for example. In the benchmark above, it does not notice HashMap.get. + + Next up, GC profiler. Running with -prof gc will yield: + + Benchmark (type) Mode Cnt Score Error Units + + JMHSample_35_Profilers.Maps.test hashmap avgt 5 1553.201 ± 6.199 ns/op + JMHSample_35_Profilers.Maps.test:·gc.alloc.rate hashmap avgt 5 1257.046 ± 5.675 MB/sec + JMHSample_35_Profilers.Maps.test:·gc.alloc.rate.norm hashmap avgt 5 2048.001 ± 0.001 B/op + JMHSample_35_Profilers.Maps.test:·gc.churn.PS_Eden_Space hashmap avgt 5 1259.148 ± 315.277 MB/sec + JMHSample_35_Profilers.Maps.test:·gc.churn.PS_Eden_Space.norm hashmap avgt 5 2051.519 ± 520.324 B/op + JMHSample_35_Profilers.Maps.test:·gc.churn.PS_Survivor_Space hashmap avgt 5 0.175 ± 0.386 MB/sec + JMHSample_35_Profilers.Maps.test:·gc.churn.PS_Survivor_Space.norm hashmap avgt 5 0.285 ± 0.629 B/op + JMHSample_35_Profilers.Maps.test:·gc.count hashmap avgt 5 29.000 counts + JMHSample_35_Profilers.Maps.test:·gc.time hashmap avgt 5 16.000 ms + + JMHSample_35_Profilers.Maps.test treemap avgt 5 5177.065 ± 361.278 ns/op + JMHSample_35_Profilers.Maps.test:·gc.alloc.rate treemap avgt 5 377.251 ± 26.188 MB/sec + JMHSample_35_Profilers.Maps.test:·gc.alloc.rate.norm treemap avgt 5 2048.003 ± 0.001 B/op + JMHSample_35_Profilers.Maps.test:·gc.churn.PS_Eden_Space treemap avgt 5 392.743 ± 174.156 MB/sec + JMHSample_35_Profilers.Maps.test:·gc.churn.PS_Eden_Space.norm treemap avgt 5 2131.767 ± 913.941 B/op + JMHSample_35_Profilers.Maps.test:·gc.churn.PS_Survivor_Space treemap avgt 5 0.131 ± 0.215 MB/sec + JMHSample_35_Profilers.Maps.test:·gc.churn.PS_Survivor_Space.norm treemap avgt 5 0.709 ± 1.125 B/op + JMHSample_35_Profilers.Maps.test:·gc.count treemap avgt 5 25.000 counts + JMHSample_35_Profilers.Maps.test:·gc.time treemap avgt 5 26.000 ms + + There, we can see that the tests are producing quite some garbage. "gc.alloc" would say we are allocating 1257 + and 377 MB of objects per second, or 2048 bytes per benchmark operation. "gc.churn" would say that GC removes + the same amount of garbage from Eden space every second. In other words, we are producing 2048 bytes of garbage per + benchmark operation. + + If you look closely at the test, you can get a (correct) hypothesis this is due to Integer autoboxing. + + Note that "gc.alloc" counters generally produce more accurate data, but they can also fail when threads come and + go over the course of the benchmark. "gc.churn" values are updated on each GC event, and so if you want a more accurate + data, running longer and/or with small heap would help. But anyhow, always cross-reference "gc.alloc" and "gc.churn" + values with each other to get a complete picture. + + It is also worth noticing that non-normalized counters are dependent on benchmark performance! Here, "treemap" + tests are 3x slower, and thus both allocation and churn rates are also comparably lower. It is often useful to look + into non-normalized counters to see if the test is allocation/GC-bound (figure the allocation pressure "ceiling" + for your configuration!), and normalized counters to see the more precise benchmark behavior. + + As most profilers, both "stack" and "gc" profile are able to aggregate samples from multiple forks. It is a good + idea to run multiple forks with the profilers enabled, as it improves results error estimates. + */ + } + + /* + * ================================ CLASSLOADER BENCHMARK ================================ + */ + + + @State(Scope.Thread) + @Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) + @Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) + @Fork(3) + @BenchmarkMode(Mode.AverageTime) + @OutputTimeUnit(TimeUnit.NANOSECONDS) + public static class Classy { + + /** + * Our own crippled classloader, that can only load a simple class over and over again. + */ + public static class XLoader extends URLClassLoader { + private static final byte[] X_BYTECODE = new byte[]{ + (byte) 0xCA, (byte) 0xFE, (byte) 0xBA, (byte) 0xBE, 0x00, 0x00, 0x00, 0x34, 0x00, 0x0D, 0x0A, 0x00, 0x03, 0x00, + 0x0A, 0x07, 0x00, 0x0B, 0x07, 0x00, 0x0C, 0x01, 0x00, 0x06, 0x3C, 0x69, 0x6E, 0x69, 0x74, 0x3E, 0x01, 0x00, 0x03, + 0x28, 0x29, 0x56, 0x01, 0x00, 0x04, 0x43, 0x6F, 0x64, 0x65, 0x01, 0x00, 0x0F, 0x4C, 0x69, 0x6E, 0x65, 0x4E, 0x75, + 0x6D, 0x62, 0x65, 0x72, 0x54, 0x61, 0x62, 0x6C, 0x65, 0x01, 0x00, 0x0A, 0x53, 0x6F, 0x75, 0x72, 0x63, 0x65, 0x46, + 0x69, 0x6C, 0x65, 0x01, 0x00, 0x06, 0x58, 0x2E, 0x6A, 0x61, 0x76, 0x61, 0x0C, 0x00, 0x04, 0x00, 0x05, 0x01, 0x00, + 0x01, 0x58, 0x01, 0x00, 0x10, 0x6A, 0x61, 0x76, 0x61, 0x2F, 0x6C, 0x61, 0x6E, 0x67, 0x2F, 0x4F, 0x62, 0x6A, 0x65, + 0x63, 0x74, 0x00, 0x20, 0x00, 0x02, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x05, 0x00, 0x01, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x2A, + (byte) 0xB7, 0x00, 0x01, (byte) 0xB1, 0x00, 0x00, 0x00, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, 0x00, 0x09, + }; + + public XLoader() { + super(new URL[0], ClassLoader.getSystemClassLoader()); + } + + @Override + protected Class findClass(final String name) throws ClassNotFoundException { + return defineClass(name, X_BYTECODE, 0, X_BYTECODE.length); + } + + } + + @Benchmark + public Class load() throws ClassNotFoundException { + return Class.forName("X", true, new XLoader()); + } + + /* + * ============================== HOW TO RUN THIS TEST: ==================================== + * + * You can run this test: + * + * a) Via the command line: + * $ mvn clean install + * $ java -jar target/benchmarks.jar JMHSample_35.*Classy -prof cl + * $ java -jar target/benchmarks.jar JMHSample_35.*Classy -prof comp + * + * b) Via the Java API: + * (see the JMH homepage for possible caveats when running from IDE: + * http://openjdk.java.net/projects/code-tools/jmh/) + */ + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(JMHSample_35_Profilers.Classy.class.getSimpleName()) + .addProfiler(ClassloaderProfiler.class) +// .addProfiler(CompilerProfiler.class) + .build(); + + new Runner(opt).run(); + } + + /* + Running with -prof cl will yield: + + Benchmark Mode Cnt Score Error Units + JMHSample_35_Profilers.Classy.load avgt 15 34215.363 ± 545.892 ns/op + JMHSample_35_Profilers.Classy.load:·class.load avgt 15 29374.097 ± 716.743 classes/sec + JMHSample_35_Profilers.Classy.load:·class.load.norm avgt 15 1.000 ± 0.001 classes/op + JMHSample_35_Profilers.Classy.load:·class.unload avgt 15 29598.233 ± 3420.181 classes/sec + JMHSample_35_Profilers.Classy.load:·class.unload.norm avgt 15 1.008 ± 0.119 classes/op + + Here, we can see the benchmark indeed load class per benchmark op, and this adds up to more than 29K classloads + per second. We can also see the runtime is able to successfully keep the number of loaded classes at bay, + since the class unloading happens at the same rate. + + This profiler is handy when doing the classloading performance work, because it says if the classes + were actually loaded, and not reused across the Class.forName calls. It also helps to see if the benchmark + performs any classloading in the measurement phase. For example, if you have non-classloading benchmark, + you would expect these metrics be zero. + + Another useful profiler that could tell if compiler is doing a heavy work in background, and thus interfering + with measurement, -prof comp: + + Benchmark Mode Cnt Score Error Units + JMHSample_35_Profilers.Classy.load avgt 5 33523.875 ± 3026.025 ns/op + JMHSample_35_Profilers.Classy.load:·compiler.time.profiled avgt 5 5.000 ms + JMHSample_35_Profilers.Classy.load:·compiler.time.total avgt 5 479.000 ms + + We seem to be at proper steady state: out of 479 ms of total compiler work, only 5 ms happen during the + measurement window. It is expected to have some level of background compilation even at steady state. + + As most profilers, both "cl" and "comp" are able to aggregate samples from multiple forks. It is a good + idea to run multiple forks with the profilers enabled, as it improves results error estimates. + */ + } + + /* + * ================================ ATOMIC LONG BENCHMARK ================================ + */ + + @State(Scope.Benchmark) + @Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) + @Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) + @Fork(1) + @BenchmarkMode(Mode.AverageTime) + @OutputTimeUnit(TimeUnit.NANOSECONDS) + public static class Atomic { + private AtomicLong n; + + @Setup + public void setup() { + n = new AtomicLong(); + } + + @Benchmark + public long test() { + return n.incrementAndGet(); + } + + /* + * ============================== HOW TO RUN THIS TEST: ==================================== + * + * You can run this test: + * + * a) Via the command line: + * $ mvn clean install + * $ java -jar target/benchmarks.jar JMHSample_35.*Atomic -prof perf -f 1 (Linux) + * $ java -jar target/benchmarks.jar JMHSample_35.*Atomic -prof perfnorm -f 3 (Linux) + * $ java -jar target/benchmarks.jar JMHSample_35.*Atomic -prof perfasm -f 1 (Linux) + * $ java -jar target/benchmarks.jar JMHSample_35.*Atomic -prof xperfasm -f 1 (Windows) + * $ java -jar target/benchmarks.jar JMHSample_35.*Atomic -prof dtraceasm -f 1 (Mac OS X) + * b) Via the Java API: + * (see the JMH homepage for possible caveats when running from IDE: + * http://openjdk.java.net/projects/code-tools/jmh/) + */ + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(JMHSample_35_Profilers.Atomic.class.getSimpleName()) + .addProfiler(LinuxPerfProfiler.class) +// .addProfiler(LinuxPerfNormProfiler.class) +// .addProfiler(LinuxPerfAsmProfiler.class) +// .addProfiler(WinPerfAsmProfiler.class) +// .addProfiler(DTraceAsmProfiler.class) + .build(); + + new Runner(opt).run(); + } + + /* + Dealing with nanobenchmarks like these requires looking into the abyss of runtime, hardware, and + generated code. Luckily, JMH has a few handy tools that ease the pain. If you are running Linux, + then perf_events are probably available as standard package. This kernel facility taps into + hardware counters, and provides the data for user space programs like JMH. Windows has less + sophisticated facilities, but also usable, see below. + + One can simply run "perf stat java -jar ..." to get the first idea how the workload behaves. In + JMH case, however, this will cause perf to profile both host and forked JVMs. + + -prof perf avoids that: JMH invokes perf for the forked VM alone. For the benchmark above, it + would print something like: + + Perf stats: + -------------------------------------------------- + + 4172.776137 task-clock (msec) # 0.411 CPUs utilized + 612 context-switches # 0.147 K/sec + 31 cpu-migrations # 0.007 K/sec + 195 page-faults # 0.047 K/sec + 16,599,643,026 cycles # 3.978 GHz [30.80%] + stalled-cycles-frontend + stalled-cycles-backend + 17,815,084,879 instructions # 1.07 insns per cycle [38.49%] + 3,813,373,583 branches # 913.870 M/sec [38.56%] + 1,212,788 branch-misses # 0.03% of all branches [38.91%] + 7,582,256,427 L1-dcache-loads # 1817.077 M/sec [39.07%] + 312,913 L1-dcache-load-misses # 0.00% of all L1-dcache hits [38.66%] + 35,688 LLC-loads # 0.009 M/sec [32.58%] + LLC-load-misses:HG + L1-icache-loads:HG + 161,436 L1-icache-load-misses:HG # 0.00% of all L1-icache hits [32.81%] + 7,200,981,198 dTLB-loads:HG # 1725.705 M/sec [32.68%] + 3,360 dTLB-load-misses:HG # 0.00% of all dTLB cache hits [32.65%] + 193,874 iTLB-loads:HG # 0.046 M/sec [32.56%] + 4,193 iTLB-load-misses:HG # 2.16% of all iTLB cache hits [32.44%] + L1-dcache-prefetches:HG + 0 L1-dcache-prefetch-misses:HG # 0.000 K/sec [32.33%] + + 10.159432892 seconds time elapsed + + We can already see this benchmark goes with good IPC, does lots of loads and lots of stores, + all of them are more or less fulfilled without misses. The data like this is not handy though: + you would like to normalize the counters per benchmark op. + + This is exactly what -prof perfnorm does: + + Benchmark Mode Cnt Score Error Units + JMHSample_35_Profilers.Atomic.test avgt 15 6.551 ± 0.023 ns/op + JMHSample_35_Profilers.Atomic.test:·CPI avgt 3 0.933 ± 0.026 #/op + JMHSample_35_Profilers.Atomic.test:·L1-dcache-load-misses avgt 3 0.001 ± 0.022 #/op + JMHSample_35_Profilers.Atomic.test:·L1-dcache-loads avgt 3 12.267 ± 1.324 #/op + JMHSample_35_Profilers.Atomic.test:·L1-dcache-store-misses avgt 3 0.001 ± 0.006 #/op + JMHSample_35_Profilers.Atomic.test:·L1-dcache-stores avgt 3 4.090 ± 0.402 #/op + JMHSample_35_Profilers.Atomic.test:·L1-icache-load-misses avgt 3 0.001 ± 0.011 #/op + JMHSample_35_Profilers.Atomic.test:·LLC-loads avgt 3 0.001 ± 0.004 #/op + JMHSample_35_Profilers.Atomic.test:·LLC-stores avgt 3 ≈ 10⁻⁴ #/op + JMHSample_35_Profilers.Atomic.test:·branch-misses avgt 3 ≈ 10⁻⁴ #/op + JMHSample_35_Profilers.Atomic.test:·branches avgt 3 6.152 ± 0.385 #/op + JMHSample_35_Profilers.Atomic.test:·bus-cycles avgt 3 0.670 ± 0.048 #/op + JMHSample_35_Profilers.Atomic.test:·context-switches avgt 3 ≈ 10⁻⁶ #/op + JMHSample_35_Profilers.Atomic.test:·cpu-migrations avgt 3 ≈ 10⁻⁷ #/op + JMHSample_35_Profilers.Atomic.test:·cycles avgt 3 26.790 ± 1.393 #/op + JMHSample_35_Profilers.Atomic.test:·dTLB-load-misses avgt 3 ≈ 10⁻⁴ #/op + JMHSample_35_Profilers.Atomic.test:·dTLB-loads avgt 3 12.278 ± 0.277 #/op + JMHSample_35_Profilers.Atomic.test:·dTLB-store-misses avgt 3 ≈ 10⁻⁵ #/op + JMHSample_35_Profilers.Atomic.test:·dTLB-stores avgt 3 4.113 ± 0.437 #/op + JMHSample_35_Profilers.Atomic.test:·iTLB-load-misses avgt 3 ≈ 10⁻⁵ #/op + JMHSample_35_Profilers.Atomic.test:·iTLB-loads avgt 3 0.001 ± 0.034 #/op + JMHSample_35_Profilers.Atomic.test:·instructions avgt 3 28.729 ± 1.297 #/op + JMHSample_35_Profilers.Atomic.test:·minor-faults avgt 3 ≈ 10⁻⁷ #/op + JMHSample_35_Profilers.Atomic.test:·page-faults avgt 3 ≈ 10⁻⁷ #/op + JMHSample_35_Profilers.Atomic.test:·ref-cycles avgt 3 26.734 ± 2.081 #/op + + It is customary to trim the lines irrelevant to the particular benchmark. We show all of them here for + completeness. + + We can see that the benchmark does ~12 loads per benchmark op, and about ~4 stores per op, most of + them fitting in the cache. There are also ~6 branches per benchmark op, all are predicted as well. + It is also easy to see the benchmark op takes ~28 instructions executed in ~27 cycles. + + The output would get more interesting when we run with more threads, say, -t 8: + + Benchmark Mode Cnt Score Error Units + JMHSample_35_Profilers.Atomic.test avgt 15 143.595 ± 1.968 ns/op + JMHSample_35_Profilers.Atomic.test:·CPI avgt 3 17.741 ± 28.761 #/op + JMHSample_35_Profilers.Atomic.test:·L1-dcache-load-misses avgt 3 0.175 ± 0.406 #/op + JMHSample_35_Profilers.Atomic.test:·L1-dcache-loads avgt 3 11.872 ± 0.786 #/op + JMHSample_35_Profilers.Atomic.test:·L1-dcache-store-misses avgt 3 0.184 ± 0.505 #/op + JMHSample_35_Profilers.Atomic.test:·L1-dcache-stores avgt 3 4.422 ± 0.561 #/op + JMHSample_35_Profilers.Atomic.test:·L1-icache-load-misses avgt 3 0.015 ± 0.083 #/op + JMHSample_35_Profilers.Atomic.test:·LLC-loads avgt 3 0.015 ± 0.128 #/op + JMHSample_35_Profilers.Atomic.test:·LLC-stores avgt 3 1.036 ± 0.045 #/op + JMHSample_35_Profilers.Atomic.test:·branch-misses avgt 3 0.224 ± 0.492 #/op + JMHSample_35_Profilers.Atomic.test:·branches avgt 3 6.524 ± 2.873 #/op + JMHSample_35_Profilers.Atomic.test:·bus-cycles avgt 3 13.475 ± 14.502 #/op + JMHSample_35_Profilers.Atomic.test:·context-switches avgt 3 ≈ 10⁻⁴ #/op + JMHSample_35_Profilers.Atomic.test:·cpu-migrations avgt 3 ≈ 10⁻⁶ #/op + JMHSample_35_Profilers.Atomic.test:·cycles avgt 3 537.874 ± 595.723 #/op + JMHSample_35_Profilers.Atomic.test:·dTLB-load-misses avgt 3 0.001 ± 0.006 #/op + JMHSample_35_Profilers.Atomic.test:·dTLB-loads avgt 3 12.032 ± 2.430 #/op + JMHSample_35_Profilers.Atomic.test:·dTLB-store-misses avgt 3 ≈ 10⁻⁴ #/op + JMHSample_35_Profilers.Atomic.test:·dTLB-stores avgt 3 4.557 ± 0.948 #/op + JMHSample_35_Profilers.Atomic.test:·iTLB-load-misses avgt 3 ≈ 10⁻³ #/op + JMHSample_35_Profilers.Atomic.test:·iTLB-loads avgt 3 0.016 ± 0.052 #/op + JMHSample_35_Profilers.Atomic.test:·instructions avgt 3 30.367 ± 15.052 #/op + JMHSample_35_Profilers.Atomic.test:·minor-faults avgt 3 ≈ 10⁻⁵ #/op + JMHSample_35_Profilers.Atomic.test:·page-faults avgt 3 ≈ 10⁻⁵ #/op + JMHSample_35_Profilers.Atomic.test:·ref-cycles avgt 3 538.697 ± 590.183 #/op + + Note how this time the CPI is awfully high: 17 cycles per instruction! Indeed, we are making almost the + same ~30 instructions, but now they take >530 cycles. Other counters highlight why: we now have cache + misses on both loads and stores, on all levels of cache hierarchy. With a simple constant-footprint + like ours, that's an indication of sharing problems. Indeed, our AtomicLong is heavily-contended + with 8 threads. + + "perfnorm", again, can (and should!) be used with multiple forks, to properly estimate the metrics. + + The last, but not the least player on our field is -prof perfasm. It is important to follow up on + generated code when dealing with fine-grained benchmarks. We could employ PrintAssembly to dump the + generated code, but it will dump *all* the generated code, and figuring out what is related to our + benchmark is a daunting task. But we have "perf" that can tell what program addresses are really hot! + This enables us to contrast the assembly output. + + -prof perfasm would indeed contrast out the hottest loop in the generated code! It will also point + fingers at "lock xadd" as the hottest instruction in our code. Hardware counters are not very precise + about the instruction addresses, so sometimes they attribute the events to the adjacent code lines. + + Hottest code regions (>10.00% "cycles" events): + ....[Hottest Region 1].............................................................................. + [0x7f1824f87c45:0x7f1824f87c79] in jmh.samples.generated.JMHSample_35_Profilers_Atomic_test::test_avgt_jmhStub + + ; - jmh.samples.generated.JMHSample_35_Profilers_Atomic_test::test_avgt_jmhStub@29 (line 201) + ; implicit exception: dispatches to 0x00007f1824f87d21 + 0x00007f1824f87c25: test %r11d,%r11d + 0x00007f1824f87c28: jne 0x00007f1824f87cbd ;*ifeq + ; - jmh.samples.generated.JMHSample_35_Profilers_Atomic_test::test_avgt_jmhStub@32 (line 201) + 0x00007f1824f87c2e: mov $0x1,%ebp + 0x00007f1824f87c33: nopw 0x0(%rax,%rax,1) + 0x00007f1824f87c3c: xchg %ax,%ax ;*aload + ; - jmh.samples.generated.JMHSample_35_Profilers_Atomic_test::test_avgt_jmhStub@13 (line 199) + 0x00007f1824f87c40: mov 0x8(%rsp),%r10 + 0.00% 0x00007f1824f87c45: mov 0xc(%r10),%r11d ;*getfield n + ; - jmh.samples.JMHSample_35_Profilers$Atomic::test@1 (line 280) + ; - jmh.samples.generated.JMHSample_35_Profilers_Atomic_test::test_avgt_jmhStub@16 (line 199) + 0.19% 0.02% 0x00007f1824f87c49: test %r11d,%r11d + 0x00007f1824f87c4c: je 0x00007f1824f87cad + 0x00007f1824f87c4e: mov $0x1,%edx + 0x00007f1824f87c53: lock xadd %rdx,0x10(%r12,%r11,8) + ;*invokevirtual getAndAddLong + ; - java.util.concurrent.atomic.AtomicLong::incrementAndGet@8 (line 200) + ; - jmh.samples.JMHSample_35_Profilers$Atomic::test@4 (line 280) + ; - jmh.samples.generated.JMHSample_35_Profilers_Atomic_test::test_avgt_jmhStub@16 (line 199) + 95.20% 95.06% 0x00007f1824f87c5a: add $0x1,%rdx ;*ladd + ; - java.util.concurrent.atomic.AtomicLong::incrementAndGet@12 (line 200) + ; - jmh.samples.JMHSample_35_Profilers$Atomic::test@4 (line 280) + ; - jmh.samples.generated.JMHSample_35_Profilers_Atomic_test::test_avgt_jmhStub@16 (line 199) + 0.24% 0.00% 0x00007f1824f87c5e: mov 0x10(%rsp),%rsi + 0x00007f1824f87c63: callq 0x00007f1824e2b020 ; OopMap{[0]=Oop [8]=Oop [16]=Oop [24]=Oop off=232} + ;*invokevirtual consume + ; - jmh.samples.generated.JMHSample_35_Profilers_Atomic_test::test_avgt_jmhStub@19 (line 199) + ; {optimized virtual_call} + 0.20% 0.01% 0x00007f1824f87c68: mov 0x18(%rsp),%r10 + 0x00007f1824f87c6d: movzbl 0x94(%r10),%r11d ;*getfield isDone + ; - jmh.samples.generated.JMHSample_35_Profilers_Atomic_test::test_avgt_jmhStub@29 (line 201) + 0.00% 0x00007f1824f87c75: add $0x1,%rbp ; OopMap{r10=Oop [0]=Oop [8]=Oop [16]=Oop [24]=Oop off=249} + ;*ifeq + ; - jmh.samples.generated.JMHSample_35_Profilers_Atomic_test::test_avgt_jmhStub@32 (line 201) + 0.20% 0.01% 0x00007f1824f87c79: test %eax,0x15f36381(%rip) # 0x00007f183aebe000 + ; {poll} + 0x00007f1824f87c7f: test %r11d,%r11d + 0x00007f1824f87c82: je 0x00007f1824f87c40 ;*aload_2 + ; - jmh.samples.generated.JMHSample_35_Profilers_Atomic_test::test_avgt_jmhStub@35 (line 202) + 0x00007f1824f87c84: mov $0x7f1839be4220,%r10 + 0x00007f1824f87c8e: callq *%r10 ;*invokestatic nanoTime + ; - jmh.samples.generated.JMHSample_35_Profilers_Atomic_test::test_avgt_jmhStub@36 (line 202) + 0x00007f1824f87c91: mov (%rsp),%r10 + .................................................................................................... + 96.03% 95.10% + + perfasm would also print the hottest methods to show if we indeed spending time in our benchmark. Most of the time, + it can demangle VM and kernel symbols as well: + + ....[Hottest Methods (after inlining)].............................................................. + 96.03% 95.10% jmh.samples.generated.JMHSample_35_Profilers_Atomic_test::test_avgt_jmhStub + 0.73% 0.78% jmh.samples.generated.JMHSample_35_Profilers_Atomic_test::test_AverageTime + 0.63% 0.00% org.openjdk.jmh.infra.Blackhole::consume + 0.23% 0.25% native_write_msr_safe ([kernel.kallsyms]) + 0.09% 0.05% _raw_spin_unlock ([kernel.kallsyms]) + 0.09% 0.00% [unknown] (libpthread-2.19.so) + 0.06% 0.07% _raw_spin_lock ([kernel.kallsyms]) + 0.06% 0.04% _raw_spin_unlock_irqrestore ([kernel.kallsyms]) + 0.06% 0.05% _IO_fwrite (libc-2.19.so) + 0.05% 0.03% __srcu_read_lock; __srcu_read_unlock ([kernel.kallsyms]) + 0.04% 0.05% _raw_spin_lock_irqsave ([kernel.kallsyms]) + 0.04% 0.06% vfprintf (libc-2.19.so) + 0.04% 0.01% mutex_unlock ([kernel.kallsyms]) + 0.04% 0.01% _nv014306rm ([nvidia]) + 0.04% 0.04% rcu_eqs_enter_common.isra.47 ([kernel.kallsyms]) + 0.04% 0.02% mutex_lock ([kernel.kallsyms]) + 0.03% 0.07% __acct_update_integrals ([kernel.kallsyms]) + 0.03% 0.02% fget_light ([kernel.kallsyms]) + 0.03% 0.01% fput ([kernel.kallsyms]) + 0.03% 0.04% rcu_eqs_exit_common.isra.48 ([kernel.kallsyms]) + 1.63% 2.26% <...other 319 warm methods...> + .................................................................................................... + 100.00% 98.97% + + ....[Distribution by Area].......................................................................... + 97.44% 95.99% + 1.60% 2.42% + 0.47% 0.78% + 0.22% 0.29% + 0.15% 0.07% + 0.07% 0.38% + 0.05% 0.06% + 0.00% 0.00% + 0.00% 0.00% + .................................................................................................... + 100.00% 100.00% + + Since program addresses change from fork to fork, it does not make sense to run perfasm with more than + a single fork. + */ + } +} diff --git a/test/src/test/java/jmh/samples/JMHSample_36_BranchPrediction.java b/test/src/test/java/jmh/samples/JMHSample_36_BranchPrediction.java new file mode 100644 index 00000000..f8ebc55c --- /dev/null +++ b/test/src/test/java/jmh/samples/JMHSample_36_BranchPrediction.java @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2015, Oracle America, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of Oracle nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package jmh.samples; + +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.infra.Blackhole; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +import java.util.Arrays; +import java.util.Random; +import java.util.concurrent.TimeUnit; + +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) +@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) +@Fork(5) +@State(Scope.Benchmark) +public class JMHSample_36_BranchPrediction { + + /* + * This sample serves as a warning against regular data sets. + * + * It is very tempting to present a regular data set to benchmark, either due to + * naive generation strategy, or just from feeling better about regular data sets. + * Unfortunately, it frequently backfires: the regular datasets are known to be + * optimized well by software and hardware. This example exploits one of these + * optimizations: branch prediction. + * + * Imagine our benchmark selects the branch based on the array contents, as + * we are streaming through it: + */ + + private static final int COUNT = 1024 * 1024; + + private byte[] sorted; + private byte[] unsorted; + + @Setup + public void setup() { + sorted = new byte[COUNT]; + unsorted = new byte[COUNT]; + Random random = new Random(1234); + random.nextBytes(sorted); + random.nextBytes(unsorted); + Arrays.sort(sorted); + } + + @Benchmark + @OperationsPerInvocation(COUNT) + public void sorted(Blackhole bh1, Blackhole bh2) { + for (byte v : sorted) { + if (v > 0) { + bh1.consume(v); + } else { + bh2.consume(v); + } + } + } + + @Benchmark + @OperationsPerInvocation(COUNT) + public void unsorted(Blackhole bh1, Blackhole bh2) { + for (byte v : unsorted) { + if (v > 0) { + bh1.consume(v); + } else { + bh2.consume(v); + } + } + } + + /* + There is a substantial difference in performance for these benchmarks! + + It is explained by good branch prediction in "sorted" case, and branch mispredicts in "unsorted" + case. -prof perfnorm conveniently highlights that, with larger "branch-misses", and larger "CPI" + for "unsorted" case: + + Benchmark Mode Cnt Score Error Units + JMHSample_36_BranchPrediction.sorted avgt 25 2.160 ± 0.049 ns/op + JMHSample_36_BranchPrediction.sorted:·CPI avgt 5 0.286 ± 0.025 #/op + JMHSample_36_BranchPrediction.sorted:·branch-misses avgt 5 ≈ 10⁻⁴ #/op + JMHSample_36_BranchPrediction.sorted:·branches avgt 5 7.606 ± 1.742 #/op + JMHSample_36_BranchPrediction.sorted:·cycles avgt 5 8.998 ± 1.081 #/op + JMHSample_36_BranchPrediction.sorted:·instructions avgt 5 31.442 ± 4.899 #/op + + JMHSample_36_BranchPrediction.unsorted avgt 25 5.943 ± 0.018 ns/op + JMHSample_36_BranchPrediction.unsorted:·CPI avgt 5 0.775 ± 0.052 #/op + JMHSample_36_BranchPrediction.unsorted:·branch-misses avgt 5 0.529 ± 0.026 #/op <--- OOPS + JMHSample_36_BranchPrediction.unsorted:·branches avgt 5 7.841 ± 0.046 #/op + JMHSample_36_BranchPrediction.unsorted:·cycles avgt 5 24.793 ± 0.434 #/op + JMHSample_36_BranchPrediction.unsorted:·instructions avgt 5 31.994 ± 2.342 #/op + + It is an open question if you want to measure only one of these tests. In many cases, you have to measure + both to get the proper best-case and worst-case estimate! + */ + + + /* + * ============================== HOW TO RUN THIS TEST: ==================================== + * + * You can run this test: + * + * a) Via the command line: + * $ mvn clean install + * $ java -jar target/benchmarks.jar JMHSample_36 + * + * b) Via the Java API: + * (see the JMH homepage for possible caveats when running from IDE: + * http://openjdk.java.net/projects/code-tools/jmh/) + */ + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(".*" + JMHSample_36_BranchPrediction.class.getSimpleName() + ".*") + .build(); + + new Runner(opt).run(); + } + +} diff --git a/test/src/test/java/jmh/samples/JMHSample_37_CacheAccess.java b/test/src/test/java/jmh/samples/JMHSample_37_CacheAccess.java new file mode 100644 index 00000000..1bf6b419 --- /dev/null +++ b/test/src/test/java/jmh/samples/JMHSample_37_CacheAccess.java @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2015, Oracle America, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of Oracle nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package jmh.samples; + +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.infra.Blackhole; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +import java.util.Random; +import java.util.concurrent.TimeUnit; + +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) +@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) +@Fork(5) +@State(Scope.Benchmark) +public class JMHSample_37_CacheAccess { + + /* + * This sample serves as a warning against subtle differences in cache access patterns. + * + * Many performance differences may be explained by the way tests are accessing memory. + * In the example below, we walk the matrix either row-first, or col-first: + */ + + private final static int COUNT = 4096; + private final static int MATRIX_SIZE = COUNT * COUNT; + + private int[][] matrix; + + @Setup + public void setup() { + matrix = new int[COUNT][COUNT]; + Random random = new Random(1234); + for (int i = 0; i < COUNT; i++) { + for (int j = 0; j < COUNT; j++) { + matrix[i][j] = random.nextInt(); + } + } + } + + @Benchmark + @OperationsPerInvocation(MATRIX_SIZE) + public void colFirst(Blackhole bh) { + for (int c = 0; c < COUNT; c++) { + for (int r = 0; r < COUNT; r++) { + bh.consume(matrix[r][c]); + } + } + } + + @Benchmark + @OperationsPerInvocation(MATRIX_SIZE) + public void rowFirst(Blackhole bh) { + for (int r = 0; r < COUNT; r++) { + for (int c = 0; c < COUNT; c++) { + bh.consume(matrix[r][c]); + } + } + } + + /* + Notably, colFirst accesses are much slower, and that's not a surprise: Java's multidimensional + arrays are actually rigged, being one-dimensional arrays of one-dimensional arrays. Therefore, + pulling n-th element from each of the inner array induces more cache misses, when matrix is large. + -prof perfnorm conveniently highlights that, with >2 cache misses per one benchmark op: + + Benchmark Mode Cnt Score Error Units + JMHSample_37_MatrixCopy.colFirst avgt 25 5.306 ± 0.020 ns/op + JMHSample_37_MatrixCopy.colFirst:·CPI avgt 5 0.621 ± 0.011 #/op + JMHSample_37_MatrixCopy.colFirst:·L1-dcache-load-misses avgt 5 2.177 ± 0.044 #/op <-- OOPS + JMHSample_37_MatrixCopy.colFirst:·L1-dcache-loads avgt 5 14.804 ± 0.261 #/op + JMHSample_37_MatrixCopy.colFirst:·LLC-loads avgt 5 2.165 ± 0.091 #/op + JMHSample_37_MatrixCopy.colFirst:·cycles avgt 5 22.272 ± 0.372 #/op + JMHSample_37_MatrixCopy.colFirst:·instructions avgt 5 35.888 ± 1.215 #/op + + JMHSample_37_MatrixCopy.rowFirst avgt 25 2.662 ± 0.003 ns/op + JMHSample_37_MatrixCopy.rowFirst:·CPI avgt 5 0.312 ± 0.003 #/op + JMHSample_37_MatrixCopy.rowFirst:·L1-dcache-load-misses avgt 5 0.066 ± 0.001 #/op + JMHSample_37_MatrixCopy.rowFirst:·L1-dcache-loads avgt 5 14.570 ± 0.400 #/op + JMHSample_37_MatrixCopy.rowFirst:·LLC-loads avgt 5 0.002 ± 0.001 #/op + JMHSample_37_MatrixCopy.rowFirst:·cycles avgt 5 11.046 ± 0.343 #/op + JMHSample_37_MatrixCopy.rowFirst:·instructions avgt 5 35.416 ± 1.248 #/op + + So, when comparing two different benchmarks, you have to follow up if the difference is caused + by the memory locality issues. + */ + + /* + * ============================== HOW TO RUN THIS TEST: ==================================== + * + * You can run this test: + * + * a) Via the command line: + * $ mvn clean install + * $ java -jar target/benchmarks.jar JMHSample_37 + * + * b) Via the Java API: + * (see the JMH homepage for possible caveats when running from IDE: + * http://openjdk.java.net/projects/code-tools/jmh/) + */ + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(".*" + JMHSample_37_CacheAccess.class.getSimpleName() + ".*") + .build(); + + new Runner(opt).run(); + } + +} diff --git a/test/src/test/java/jmh/samples/JMHSample_38_PerInvokeSetup.java b/test/src/test/java/jmh/samples/JMHSample_38_PerInvokeSetup.java new file mode 100644 index 00000000..56159c74 --- /dev/null +++ b/test/src/test/java/jmh/samples/JMHSample_38_PerInvokeSetup.java @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2015, Oracle America, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of Oracle nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package jmh.samples; + +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +import java.util.Arrays; +import java.util.Random; +import java.util.concurrent.TimeUnit; + +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) +@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) +@Fork(5) +public class JMHSample_38_PerInvokeSetup { + + /* + * This example highlights the usual mistake in non-steady-state benchmarks. + * + * Suppose we want to test how long it takes to bubble sort an array. Naively, + * we could make the test that populates an array with random (unsorted) values, + * and calls sort on it over and over again: + */ + + private void bubbleSort(byte[] b) { + boolean changed = true; + while (changed) { + changed = false; + for (int c = 0; c < b.length - 1; c++) { + if (b[c] > b[c + 1]) { + byte t = b[c]; + b[c] = b[c + 1]; + b[c + 1] = t; + changed = true; + } + } + } + } + + // Could be an implicit State instead, but we are going to use it + // as the dependency in one of the tests below + @State(Scope.Benchmark) + public static class Data { + + @Param({"1", "16", "256"}) + int count; + + byte[] arr; + + @Setup + public void setup() { + arr = new byte[count]; + Random random = new Random(1234); + random.nextBytes(arr); + } + } + + @Benchmark + public byte[] measureWrong(Data d) { + bubbleSort(d.arr); + return d.arr; + } + + /* + * The method above is subtly wrong: it sorts the random array on the first invocation + * only. Every subsequent call will "sort" the already sorted array. With bubble sort, + * that operation would be significantly faster! + * + * This is how we might *try* to measure it right by making a copy in Level.Invocation + * setup. However, this is susceptible to the problems described in Level.Invocation + * Javadocs, READ AND UNDERSTAND THOSE DOCS BEFORE USING THIS APPROACH. + */ + + @State(Scope.Thread) + public static class DataCopy { + byte[] copy; + + @Setup(Level.Invocation) + public void setup2(Data d) { + copy = Arrays.copyOf(d.arr, d.arr.length); + } + } + + @Benchmark + public byte[] measureNeutral(DataCopy d) { + bubbleSort(d.copy); + return d.copy; + } + + /* + * In an overwhelming majority of cases, the only sensible thing to do is to suck up + * the per-invocation setup costs into a benchmark itself. This work well in practice, + * especially when the payload costs dominate the setup costs. + */ + + @Benchmark + public byte[] measureRight(Data d) { + byte[] c = Arrays.copyOf(d.arr, d.arr.length); + bubbleSort(c); + return c; + } + + /* + Benchmark (count) Mode Cnt Score Error Units + + JMHSample_38_PerInvokeSetup.measureWrong 1 avgt 25 2.408 ± 0.011 ns/op + JMHSample_38_PerInvokeSetup.measureWrong 16 avgt 25 8.286 ± 0.023 ns/op + JMHSample_38_PerInvokeSetup.measureWrong 256 avgt 25 73.405 ± 0.018 ns/op + + JMHSample_38_PerInvokeSetup.measureNeutral 1 avgt 25 15.835 ± 0.470 ns/op + JMHSample_38_PerInvokeSetup.measureNeutral 16 avgt 25 112.552 ± 0.787 ns/op + JMHSample_38_PerInvokeSetup.measureNeutral 256 avgt 25 58343.848 ± 991.202 ns/op + + JMHSample_38_PerInvokeSetup.measureRight 1 avgt 25 6.075 ± 0.018 ns/op + JMHSample_38_PerInvokeSetup.measureRight 16 avgt 25 102.390 ± 0.676 ns/op + JMHSample_38_PerInvokeSetup.measureRight 256 avgt 25 58812.411 ± 997.951 ns/op + + We can clearly see that "measureWrong" provides a very weird result: it "sorts" way too fast. + "measureNeutral" is neither good or bad: while it prepares the data for each invocation correctly, + the timing overheads are clearly visible. These overheads can be overwhelming, depending on + the thread count and/or OS flavor. + */ + + /* + * ============================== HOW TO RUN THIS TEST: ==================================== + * + * You can run this test: + * + * a) Via the command line: + * $ mvn clean install + * $ java -jar target/benchmarks.jar JMHSample_38 + * + * b) Via the Java API: + * (see the JMH homepage for possible caveats when running from IDE: + * http://openjdk.java.net/projects/code-tools/jmh/) + */ + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(".*" + JMHSample_38_PerInvokeSetup.class.getSimpleName() + ".*") + .build(); + + new Runner(opt).run(); + } + +} From 07f3cb8b332c4cae7bdc6fad7e64e07f50647723 Mon Sep 17 00:00:00 2001 From: kuangcp Date: Wed, 28 Feb 2024 20:23:31 +0800 Subject: [PATCH 329/476] netty websocket url param --- netty/pom.xml | 6 +- .../netty/websocket/ChannelSupervise.java | 57 ++--- .../src/main/java/netty/websocket/Const.java | 10 + .../NioWebSocketChannelInitializer.java | 23 +- .../netty/websocket/NioWebSocketHandler.java | 203 +++++++++++------- .../netty/websocket/NioWebSocketServer.java | 38 ---- netty/src/main/java/netty/websocket/Readme.md | 6 +- .../src/main/java/netty/websocket/Server.java | 40 ++++ .../src/main/java/netty/websocket/client.html | 31 +++ 9 files changed, 265 insertions(+), 149 deletions(-) create mode 100644 netty/src/main/java/netty/websocket/Const.java delete mode 100644 netty/src/main/java/netty/websocket/NioWebSocketServer.java create mode 100644 netty/src/main/java/netty/websocket/Server.java create mode 100644 netty/src/main/java/netty/websocket/client.html diff --git a/netty/pom.xml b/netty/pom.xml index bc124016..556b2ccd 100644 --- a/netty/pom.xml +++ b/netty/pom.xml @@ -23,8 +23,12 @@ io.netty netty-all + + org.apache.commons + commons-lang3 + - + diff --git a/netty/src/main/java/netty/websocket/ChannelSupervise.java b/netty/src/main/java/netty/websocket/ChannelSupervise.java index 1a5b6eed..40cd0332 100644 --- a/netty/src/main/java/netty/websocket/ChannelSupervise.java +++ b/netty/src/main/java/netty/websocket/ChannelSupervise.java @@ -6,9 +6,10 @@ import io.netty.channel.group.DefaultChannelGroup; import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; import io.netty.util.concurrent.GlobalEventExecutor; +import lombok.extern.slf4j.Slf4j; + import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; -import lombok.extern.slf4j.Slf4j; /** * @author https://github.com/kuangcp on 2021-05-18 08:34 @@ -16,31 +17,31 @@ @Slf4j public class ChannelSupervise { - private static final ChannelGroup GLOBAL_GROUP = new DefaultChannelGroup( - GlobalEventExecutor.INSTANCE); - private static final ConcurrentMap CHANNEL_MAP = new ConcurrentHashMap<>(); - - public static void addChannel(Channel channel) { - GLOBAL_GROUP.add(channel); - CHANNEL_MAP.put(channel.id().asShortText(), channel.id()); - printState(); - } - - public static void removeChannel(Channel channel) { - GLOBAL_GROUP.remove(channel); - CHANNEL_MAP.remove(channel.id().asShortText()); - printState(); - } - - public static Channel findChannel(String id) { - return GLOBAL_GROUP.find(CHANNEL_MAP.get(id)); - } - - public static void send2All(TextWebSocketFrame tws) { - GLOBAL_GROUP.writeAndFlush(tws); - } - - private static void printState() { - log.info("channelSize={} global={}", CHANNEL_MAP.size(), GLOBAL_GROUP.size()); - } + private static final ChannelGroup GLOBAL_GROUP = new DefaultChannelGroup( + GlobalEventExecutor.INSTANCE); + private static final ConcurrentMap CHANNEL_MAP = new ConcurrentHashMap<>(); + + public static void addChannel(Channel channel) { + GLOBAL_GROUP.add(channel); + CHANNEL_MAP.put(channel.id().asShortText(), channel.id()); + printState(); + } + + public static void removeChannel(Channel channel) { + GLOBAL_GROUP.remove(channel); + CHANNEL_MAP.remove(channel.id().asShortText()); + printState(); + } + + public static Channel findChannel(String id) { + return GLOBAL_GROUP.find(CHANNEL_MAP.get(id)); + } + + public static void send2All(TextWebSocketFrame tws) { + GLOBAL_GROUP.writeAndFlush(tws); + } + + private static void printState() { + log.info("channelSize={} global={}", CHANNEL_MAP.size(), GLOBAL_GROUP.size()); + } } diff --git a/netty/src/main/java/netty/websocket/Const.java b/netty/src/main/java/netty/websocket/Const.java new file mode 100644 index 00000000..3126dfd1 --- /dev/null +++ b/netty/src/main/java/netty/websocket/Const.java @@ -0,0 +1,10 @@ +package netty.websocket; + +/** + * + * @author Kuangcp + * 2024-02-28 20:12 + */ +public interface Const { + String webSocketPath = "/ws"; +} diff --git a/netty/src/main/java/netty/websocket/NioWebSocketChannelInitializer.java b/netty/src/main/java/netty/websocket/NioWebSocketChannelInitializer.java index 8057e8c4..a9cb92e2 100644 --- a/netty/src/main/java/netty/websocket/NioWebSocketChannelInitializer.java +++ b/netty/src/main/java/netty/websocket/NioWebSocketChannelInitializer.java @@ -1,22 +1,29 @@ package netty.websocket; import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelPipeline; import io.netty.channel.socket.SocketChannel; import io.netty.handler.codec.http.HttpObjectAggregator; import io.netty.handler.codec.http.HttpServerCodec; +import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler; import io.netty.handler.logging.LoggingHandler; import io.netty.handler.stream.ChunkedWriteHandler; +import static io.netty.handler.codec.http.HttpHeaders.Names.WEBSOCKET_PROTOCOL; + /** * @author https://github.com/kuangcp on 2021-05-18 08:33 */ public class NioWebSocketChannelInitializer extends ChannelInitializer { - @Override - protected void initChannel(SocketChannel ch) { - ch.pipeline().addLast("logging",new LoggingHandler("DEBUG"));//设置log监听器,并且日志级别为debug,方便观察运行流程 - ch.pipeline().addLast("http-codec",new HttpServerCodec());//设置解码器 - ch.pipeline().addLast("aggregator",new HttpObjectAggregator(65536));//聚合器,使用websocket会用到 - ch.pipeline().addLast("http-chunked",new ChunkedWriteHandler());//用于大数据的分区传输 - ch.pipeline().addLast("handler",new NioWebSocketHandler());//自定义的业务handler - } + @Override + protected void initChannel(SocketChannel ch) { + ChannelPipeline pipeline = ch.pipeline(); + pipeline.addLast("logging", new LoggingHandler("DEBUG"));//设置log监听器,并且日志级别为debug,方便观察运行流程 + pipeline.addLast("http-codec", new HttpServerCodec());//设置解码器 + pipeline.addLast("aggregator", new HttpObjectAggregator(65536));//聚合器,使用websocket会用到 + pipeline.addLast("http-chunked", new ChunkedWriteHandler());//用于大数据的分区传输 + pipeline.addLast("handler", new NioWebSocketHandler());//自定义的业务handler + // checkStartsWith 为true 支持路径带参数 + pipeline.addLast("", new WebSocketServerProtocolHandler(Const.webSocketPath, WEBSOCKET_PROTOCOL, true, 65536, false, true)); + } } diff --git a/netty/src/main/java/netty/websocket/NioWebSocketHandler.java b/netty/src/main/java/netty/websocket/NioWebSocketHandler.java index 35cf6b74..42ea83df 100644 --- a/netty/src/main/java/netty/websocket/NioWebSocketHandler.java +++ b/netty/src/main/java/netty/websocket/NioWebSocketHandler.java @@ -3,36 +3,24 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelFuture; -import io.netty.channel.ChannelFutureListener; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.SimpleChannelInboundHandler; +import io.netty.channel.*; import io.netty.handler.codec.http.*; import io.netty.handler.codec.http.websocketx.*; import io.netty.util.CharsetUtil; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; -import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; /** * @author https://github.com/kuangcp on 2021-05-18 08:33 */ @Slf4j -public class NioWebSocketHandler extends SimpleChannelInboundHandler { +public class NioWebSocketHandler extends SimpleChannelInboundHandler { - private WebSocketServerHandshaker handshaker; - - @Override - protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception { - log.debug("收到消息:" + msg); - if (msg instanceof FullHttpRequest) { - //以http请求形式接入,但是走的是websocket - handleHttpRequest(ctx, (FullHttpRequest) msg); - } else if (msg instanceof WebSocketFrame) { - //处理websocket客户端的消息 - handlerWebSocketFrame(ctx, (WebSocketFrame) msg); - } - } + private static final Map userMap = new ConcurrentHashMap<>(); @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { @@ -51,77 +39,148 @@ public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { ctx.flush(); } - private void handlerWebSocketFrame(ChannelHandlerContext ctx, WebSocketFrame frame) { - // 判断是否关闭链路的指令 - if (frame instanceof CloseWebSocketFrame) { - handshaker.close(ctx.channel(), (CloseWebSocketFrame) frame.retain()); - return; - } +// private WebSocketServerHandshaker handShaker; +// +// private void handleHttpRequest(ChannelHandlerContext ctx, FullHttpRequest req) { +// //要求Upgrade为websocket,过滤掉get/Post +// if (!req.decoderResult().isSuccess() +// || (!"websocket".equals(req.headers().get("Upgrade")))) { +// //若不是websocket方式,则创建BAD_REQUEST的req,返回给客户端 +// sendHttpResponse(ctx, req, new DefaultFullHttpResponse( +// HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST)); +// return; +// } +// +// WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory( +// "ws://localhost:7094/ws", null, false); +// handShaker = wsFactory.newHandshaker(req); +// if (handShaker == null) { +// WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel()); +// } else { +// handShaker.handshake(ctx.channel(), req); +// } +// } - // 判断是否ping消息 - if (frame instanceof PingWebSocketFrame) { - ctx.channel().write(new PongWebSocketFrame(frame.content().retain())); - return; + /** + * 拒绝不合法的请求,并返回错误信息 + */ + private static void sendHttpResponse(ChannelHandlerContext ctx, FullHttpRequest req, DefaultFullHttpResponse res) { + // 返回应答给客户端 + if (res.status().code() != 200) { + ByteBuf buf = Unpooled.copiedBuffer(res.status().toString(), CharsetUtil.UTF_8); + res.content().writeBytes(buf); + buf.release(); + } + ChannelFuture f = ctx.channel().writeAndFlush(res); + // 如果是非Keep-Alive,关闭连接 + if (!HttpUtil.isKeepAlive(req) || res.status().code() != 200) { + f.addListener(ChannelFutureListener.CLOSE); } + } + + + /** + * 将路径参数转换成Map对象,如果路径参数出现重复参数名,将以最后的参数值为准 + * @param uri 传入的携带参数的路径 + */ + public static Map getParams(String uri) { + Map params = new HashMap<>(10); + + int idx = uri.indexOf("?"); + if (idx != -1) { + String[] paramsArr = uri.substring(idx + 1).split("&"); - // 本例程仅支持文本消息,不支持二进制消息 - if (!(frame instanceof TextWebSocketFrame)) { - log.debug("本例程仅支持文本消息,不支持二进制消息"); - throw new UnsupportedOperationException(String.format( - "%s frame types not supported", frame.getClass().getName())); + for (String param : paramsArr) { + idx = param.indexOf("="); + params.put(param.substring(0, idx), param.substring(idx + 1)); + } } - // 返回应答消息 - String request = ((TextWebSocketFrame) frame).text(); - log.info("服务端收到:final:{} txt:{}", frame.isFinalFragment(), request.length()); - TextWebSocketFrame tws = new TextWebSocketFrame( - new Date().toString() + ctx.channel().id() + ":" + request); + return params; + } + + /** + * 获取URI中参数以外部分路径 + */ + public static String getBasePath(String uri) { + if (uri == null || uri.isEmpty()) + return null; - // 群发至所有连接 -// ChannelSupervise.send2All(tws); + int idx = uri.indexOf("?"); + if (idx == -1) + return uri; - // 返回【谁发的发给谁】 - ctx.channel().writeAndFlush(tws); + return uri.substring(0, idx); } + /** - * 唯一的一次http请求,用于创建websocket + * 唯一的一次http请求,用于升级至websocket 需要正确响应 */ - private void handleHttpRequest(ChannelHandlerContext ctx, FullHttpRequest req) { - //要求Upgrade为websocket,过滤掉get/Post - if (!req.decoderResult().isSuccess() - || (!"websocket".equals(req.headers().get("Upgrade")))) { - //若不是websocket方式,则创建BAD_REQUEST的req,返回给客户端 - sendHttpResponse(ctx, req, new DefaultFullHttpResponse( - HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST)); - return; + private void fullHttpRequestHandler(ChannelHandlerContext ctx, FullHttpRequest request) { + String uri = request.uri(); + Map params = getParams(uri); + log.info("客户端请求参数:{}", params); + + String userIdStr = params.get("userId"); + if (StringUtils.isNotBlank(userIdStr)) { + long userId = Long.parseLong(userIdStr); + userMap.put(userId, ctx.channel()); } - WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory( - "ws://localhost:7094/ws", null, false); - handshaker = wsFactory.newHandshaker(req); - if (handshaker == null) { - WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel()); + // 判断请求路径是否跟配置中的一致 + if (Const.webSocketPath.equals(getBasePath(uri))) { + // 因为有可能携带了参数,导致客户端一直无法返回握手包,因此在校验通过后,重置请求路径 + request.setUri(Const.webSocketPath); } else { - handshaker.handshake(ctx.channel(), req); + ctx.close(); } } - /** - * 拒绝不合法的请求,并返回错误信息 - */ - private static void sendHttpResponse(ChannelHandlerContext ctx, - FullHttpRequest req, DefaultFullHttpResponse res) { - // 返回应答给客户端 - if (res.status().code() != 200) { - ByteBuf buf = Unpooled.copiedBuffer(res.status().toString(), CharsetUtil.UTF_8); - res.content().writeBytes(buf); - buf.release(); + + @Override + protected void channelRead0(ChannelHandlerContext ctx, WebSocketFrame frame) { + if (frame instanceof PingWebSocketFrame) { + pingWebSocketFrameHandler(ctx, (PingWebSocketFrame) frame); + } else if (frame instanceof TextWebSocketFrame) { + textWebSocketFrameHandler(ctx, (TextWebSocketFrame) frame); + } else if (frame instanceof CloseWebSocketFrame) { + closeWebSocketFrameHandler(ctx, (CloseWebSocketFrame) frame); } - ChannelFuture f = ctx.channel().writeAndFlush(res); - // 如果是非Keep-Alive,关闭连接 - if (!HttpUtil.isKeepAlive(req) || res.status().code() != 200) { - f.addListener(ChannelFutureListener.CLOSE); + } + + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + log.info("客户端请求数据类型:{}", msg.getClass()); + if (msg instanceof FullHttpRequest) { + fullHttpRequestHandler(ctx, (FullHttpRequest) msg); } + super.channelRead(ctx, msg); + } + + + /** + * 客户端发送断开请求处理 + * @param ctx + * @param frame + */ + private void closeWebSocketFrameHandler(ChannelHandlerContext ctx, CloseWebSocketFrame frame) { + ctx.close(); + } + + /** + * 创建连接之后,客户端发送的消息都会在这里处理 + * @param ctx + * @param frame + */ + private void textWebSocketFrameHandler(ChannelHandlerContext ctx, TextWebSocketFrame frame) { + ctx.channel().writeAndFlush(frame.retain()); + } + + /** + * 处理客户端心跳包 + */ + private void pingWebSocketFrameHandler(ChannelHandlerContext ctx, PingWebSocketFrame frame) { + ctx.channel().writeAndFlush(new PongWebSocketFrame(frame.content().retain())); } } diff --git a/netty/src/main/java/netty/websocket/NioWebSocketServer.java b/netty/src/main/java/netty/websocket/NioWebSocketServer.java deleted file mode 100644 index 1163810f..00000000 --- a/netty/src/main/java/netty/websocket/NioWebSocketServer.java +++ /dev/null @@ -1,38 +0,0 @@ -package netty.websocket; - -import io.netty.bootstrap.ServerBootstrap; -import io.netty.channel.Channel; -import io.netty.channel.nio.NioEventLoopGroup; -import io.netty.channel.socket.nio.NioServerSocketChannel; - -/** - * @author https://github.com/kuangcp on 2021-05-18 08:32 - */ -public class NioWebSocketServer { - - private void init() { -// log.info("正在启动WebSocket服务器"); - NioEventLoopGroup boss = new NioEventLoopGroup(); - NioEventLoopGroup work = new NioEventLoopGroup(); - try { - ServerBootstrap bootstrap = new ServerBootstrap(); - bootstrap.group(boss, work); - bootstrap.channel(NioServerSocketChannel.class); - bootstrap.childHandler(new NioWebSocketChannelInitializer()); - Channel channel = bootstrap.bind(7094).sync().channel(); -// log.info("WebSocket服务器启动成功:" + channel); - channel.closeFuture().sync(); - } catch (InterruptedException e) { -// log.info("", e); - } finally { - boss.shutdownGracefully(); - work.shutdownGracefully(); -// log.info("WebSocket服务器已关闭"); - } - } - - public static void main(String[] args) { - new NioWebSocketServer().init(); - } -} - diff --git a/netty/src/main/java/netty/websocket/Readme.md b/netty/src/main/java/netty/websocket/Readme.md index 25bb730b..f81599a0 100644 --- a/netty/src/main/java/netty/websocket/Readme.md +++ b/netty/src/main/java/netty/websocket/Readme.md @@ -1,3 +1,5 @@ -Netty 实现的 websocket +Netty 实现的 websocket -TODO 对比 Tomcat,Spring实现 进行性能测试(固定 JVM 参数 相同硬件,测试最大连接数,失败数,GC情况,延迟) +[SpringBoot整合Netty处理WebSocket(支持url参数)](https://blog.csdn.net/RisenMyth/article/details/104441155) + +TODO 对比 Tomcat实现 进行性能测试(固定 JVM 参数 相同硬件,测试最大连接数,失败数,GC情况,延迟) diff --git a/netty/src/main/java/netty/websocket/Server.java b/netty/src/main/java/netty/websocket/Server.java new file mode 100644 index 00000000..33282437 --- /dev/null +++ b/netty/src/main/java/netty/websocket/Server.java @@ -0,0 +1,40 @@ +package netty.websocket; + +import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.Channel; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.nio.NioServerSocketChannel; +import lombok.extern.slf4j.Slf4j; + +/** + * @author https://github.com/kuangcp on 2021-05-18 08:32 + */ +@Slf4j +public class Server { + + private void init() { + log.info("正在启动WebSocket服务器"); + NioEventLoopGroup boss = new NioEventLoopGroup(); + NioEventLoopGroup work = new NioEventLoopGroup(); + try { + ServerBootstrap bootstrap = new ServerBootstrap(); + bootstrap.group(boss, work); + bootstrap.channel(NioServerSocketChannel.class); + bootstrap.childHandler(new NioWebSocketChannelInitializer()); + Channel channel = bootstrap.bind(7094).sync().channel(); + log.info("WebSocket服务器启动成功:" + channel); + channel.closeFuture().sync(); + } catch (InterruptedException e) { + log.info("", e); + } finally { + boss.shutdownGracefully(); + work.shutdownGracefully(); + log.info("WebSocket服务器已关闭"); + } + } + + public static void main(String[] args) { + new Server().init(); + } +} + diff --git a/netty/src/main/java/netty/websocket/client.html b/netty/src/main/java/netty/websocket/client.html new file mode 100644 index 00000000..3d29fade --- /dev/null +++ b/netty/src/main/java/netty/websocket/client.html @@ -0,0 +1,31 @@ + + + + + + + WeChat Client + + + + + + + \ No newline at end of file From fe99dab0e30081cc4e8fcdd7f5531e8c88afd918 Mon Sep 17 00:00:00 2001 From: kuangcp Date: Wed, 28 Feb 2024 20:37:57 +0800 Subject: [PATCH 330/476] reactor --- .../java/reactor}/http/FirstHttpServer.java | 2 +- .../main/java/reactor/websocket/Client.java | 31 +++++++++++++++++++ .../src/main/java/reactor/websocket/Readme.md | 1 + .../main/java/reactor/websocket/Server.java | 29 +++++++++++++++++ 4 files changed, 62 insertions(+), 1 deletion(-) rename {spring/src/main/java/com/github/kuangcp/reactor/netty => netty/src/main/java/reactor}/http/FirstHttpServer.java (98%) create mode 100644 netty/src/main/java/reactor/websocket/Client.java create mode 100644 netty/src/main/java/reactor/websocket/Readme.md create mode 100644 netty/src/main/java/reactor/websocket/Server.java diff --git a/spring/src/main/java/com/github/kuangcp/reactor/netty/http/FirstHttpServer.java b/netty/src/main/java/reactor/http/FirstHttpServer.java similarity index 98% rename from spring/src/main/java/com/github/kuangcp/reactor/netty/http/FirstHttpServer.java rename to netty/src/main/java/reactor/http/FirstHttpServer.java index 97122813..1c0bcd8e 100644 --- a/spring/src/main/java/com/github/kuangcp/reactor/netty/http/FirstHttpServer.java +++ b/netty/src/main/java/reactor/http/FirstHttpServer.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.reactor.netty.http; +package reactor.http; import com.fasterxml.jackson.databind.ObjectMapper; diff --git a/netty/src/main/java/reactor/websocket/Client.java b/netty/src/main/java/reactor/websocket/Client.java new file mode 100644 index 00000000..d76907cc --- /dev/null +++ b/netty/src/main/java/reactor/websocket/Client.java @@ -0,0 +1,31 @@ +package reactor.websocket; + +import io.netty.buffer.Unpooled; +import io.netty.util.CharsetUtil; +import reactor.core.publisher.Flux; +import reactor.netty.http.client.HttpClient; + +/** + * + * @author kuangchengping@sinohealth.cn + * 2024-02-28 20:29 + */ +public class Client { + public static void main(String[] args) { + HttpClient client = HttpClient.create(); + + client.websocket() + .uri("ws://localhost:40032/ws") + .handle((inbound, outbound) -> { + inbound.receive() + .asString() + .take(1) + .subscribe(System.out::println); + + final byte[] msgBytes = "hello".getBytes(CharsetUtil.ISO_8859_1); + return outbound.send(Flux.just(Unpooled.wrappedBuffer(msgBytes), Unpooled.wrappedBuffer(msgBytes))) + .neverComplete(); + }) + .blockLast(); + } +} diff --git a/netty/src/main/java/reactor/websocket/Readme.md b/netty/src/main/java/reactor/websocket/Readme.md new file mode 100644 index 00000000..991a3650 --- /dev/null +++ b/netty/src/main/java/reactor/websocket/Readme.md @@ -0,0 +1 @@ +https://projectreactor.io/docs/netty/1.1.15/reference/index.html \ No newline at end of file diff --git a/netty/src/main/java/reactor/websocket/Server.java b/netty/src/main/java/reactor/websocket/Server.java new file mode 100644 index 00000000..28271c8a --- /dev/null +++ b/netty/src/main/java/reactor/websocket/Server.java @@ -0,0 +1,29 @@ +package reactor.websocket; + +import reactor.core.publisher.Mono; +import reactor.netty.DisposableServer; +import reactor.netty.http.server.HttpServer; + +import java.io.IOException; + +/** + * + * @author kuangchengping@sinohealth.cn + * 2024-02-28 20:27 + */ +public class Server { + public static void main(String[] args) throws IOException { + DisposableServer server = HttpServer.create() + .route(routes -> routes.get("/hello", (request, response) -> response.sendString(Mono.just("Hello World!"))) + .post("/echo", (request, response) -> response.send(request.receive().retain())) + .get("/path/{param}", (request, response) -> response.sendString(Mono.just(request.param("param")))) + .ws("/ws", (wsInbound, wsOutbound) -> { + return wsOutbound.send(wsInbound.receive().retain()); + } + )) + .port(40032) + .bindNow(); + + server.onDispose().block(); + } +} From b47767025d71e24e138159eb259be590fe827f54 Mon Sep 17 00:00:00 2001 From: kuangcp Date: Wed, 28 Feb 2024 20:42:18 +0800 Subject: [PATCH 331/476] pom --- netty/pom.xml | 17 +++++++++++++++++ pom.xml | 2 +- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/netty/pom.xml b/netty/pom.xml index 556b2ccd..13b18658 100644 --- a/netty/pom.xml +++ b/netty/pom.xml @@ -27,6 +27,23 @@ org.apache.commons commons-lang3 + + io.projectreactor.netty + reactor-netty-core + + + io.projectreactor.netty + reactor-netty-http + + + + com.fasterxml.jackson.core + jackson-core + + + com.fasterxml.jackson.core + jackson-databind + diff --git a/pom.xml b/pom.xml index c525d94a..5ed0e2cf 100644 --- a/pom.xml +++ b/pom.xml @@ -181,7 +181,7 @@ io.netty netty-all - 4.1.67.Final + 4.1.107.Final com.hellokaton From e352da66c12e9a784668b4c7abecd22e0c20a922 Mon Sep 17 00:00:00 2001 From: kuangcp Date: Thu, 29 Feb 2024 18:46:57 +0800 Subject: [PATCH 332/476] fmt --- .../kuangcp/generic/inherit/InheritTest.java | 9 ++ netty/src/main/java/netty/websocket/Readme.md | 2 + .../bio/chattingroom/SocketClient.java | 59 ++++++------- .../bio/chattingroom/SocketServer.java | 25 +++--- .../github/kuangcp/port/LogicThreadTest.java | 22 ++--- .../github/kuangcp/port/MulSocketServer.java | 76 ++++++++-------- .../kuangcp/runable/GreetingClient.java | 83 +++++++++--------- .../kuangcp/runable/GreetingServer.java | 86 +++++++++---------- .../kuangcp/selfclose/SocketSelfClose.java | 34 ++++---- .../java/com/github/kuangcp/util/StrUtil.java | 12 ++- test/src/test/java/jmh/GuideTest.java | 13 ++- 11 files changed, 228 insertions(+), 193 deletions(-) diff --git a/class/src/test/java/com/github/kuangcp/generic/inherit/InheritTest.java b/class/src/test/java/com/github/kuangcp/generic/inherit/InheritTest.java index b92e643e..624e1f2f 100644 --- a/class/src/test/java/com/github/kuangcp/generic/inherit/InheritTest.java +++ b/class/src/test/java/com/github/kuangcp/generic/inherit/InheritTest.java @@ -6,6 +6,9 @@ import java.util.Arrays; import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.IntStream; /** * @author kuangcp on 3/6/19-3:16 PM @@ -53,4 +56,10 @@ public void testInit() { System.out.println(b.get(0)); System.out.println(temp.get(0)); } + + @Test + public void testPart() throws Exception { + Map> co = IntStream.range(0, 10).boxed().collect(Collectors.partitioningBy(v -> v % 2 == 0)); + System.out.println(co); + } } \ No newline at end of file diff --git a/netty/src/main/java/netty/websocket/Readme.md b/netty/src/main/java/netty/websocket/Readme.md index f81599a0..39c5f495 100644 --- a/netty/src/main/java/netty/websocket/Readme.md +++ b/netty/src/main/java/netty/websocket/Readme.md @@ -2,4 +2,6 @@ Netty 实现的 websocket [SpringBoot整合Netty处理WebSocket(支持url参数)](https://blog.csdn.net/RisenMyth/article/details/104441155) +https://blog.csdn.net/linuu/article/details/51404264 + TODO 对比 Tomcat实现 进行性能测试(固定 JVM 参数 相同硬件,测试最大连接数,失败数,GC情况,延迟) diff --git a/network/src/main/java/com/github/kuangcp/bio/chattingroom/SocketClient.java b/network/src/main/java/com/github/kuangcp/bio/chattingroom/SocketClient.java index ca2cfd93..4daa06c8 100644 --- a/network/src/main/java/com/github/kuangcp/bio/chattingroom/SocketClient.java +++ b/network/src/main/java/com/github/kuangcp/bio/chattingroom/SocketClient.java @@ -1,10 +1,11 @@ package com.github.kuangcp.bio.chattingroom; +import lombok.extern.slf4j.Slf4j; + import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.PrintStream; import java.net.Socket; -import lombok.extern.slf4j.Slf4j; /** * Created by Myth on 2017/4/2 @@ -13,33 +14,33 @@ @Slf4j public class SocketClient { - private String name; - - public static void main(String[] args) throws Exception { - SocketClient client = new SocketClient(); - client.inputName(); - client.start(); - } - - private void start() throws Exception { - Socket socket = new Socket("127.0.0.1", 30000); - //客户端启动线程不断的读取来自服务器的数据 - new Thread(new ClientThread(this.name, socket)).start(); - - PrintStream printStream = new PrintStream(socket.getOutputStream()); - String line; - //不断读取键盘输入 - BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); - while ((line = br.readLine()) != null) { - //将键盘输入写入 Socket 输入流中 - printStream.println(this.name + " : " + line); + private String name; + + public static void main(String[] args) throws Exception { + SocketClient client = new SocketClient(); + client.inputName(); + client.start(); + } + + private void start() throws Exception { + Socket socket = new Socket("127.0.0.1", 30000); + //客户端启动线程不断的读取来自服务器的数据 + new Thread(new ClientThread(this.name, socket)).start(); + + PrintStream printStream = new PrintStream(socket.getOutputStream()); + String line; + //不断读取键盘输入 + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + while ((line = br.readLine()) != null) { + //将键盘输入写入 Socket 输入流中 + printStream.println(this.name + " : " + line); + } + } + + private void inputName() throws Exception { + log.info("input name"); + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + name = br.readLine(); + log.info("your name: {}", name); } - } - - private void inputName() throws Exception { - log.info("input name"); - BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); - name = br.readLine(); - log.info("your name: {}", name); - } } diff --git a/network/src/main/java/com/github/kuangcp/bio/chattingroom/SocketServer.java b/network/src/main/java/com/github/kuangcp/bio/chattingroom/SocketServer.java index 1a56c068..bbf88ca5 100644 --- a/network/src/main/java/com/github/kuangcp/bio/chattingroom/SocketServer.java +++ b/network/src/main/java/com/github/kuangcp/bio/chattingroom/SocketServer.java @@ -1,11 +1,12 @@ package com.github.kuangcp.bio.chattingroom; +import lombok.extern.slf4j.Slf4j; + import java.net.ServerSocket; import java.net.Socket; import java.util.ArrayList; import java.util.Collections; import java.util.List; -import lombok.extern.slf4j.Slf4j; /** * Created by Myth on 2017/4/2 0002 @@ -15,18 +16,18 @@ @Slf4j public class SocketServer { - //定义保存所有的socket的集合,并包装成线程安全的 - static List socketList = Collections.synchronizedList(new ArrayList<>()); + //定义保存所有的socket的集合,并包装成线程安全的 + static List socketList = Collections.synchronizedList(new ArrayList<>()); - public static void main(String[] s) throws Exception { - ServerSocket server = new ServerSocket(30000); - while (true) { - Socket socket = server.accept(); - socketList.add(socket); - log.info("establish connect: socket={}", socket); + public static void main(String[] s) throws Exception { + ServerSocket server = new ServerSocket(30000); + while (true) { + Socket socket = server.accept(); + socketList.add(socket); + log.info("establish connect: socket={}", socket); - //每当客户端连接后启动一个ServerThread 线程为该客户服务 - new Thread(new ServerThread(socket)).start(); + //每当客户端连接后启动一个ServerThread 线程为该客户服务 + new Thread(new ServerThread(socket)).start(); + } } - } } diff --git a/network/src/main/java/com/github/kuangcp/port/LogicThreadTest.java b/network/src/main/java/com/github/kuangcp/port/LogicThreadTest.java index ea661544..633d13bd 100644 --- a/network/src/main/java/com/github/kuangcp/port/LogicThreadTest.java +++ b/network/src/main/java/com/github/kuangcp/port/LogicThreadTest.java @@ -9,17 +9,17 @@ @Slf4j public class LogicThreadTest { - public static void main(String[] args) { - ServerSocket serverSocket; - Socket socket; - try { - serverSocket = new ServerSocket(10000); - socket = serverSocket.accept(); - LogicThread t = new LogicThread(socket); - t.start(); - } catch (IOException e) { - log.error("", e); + public static void main(String[] args) { + ServerSocket serverSocket; + Socket socket; + try { + serverSocket = new ServerSocket(10000); + socket = serverSocket.accept(); + LogicThread t = new LogicThread(socket); + t.start(); + } catch (IOException e) { + log.error("", e); + } } - } } diff --git a/network/src/main/java/com/github/kuangcp/port/MulSocketServer.java b/network/src/main/java/com/github/kuangcp/port/MulSocketServer.java index 007e13a3..5e2b99e6 100644 --- a/network/src/main/java/com/github/kuangcp/port/MulSocketServer.java +++ b/network/src/main/java/com/github/kuangcp/port/MulSocketServer.java @@ -1,51 +1,57 @@ package com.github.kuangcp.port; import com.github.kuangcp.io.ResourceTool; +import com.github.kuangcp.runable.GreetingClient; +import lombok.extern.slf4j.Slf4j; + import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; -import lombok.extern.slf4j.Slf4j; /** - * 复用连接的echo服务器 功能:将客户端发送的内容反馈给客户端 + * 复用连接的echo服务器 + * + * 功能:将客户端发送的内容反馈给客户端 + * + * @see GreetingClient 客户端 */ @Slf4j public class MulSocketServer { - public static void main(String[] args) { - ServerSocket serverSocket = null; - Socket socket = null; - OutputStream os = null; - InputStream is = null; - // 监听端口号 - int port = 10000; - try { - // 建立连接 - serverSocket = new ServerSocket(port); - System.out.println("服务器已启动:"); - // 获得连接 - socket = serverSocket.accept(); - // 初始化流 - is = socket.getInputStream(); - os = socket.getOutputStream(); - byte[] b = new byte[1024]; - for (int i = 0; i < 3; i++) { - int n = is.read(b); - // 输出 - System.out.println("客户端发送内容为:" + new String(b, 0, n)); - // 向客户端发送反馈内容 - os.write(b, 0, n); - } - } catch (Exception e) { - log.error(e.getMessage(), e); - } finally { - try { - ResourceTool.close(os, is, socket, serverSocket); - } catch (Exception e) { - log.error(e.getMessage(), e); - } + public static void main(String[] args) { + ServerSocket serverSocket = null; + Socket socket = null; + OutputStream os = null; + InputStream is = null; + // 监听端口号 + int port = 10000; + try { + // 建立连接 + serverSocket = new ServerSocket(port); + System.out.println("服务器已启动:"); + // 获得连接 + socket = serverSocket.accept(); + // 初始化流 + is = socket.getInputStream(); + os = socket.getOutputStream(); + byte[] b = new byte[1024]; + for (int i = 0; i < 3; i++) { + int n = is.read(b); + // 输出 + System.out.println("客户端发送内容为:" + new String(b, 0, n)); + // 向客户端发送反馈内容 + os.write(b, 0, n); + } + } catch (Exception e) { + log.error(e.getMessage(), e); + } finally { + try { + ResourceTool.close(os, is, socket, serverSocket); + } catch (Exception e) { + log.error(e.getMessage(), e); + } + } } - } } \ No newline at end of file diff --git a/network/src/main/java/com/github/kuangcp/runable/GreetingClient.java b/network/src/main/java/com/github/kuangcp/runable/GreetingClient.java index e0cfd8ae..108d72c2 100644 --- a/network/src/main/java/com/github/kuangcp/runable/GreetingClient.java +++ b/network/src/main/java/com/github/kuangcp/runable/GreetingClient.java @@ -14,61 +14,60 @@ @Slf4j public class GreetingClient { - private static Logger logger = LoggerFactory.getLogger(GreetingClient.class); + private static Logger logger = LoggerFactory.getLogger(GreetingClient.class); - public static void main(String[] s) { -// In(); - String[] args = {"localhost", "10000"}; - String serverName = args[0]; - int port = Integer.parseInt(args[1]); + public static void main(String[] s) { + String[] args = {"localhost", "10000"}; + String serverName = args[0]; + int port = Integer.parseInt(args[1]); - try { - Socket client = new Socket(serverName, port); - while (true) { - logger.info("### Connecting to " + serverName + " on port " + port); + try { + Socket client = new Socket(serverName, port); + while (true) { + logger.info("### Connecting to " + serverName + " on port " + port); - logger.info("Just connected to " + client.getRemoteSocketAddress()); - OutputStream outToServer = client.getOutputStream(); + logger.info("Just connected to " + client.getRemoteSocketAddress()); + OutputStream outToServer = client.getOutputStream(); - DataOutputStream out = new DataOutputStream(outToServer); - out.writeUTF("[客户端发送 : Hello from " + client.getLocalSocketAddress() + "]"); + DataOutputStream out = new DataOutputStream(outToServer); + out.writeUTF("[客户端发送 : Hello from " + client.getLocalSocketAddress() + "]"); - InputStream inFromServer = client.getInputStream(); - DataInputStream in = new DataInputStream(inFromServer); - logger.info("接收到服务器:" + in.readUTF()); + InputStream inFromServer = client.getInputStream(); + DataInputStream in = new DataInputStream(inFromServer); + logger.info("接收到服务器:" + in.readUTF()); - while (true) { - InputStream inFromServer2 = client.getInputStream(); - DataInputStream in2 = new DataInputStream(inFromServer2); - logger.info("接收到服务器:" + in2.readUTF()); + while (true) { + InputStream inFromServer2 = client.getInputStream(); + DataInputStream in2 = new DataInputStream(inFromServer2); + logger.info("接收到服务器:" + in2.readUTF()); - Scanner scanner = new Scanner(System.in); - String temp = scanner.nextLine(); - out = new DataOutputStream(outToServer); - out.writeUTF(temp); - if ("9090".equals(temp)) { - break; - } - } - break; - } - client.close(); + Scanner scanner = new Scanner(System.in); + String temp = scanner.nextLine(); + out = new DataOutputStream(outToServer); + out.writeUTF(temp); + if ("9090".equals(temp)) { + break; + } + } + break; + } + client.close(); - } catch (IOException e) { - log.error("", e); + } catch (IOException e) { + log.error("", e); + } } - } - public static void In() { + public static void In() { // Scanner scanner = new Scanner(System.in); // logger.info(scanner.nextLine()); // scanner.nextLine(); - BufferedReader sin = new BufferedReader(new InputStreamReader(System.in)); - try { - logger.info(sin.readLine()); - } catch (IOException e) { - log.error("", e); + BufferedReader sin = new BufferedReader(new InputStreamReader(System.in)); + try { + logger.info(sin.readLine()); + } catch (IOException e) { + log.error("", e); + } } - } } diff --git a/network/src/main/java/com/github/kuangcp/runable/GreetingServer.java b/network/src/main/java/com/github/kuangcp/runable/GreetingServer.java index 20f9761e..1259b668 100644 --- a/network/src/main/java/com/github/kuangcp/runable/GreetingServer.java +++ b/network/src/main/java/com/github/kuangcp/runable/GreetingServer.java @@ -18,57 +18,57 @@ @Slf4j public class GreetingServer extends Thread { - private ServerSocket serverSocket; + private ServerSocket serverSocket; - public GreetingServer(int port) throws IOException { - serverSocket = new ServerSocket(port); - serverSocket.setSoTimeout(10000); - } + public GreetingServer(int port) throws IOException { + serverSocket = new ServerSocket(port); + serverSocket.setSoTimeout(10000); + } - public void run() { - while (true) { - try { - log.info("##### Waiting for client on port " + serverSocket.getLocalPort() + "..."); - Socket server = serverSocket.accept(); - Scanner scanner = new Scanner(System.in); + public void run() { + while (true) { + try { + log.info("##### Waiting for client on port " + serverSocket.getLocalPort() + "..."); + Socket server = serverSocket.accept(); + Scanner scanner = new Scanner(System.in); - log.info("Just connected to " + server.getRemoteSocketAddress()); - DataInputStream in = new DataInputStream(server.getInputStream()); - log.info("接收到的:" + in.readUTF()); - // 对客户端发出的消息 - DataOutputStream out = new DataOutputStream(server.getOutputStream()); + log.info("Just connected to " + server.getRemoteSocketAddress()); + DataInputStream in = new DataInputStream(server.getInputStream()); + log.info("接收到的:" + in.readUTF()); + // 对客户端发出的消息 + DataOutputStream out = new DataOutputStream(server.getOutputStream()); - while (true) { - String temp = scanner.nextLine(); - log.info("input:" + temp); - out.writeUTF(temp); - if ("90".equals(temp)) { - break; - } + while (true) { + String temp = scanner.nextLine(); + log.info("input:" + temp); + out.writeUTF(temp); + if ("90".equals(temp)) { + break; + } - } - out.writeUTF( - "Thank you for connecting to " + server.getLocalSocketAddress() + " Goodbye!"); + } + out.writeUTF( + "Thank you for connecting to " + server.getLocalSocketAddress() + " Goodbye!"); - server.close(); - } catch (SocketTimeoutException s) { - log.info("Socket timed out!"); - break; - } catch (IOException e) { - log.error("", e); - break; - } + server.close(); + } catch (SocketTimeoutException s) { + log.info("Socket timed out!"); + break; + } catch (IOException e) { + log.error("", e); + break; + } + } } - } - public static void main(String[] args) { + public static void main(String[] args) { // int port = Integer.parseInt(args[0]); - int port = 10000; - try { - Thread t = new GreetingServer(port); - t.start(); - } catch (IOException e) { - log.error("", e); + int port = 10000; + try { + Thread t = new GreetingServer(port); + t.start(); + } catch (IOException e) { + log.error("", e); + } } - } } diff --git a/network/src/main/java/com/github/kuangcp/selfclose/SocketSelfClose.java b/network/src/main/java/com/github/kuangcp/selfclose/SocketSelfClose.java index ae3b2e33..014fda31 100644 --- a/network/src/main/java/com/github/kuangcp/selfclose/SocketSelfClose.java +++ b/network/src/main/java/com/github/kuangcp/selfclose/SocketSelfClose.java @@ -10,27 +10,27 @@ * Socket 半关闭状态,可以输入输出流单独控制关闭,但是关闭后就不能开启了 * * 只适用于一站式的通信协议,例如HTTP协议-客户端连接到服务器后,开始发送请求数据,发送完成后无需再次发送数据 - * 客户端,使用chatting room下的客户端即可进行测试验证 + * @see com.github.kuangcp.bio.chattingroom.SocketClient 客户端,使用chatting room下的客户端即可进行测试验证 */ public class SocketSelfClose { - public static void main(String[] strings) throws Exception { - ServerSocket serverSocket = new ServerSocket(30000); - Socket socket = serverSocket.accept(); - PrintStream printStream = new PrintStream(socket.getOutputStream()); + public static void main(String[] strings) throws Exception { + ServerSocket serverSocket = new ServerSocket(30000); + Socket socket = serverSocket.accept(); + PrintStream printStream = new PrintStream(socket.getOutputStream()); - printStream.println("服务器第一行数据"); - printStream.println("服务器第二行数据"); + printStream.println("服务器第一行数据"); + printStream.println("服务器第二行数据"); - //关闭Socket的输出流, - socket.shutdownOutput(); - System.out.println("socket对象是否关闭" + socket.isClosed()); - Scanner scanner = new Scanner(socket.getInputStream()); - while (scanner.hasNextLine()) { - System.out.println(scanner.nextLine()); + //关闭Socket的输出流, + socket.shutdownOutput(); + System.out.println("socket对象是否关闭" + socket.isClosed()); + Scanner scanner = new Scanner(socket.getInputStream()); + while (scanner.hasNextLine()) { + System.out.println(scanner.nextLine()); + } + scanner.close(); + socket.close(); + serverSocket.close(); } - scanner.close(); - socket.close(); - serverSocket.close(); - } } diff --git a/test/src/main/java/com/github/kuangcp/util/StrUtil.java b/test/src/main/java/com/github/kuangcp/util/StrUtil.java index b0c74870..4e4fe4b1 100644 --- a/test/src/main/java/com/github/kuangcp/util/StrUtil.java +++ b/test/src/main/java/com/github/kuangcp/util/StrUtil.java @@ -2,6 +2,7 @@ import org.apache.commons.lang3.StringUtils; +import java.security.SecureRandom; import java.util.Arrays; import java.util.List; import java.util.Random; @@ -9,7 +10,7 @@ import java.util.stream.Stream; /** - * @author kuangchengping@sinohealth.cn + * @author Kuangcp * 2023-06-20 18:42 */ public class StrUtil { @@ -55,4 +56,13 @@ public static String randomAlphaAL(int len) { } return builder.toString(); } + + public static String randomAlphaAS(int len) { + Random random = new SecureRandom(); + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < len; i++) { + builder.append(ALPHA[random.nextInt(LIST.size())]); + } + return builder.toString(); + } } diff --git a/test/src/test/java/jmh/GuideTest.java b/test/src/test/java/jmh/GuideTest.java index 184a055b..4eb8c941 100644 --- a/test/src/test/java/jmh/GuideTest.java +++ b/test/src/test/java/jmh/GuideTest.java @@ -7,10 +7,10 @@ import org.openjdk.jmh.runner.options.Options; import org.openjdk.jmh.runner.options.OptionsBuilder; -import java.util.HashSet; -import java.util.Set; -import java.util.UUID; +import java.util.*; import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; +import java.util.stream.Stream; /** * @@ -75,5 +75,12 @@ public void testDiff() throws Exception { } System.out.println(re2.size()); + Set re3 = new HashSet<>(); + for (int i = 0; i < 10000; i++) { + re3.add(StrUtil.randomAlphaAS(4)); + } + System.out.println(re3.size()); } + + } From 2be3814076ae4af4fca9b00d999f475a34bca5d8 Mon Sep 17 00:00:00 2001 From: kuangcp Date: Mon, 4 Mar 2024 14:40:28 +0800 Subject: [PATCH 333/476] recovery oom --- class/src/main/java/jvm/oom/HeapOOM.java | 44 ++++++++++++++---- class/src/test/java/jvm/oom/HeapOOMTest.java | 47 +++++++++++--------- 2 files changed, 61 insertions(+), 30 deletions(-) diff --git a/class/src/main/java/jvm/oom/HeapOOM.java b/class/src/main/java/jvm/oom/HeapOOM.java index 0a541b28..98f1e710 100644 --- a/class/src/main/java/jvm/oom/HeapOOM.java +++ b/class/src/main/java/jvm/oom/HeapOOM.java @@ -1,29 +1,55 @@ package jvm.oom; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.WeakHashMap; -import java.util.concurrent.TimeUnit; - import lombok.extern.slf4j.Slf4j; +import java.util.*; +import java.util.concurrent.TimeUnit; + /** * @author kuangcp on 4/3/19-10:11 PM */ @Slf4j public class HeapOOM { - void createArray() { + public void createArray() { List data = new ArrayList<>(); while (true) { - data.add(new byte[1024 * 1024]); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { log.error("", e); } + log.info("size={}", data.size()); + data.add(new byte[1024 * 1024]); + } + } + + public void createArrayRecovery() { + try { + List data = new ArrayList<>(); + while (true) { + try { + TimeUnit.MILLISECONDS.sleep(500); + } catch (InterruptedException e) { + log.error("", e); + } + log.info("size={}", data.size()); + data.add(new byte[1024 * 1024]); + } + // 如果是 Exception 就无法捕获OOM Error 同样会中断执行 + // 如果能Catch到OOMError,就可以维持线程的继续执行 Throwable Error 等都可以实现 +// } catch (Exception e) { + } catch (Throwable e) { + log.error("", e); + } + + while (true) { + try { + TimeUnit.MILLISECONDS.sleep(500); + } catch (InterruptedException e) { + log.error("", e); + } + log.info("do something"); } } diff --git a/class/src/test/java/jvm/oom/HeapOOMTest.java b/class/src/test/java/jvm/oom/HeapOOMTest.java index b7d3b093..94ae28d6 100644 --- a/class/src/test/java/jvm/oom/HeapOOMTest.java +++ b/class/src/test/java/jvm/oom/HeapOOMTest.java @@ -14,25 +14,30 @@ @Slf4j public class HeapOOMTest { - private HeapOOM heapOOM = new HeapOOM(); - - @Test - public void testCreateArray() { - heapOOM.createArray(); - } - - @Test - public void testCreateMap() { - heapOOM.createMap(); - } - - @Test - public void testCreateWeakMap() { - heapOOM.createWeakMap(); - } - - @Test - public void testOtherThreadWithOOM() throws Exception { - heapOOM.otherThreadWithOOM(); - } + private HeapOOM heapOOM = new HeapOOM(); + + @Test + public void testCreateArray() { + heapOOM.createArray(); + } + + @Test + public void testCreateArrayRecovery() throws Exception { + heapOOM.createArrayRecovery(); + } + + @Test + public void testCreateMap() { + heapOOM.createMap(); + } + + @Test + public void testCreateWeakMap() { + heapOOM.createWeakMap(); + } + + @Test + public void testOtherThreadWithOOM() throws Exception { + heapOOM.otherThreadWithOOM(); + } } \ No newline at end of file From ec4dd34fb4fdcf008692bcb02311514cfac5d880 Mon Sep 17 00:00:00 2001 From: kuangcp Date: Mon, 11 Mar 2024 18:34:04 +0800 Subject: [PATCH 334/476] class --- class/pom.xml | 12 ++- .../github/kuangcp/bytecode/aop/AddImpl.java | 12 +++ .../com/github/kuangcp/bytecode/aop/Cal.java | 11 +++ .../kuangcp/{ => bytecode}/aop/Readme.md | 0 .../java/com/github/kuangcp/js/JsUtil.java | 49 +++++++++++++ class/src/main/resources/js/add.js | 3 + .../kuangcp/bytecode/aop/AddImplTest.java | 34 +++++++++ .../com/github/kuangcp/js/JsUtilTest.java | 20 +++++ .../java/jvm/load/ShowClassLoaderTest.java | 22 +++--- .../main/java/reactor/proxy/ProxyServer.java | 73 +++++++++++++++++++ netty/src/main/java/reactor/proxy/Readme.md | 1 + .../main/java/reactor/proxy/domain/Resp.java | 36 +++++++++ .../main/java/reactor/websocket/Client.java | 2 +- .../main/java/reactor/websocket/Server.java | 2 +- pom.xml | 5 ++ 15 files changed, 265 insertions(+), 17 deletions(-) create mode 100644 class/src/main/java/com/github/kuangcp/bytecode/aop/AddImpl.java create mode 100644 class/src/main/java/com/github/kuangcp/bytecode/aop/Cal.java rename class/src/main/java/com/github/kuangcp/{ => bytecode}/aop/Readme.md (100%) create mode 100644 class/src/main/java/com/github/kuangcp/js/JsUtil.java create mode 100644 class/src/main/resources/js/add.js create mode 100644 class/src/test/java/com/github/kuangcp/bytecode/aop/AddImplTest.java create mode 100644 class/src/test/java/com/github/kuangcp/js/JsUtilTest.java create mode 100644 netty/src/main/java/reactor/proxy/ProxyServer.java create mode 100644 netty/src/main/java/reactor/proxy/Readme.md create mode 100644 netty/src/main/java/reactor/proxy/domain/Resp.java diff --git a/class/pom.xml b/class/pom.xml index fd7173bb..4bde3645 100644 --- a/class/pom.xml +++ b/class/pom.xml @@ -25,16 +25,20 @@ cglib 3.2.4 + + org.javassist + javassist + com.github.kuangcp kcp-tool - - - - + + + + org.apache.commons diff --git a/class/src/main/java/com/github/kuangcp/bytecode/aop/AddImpl.java b/class/src/main/java/com/github/kuangcp/bytecode/aop/AddImpl.java new file mode 100644 index 00000000..9338b5af --- /dev/null +++ b/class/src/main/java/com/github/kuangcp/bytecode/aop/AddImpl.java @@ -0,0 +1,12 @@ +package com.github.kuangcp.bytecode.aop; + +/** + * + * @author Kuangcp + * 2024-03-11 18:24 + */ +public class AddImpl implements Cal { + public int add(int a, int b) { + return a + b; + } +} diff --git a/class/src/main/java/com/github/kuangcp/bytecode/aop/Cal.java b/class/src/main/java/com/github/kuangcp/bytecode/aop/Cal.java new file mode 100644 index 00000000..f2e601ab --- /dev/null +++ b/class/src/main/java/com/github/kuangcp/bytecode/aop/Cal.java @@ -0,0 +1,11 @@ +package com.github.kuangcp.bytecode.aop; + +/** + * + * @author Kuangcp + * 2024-03-11 18:26 + */ +public interface Cal { + + int add(int a, int b); +} diff --git a/class/src/main/java/com/github/kuangcp/aop/Readme.md b/class/src/main/java/com/github/kuangcp/bytecode/aop/Readme.md similarity index 100% rename from class/src/main/java/com/github/kuangcp/aop/Readme.md rename to class/src/main/java/com/github/kuangcp/bytecode/aop/Readme.md diff --git a/class/src/main/java/com/github/kuangcp/js/JsUtil.java b/class/src/main/java/com/github/kuangcp/js/JsUtil.java new file mode 100644 index 00000000..6ca6b666 --- /dev/null +++ b/class/src/main/java/com/github/kuangcp/js/JsUtil.java @@ -0,0 +1,49 @@ +package com.github.kuangcp.js; + +import jdk.nashorn.api.scripting.NashornScriptEngineFactory; + +import javax.script.Invocable; +import javax.script.ScriptEngine; +import javax.script.ScriptEngineFactory; +import javax.script.ScriptException; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; + +/** + * + * @author Kuangcp + * 2024-03-11 17:36 + */ +public class JsUtil { + + private static final ScriptEngineFactory engineFactory; + + static { + engineFactory = new NashornScriptEngineFactory(); + } + + + public static Object invoke(Invocable invocable, String functionName, Object... args) throws Exception { + if (invocable != null) { + return invocable.invokeFunction(functionName, args); + } + return null; + } + + public static Invocable load(String path) throws IOException, ScriptException { + InputStream stream = JsUtil.class.getClassLoader().getResourceAsStream(path); + if (stream == null) { + throw new RuntimeException("file not exist"); + } + + try (InputStreamReader reader = new InputStreamReader(stream)) { + ScriptEngine engine = engineFactory.getScriptEngine(); + engine.eval(reader); + if (engine instanceof Invocable) { + return (Invocable) engine; + } + return null; + } + } +} diff --git a/class/src/main/resources/js/add.js b/class/src/main/resources/js/add.js new file mode 100644 index 00000000..85f080e7 --- /dev/null +++ b/class/src/main/resources/js/add.js @@ -0,0 +1,3 @@ +function add(a, b) { + return a + b; +} \ No newline at end of file diff --git a/class/src/test/java/com/github/kuangcp/bytecode/aop/AddImplTest.java b/class/src/test/java/com/github/kuangcp/bytecode/aop/AddImplTest.java new file mode 100644 index 00000000..0f8ed9e6 --- /dev/null +++ b/class/src/test/java/com/github/kuangcp/bytecode/aop/AddImplTest.java @@ -0,0 +1,34 @@ +package com.github.kuangcp.bytecode.aop; + +import javassist.ClassPool; +import javassist.CtClass; +import javassist.CtMethod; +import org.junit.Test; + +/** + * + * @author Kuangcp + * 2024-03-11 18:24 + */ +public class AddImplTest { + @Test + public void testNormal() throws Exception { + AddImpl add = new AddImpl(); + int r = add.add(1, 2); + System.out.println(r); + } + + @Test + public void testInject() throws Exception { + ClassPool pool = ClassPool.getDefault(); + CtClass cc = pool.get("com.github.kuangcp.bytecode.aop.AddImpl"); + CtMethod ctMethod = cc.getDeclaredMethod("add", new CtClass[]{pool.get("int"), pool.get("int")}); + ctMethod.insertBefore("System.out.println(\"insert before by Javassist: \"+a+\" \"+b);"); + ctMethod.insertAfter("System.out.println(\"insert after by Javassist\");"); + Class klass = cc.toClass(); + System.out.println(klass.getName()); + Cal cal = (Cal) klass.getDeclaredConstructor().newInstance(); + int add = cal.add(1, 4); + System.out.println(add); + } +} diff --git a/class/src/test/java/com/github/kuangcp/js/JsUtilTest.java b/class/src/test/java/com/github/kuangcp/js/JsUtilTest.java new file mode 100644 index 00000000..5ee6a083 --- /dev/null +++ b/class/src/test/java/com/github/kuangcp/js/JsUtilTest.java @@ -0,0 +1,20 @@ +package com.github.kuangcp.js; + +import org.junit.Test; + +import javax.script.Invocable; + +/** + * + * @author Kuangcp + * 2024-03-11 17:39 + */ +public class JsUtilTest { + + @Test + public void testAdd() throws Exception { + Invocable func = JsUtil.load("js/add.js"); + Object res = func.invokeFunction("add", 3, 5); + System.out.println(res); + } +} diff --git a/class/src/test/java/jvm/load/ShowClassLoaderTest.java b/class/src/test/java/jvm/load/ShowClassLoaderTest.java index 25485c77..e413259e 100644 --- a/class/src/test/java/jvm/load/ShowClassLoaderTest.java +++ b/class/src/test/java/jvm/load/ShowClassLoaderTest.java @@ -1,25 +1,25 @@ package jvm.load; -import java.util.ArrayList; import lombok.extern.slf4j.Slf4j; import org.junit.Test; +import java.util.ArrayList; + /** * @author https://github.com/kuangcp on 2020-09-12 19:48 */ @Slf4j public class ShowClassLoaderTest { - /** - * 这里分别对应三种不同类型的类加载器:AppClassLoader、ExtClassLoader 和 BootstrapClassLoader(显示为 null)。 - */ - @Test - public void test() throws Exception { - log.info("Classloader of this class:{}", ShowClassLoaderTest.class.getClassLoader()); + /** + * 这里分别对应三种不同类型的类加载器:AppClassLoader、ExtClassLoader 和 BootstrapClassLoader(显示为 null)。 + */ + @Test + public void test() throws Exception { + log.info("Classloader of this class:{}", ShowClassLoaderTest.class.getClassLoader()); - log.info("Classloader of Logging:{}", - com.sun.java.accessibility.util.EventID.class.getClassLoader()); + log.info("Classloader of Logging:{}", com.sun.java.accessibility.util.EventID.class.getClassLoader()); - log.info("Classloader of ArrayList:{}", ArrayList.class.getClassLoader()); - } + log.info("Classloader of ArrayList:{}", ArrayList.class.getClassLoader()); + } } diff --git a/netty/src/main/java/reactor/proxy/ProxyServer.java b/netty/src/main/java/reactor/proxy/ProxyServer.java new file mode 100644 index 00000000..34027984 --- /dev/null +++ b/netty/src/main/java/reactor/proxy/ProxyServer.java @@ -0,0 +1,73 @@ +package reactor.proxy; + + +import io.netty.buffer.ByteBuf; +import lombok.extern.slf4j.Slf4j; +import reactor.core.publisher.Mono; +import reactor.netty.DisposableServer; +import reactor.netty.NettyOutbound; +import reactor.netty.http.client.HttpClient; +import reactor.netty.http.server.HttpServer; +import reactor.netty.http.server.HttpServerRequest; +import reactor.netty.http.server.HttpServerResponse; + +/** + * https://projectreactor.io/docs/netty/release/reference/index.html + * + * @author Github + * 2023-10-10 09:52 + */ +@Slf4j +public class ProxyServer { + + private static final HttpClient client = HttpClient.create(); + + public static void main(String[] args) { + DisposableServer server = HttpServer.create() + .handle(ProxyServer::handle) + .port(32990) + .bindNow(); + + server.onDispose().block(); + } + + private static NettyOutbound handle(HttpServerRequest r, HttpServerResponse w) { +// Mono response = client.request(r.method()).uri(r.uri()).send(r.receive()) +// .response(); +// response.subscribe(v -> { +// log.info("v={}", v); +// }); + + Mono body = client.request(r.method()).uri(r.uri()).send(r.receive()) + .responseSingle((resp, bytes) -> { + w.status(resp.status()); + w.headers(resp.responseHeaders()); + return bytes; + }); + return w.send(body); + +// Mono body = client.request(r.method()).uri(r.uri()).send(r.receive()).response() +// .handle((a, b) -> { +// HttpHeaders header = a.responseHeaders(); +// header.add("proxy", "xxx"); +// w.status(a.status()).headers(header); +// log.info("={} {}", a.toString(), a.getClass()); +// w.headers(header); +// +// b.next("xtt"); +// b.complete(); +// }); +// return w.sendString(body); + + + // Bug +// Mono respMono = client.request(r.method()).uri(r.uri()).send(r.receive()) +// .responseSingle((resp, bytes) -> { +// return Mono.just(new Resp(resp.status(), resp.responseHeaders(), bytes)); +// }); +// respMono.subscribe(resp -> { +// w.status(resp.getStatus()).headers(resp.getResponseHeaders()).send(resp.getDataStream()); +// }); +// return respMono.then(); + } +} diff --git a/netty/src/main/java/reactor/proxy/Readme.md b/netty/src/main/java/reactor/proxy/Readme.md new file mode 100644 index 00000000..04604c67 --- /dev/null +++ b/netty/src/main/java/reactor/proxy/Readme.md @@ -0,0 +1 @@ +curl -X POST -x 127.0.0.1:32990 localhost:30040/api/task/rt diff --git a/netty/src/main/java/reactor/proxy/domain/Resp.java b/netty/src/main/java/reactor/proxy/domain/Resp.java new file mode 100644 index 00000000..8f9c7901 --- /dev/null +++ b/netty/src/main/java/reactor/proxy/domain/Resp.java @@ -0,0 +1,36 @@ +package reactor.proxy.domain; + +import io.netty.buffer.ByteBuf; +import io.netty.handler.codec.http.HttpHeaders; +import io.netty.handler.codec.http.HttpResponseStatus; +import org.reactivestreams.Publisher; + +/** + * + * @author Kuangcp + * 2024-03-05 12:03 + */ +public class Resp { + + HttpResponseStatus status; + HttpHeaders responseHeaders; + Publisher dataStream; + + public Resp(HttpResponseStatus status, HttpHeaders responseHeaders, Publisher dataStream) { + this.status = status; + this.responseHeaders = responseHeaders; + this.dataStream = dataStream; + } + + public HttpResponseStatus getStatus() { + return status; + } + + public HttpHeaders getResponseHeaders() { + return responseHeaders; + } + + public Publisher getDataStream() { + return dataStream; + } +} diff --git a/netty/src/main/java/reactor/websocket/Client.java b/netty/src/main/java/reactor/websocket/Client.java index d76907cc..8b1455e4 100644 --- a/netty/src/main/java/reactor/websocket/Client.java +++ b/netty/src/main/java/reactor/websocket/Client.java @@ -7,7 +7,7 @@ /** * - * @author kuangchengping@sinohealth.cn + * @author Kuangcp * 2024-02-28 20:29 */ public class Client { diff --git a/netty/src/main/java/reactor/websocket/Server.java b/netty/src/main/java/reactor/websocket/Server.java index 28271c8a..176568e9 100644 --- a/netty/src/main/java/reactor/websocket/Server.java +++ b/netty/src/main/java/reactor/websocket/Server.java @@ -8,7 +8,7 @@ /** * - * @author kuangchengping@sinohealth.cn + * @author Kuangcp * 2024-02-28 20:27 */ public class Server { diff --git a/pom.xml b/pom.xml index 5ed0e2cf..3ade9de7 100644 --- a/pom.xml +++ b/pom.xml @@ -248,6 +248,11 @@ aspectjweaver 1.9.0 + + org.javassist + javassist + 3.27.0-GA + org.springframework.boot From 05b8d75cfe1cf065226ab3c25250a09a04ee3ed6 Mon Sep 17 00:00:00 2001 From: kuangcp Date: Tue, 12 Mar 2024 15:41:38 +0800 Subject: [PATCH 335/476] javac compile bug --- .../com/github/kuangcp/ValueCacheDemo.java | 27 ------ .../{distribution => sample}/SampleAble.java | 2 +- .../{distribution => sample}/SampleUtil.java | 2 +- .../generic/constraint/GenericArrayTest.java | 61 ++++++------- .../{distribution => sample}/DogRef.java | 2 +- .../SampleUtilTest.java | 2 +- .../syntax/base/BaseTypeDefaultValueTest.java | 35 ++++++++ .../src/test/java/syntax/base/FinalTest.java | 38 +++++++++ .../basetype/BaseTypeDefaultValueTest.java | 35 -------- .../java/syntax/integer/IntegerCacheTest.java | 85 +++++++++++-------- 10 files changed, 159 insertions(+), 130 deletions(-) delete mode 100644 class/src/main/java/com/github/kuangcp/ValueCacheDemo.java rename class/src/main/java/com/github/kuangcp/generic/{distribution => sample}/SampleAble.java (66%) rename class/src/main/java/com/github/kuangcp/generic/{distribution => sample}/SampleUtil.java (99%) rename class/src/test/java/com/github/kuangcp/generic/{distribution => sample}/DogRef.java (90%) rename class/src/test/java/com/github/kuangcp/generic/{distribution => sample}/SampleUtilTest.java (98%) create mode 100644 class/src/test/java/syntax/base/BaseTypeDefaultValueTest.java create mode 100644 class/src/test/java/syntax/base/FinalTest.java delete mode 100644 class/src/test/java/syntax/basetype/BaseTypeDefaultValueTest.java diff --git a/class/src/main/java/com/github/kuangcp/ValueCacheDemo.java b/class/src/main/java/com/github/kuangcp/ValueCacheDemo.java deleted file mode 100644 index 24943b6f..00000000 --- a/class/src/main/java/com/github/kuangcp/ValueCacheDemo.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.github.kuangcp; - -/** - * Created by https://github.com/kuangcp on 17-8-21 下午2:41 - */ -public class ValueCacheDemo { - - public static void main(String[] s) { - testInteger(); - } - - //java中的基本类型的包装类、其中 Byte、Boolean、Short、Character、Integer、Long 实现了常量池技术 - // (除了Boolean,都只对小于128的值才支持) - private static void testInteger() { -// 对于-128~127的Integer对象才会到IntegerCache里获取缓存,使用常量池技术 - Integer i1 = 100; - Integer i2 = 100; - // 上面两行代码,使用自动装箱特性,编译成 - // Integer i1 = Integer.valueOf(100); - // Integer i2 = Integer.valueOf(100); - System.out.println(i1 == i2); - - Integer i3 = 128; - Integer i4 = 128; - System.out.println(i3 == i4); - } -} diff --git a/class/src/main/java/com/github/kuangcp/generic/distribution/SampleAble.java b/class/src/main/java/com/github/kuangcp/generic/sample/SampleAble.java similarity index 66% rename from class/src/main/java/com/github/kuangcp/generic/distribution/SampleAble.java rename to class/src/main/java/com/github/kuangcp/generic/sample/SampleAble.java index edef0c73..cc758a8d 100644 --- a/class/src/main/java/com/github/kuangcp/generic/distribution/SampleAble.java +++ b/class/src/main/java/com/github/kuangcp/generic/sample/SampleAble.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.generic.distribution; +package com.github.kuangcp.generic.sample; /** * @author kuangcp on 3/21/19-5:42 PM diff --git a/class/src/main/java/com/github/kuangcp/generic/distribution/SampleUtil.java b/class/src/main/java/com/github/kuangcp/generic/sample/SampleUtil.java similarity index 99% rename from class/src/main/java/com/github/kuangcp/generic/distribution/SampleUtil.java rename to class/src/main/java/com/github/kuangcp/generic/sample/SampleUtil.java index a6da1648..917b6a3c 100644 --- a/class/src/main/java/com/github/kuangcp/generic/distribution/SampleUtil.java +++ b/class/src/main/java/com/github/kuangcp/generic/sample/SampleUtil.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.generic.distribution; +package com.github.kuangcp.generic.sample; import lombok.extern.slf4j.Slf4j; import org.apache.commons.math3.distribution.EnumeratedIntegerDistribution; diff --git a/class/src/test/java/com/github/kuangcp/generic/constraint/GenericArrayTest.java b/class/src/test/java/com/github/kuangcp/generic/constraint/GenericArrayTest.java index d55b2638..9b85f9b8 100644 --- a/class/src/test/java/com/github/kuangcp/generic/constraint/GenericArrayTest.java +++ b/class/src/test/java/com/github/kuangcp/generic/constraint/GenericArrayTest.java @@ -14,38 +14,39 @@ */ public class GenericArrayTest { - // 参数化类型 - @Test(expected = ClassCastException.class) - public void testParametricType(){ - // 不能通过编译 -// List[] list = new ArrayList<>[3]; - // 经过类型擦除后, 就是 List[] 可以转换为 Object[] 就可以放入任意类型的对象, 但是会抛出 ArrayStoreException - - // 声明为通配类型, 然后类型转换 可以绕过编译检查 - List[] list = (List[]) new ArrayList[3]; - list[0] = new ArrayList(); - List strings = list[0]; - strings.add(12); - - // 因为前面绕过了泛型约束, 这里取值就会将Integer强转为String而抛异常 - System.out.println(list[0].get(0)); - } - - // 泛型数组 - @Test - public void testCreate() { - String[] ones = create("one", "two"); - ones[0] = "1"; - - for (String one : ones) { - System.out.println(one); + // 参数化类型 + @Test(expected = ClassCastException.class) + public void testParametricType() { + // 不能通过编译 + // List[] list = new ArrayList<>[3]; + + // 经过类型擦除后, 就是 List[] 可以转换为 Object[] 就可以放入任意类型的对象, 但是会抛出 ArrayStoreException + + // 声明为通配类型, 然后类型转换 可以绕过编译检查 + List[] list = (List[]) new ArrayList[3]; + list[0] = new ArrayList(); + List strings = list[0]; + strings.add(12); + + // 因为前面绕过了泛型约束, 这里取值就会将Integer强转为String而抛异常 + System.out.println(list[0].get(0)); } - } - // 本质上是得到了 Object[] 强转为 T[] - private T[] create(T... element) { - return (T[]) Arrays.asList(element).toArray(); - } + // 泛型数组 + @Test + public void testCreate() { + String[] ones = create("one", "two"); + ones[0] = "1"; + + for (String one : ones) { + System.out.println(one); + } + } + + // 本质上是得到了 Object[] 强转为 T[] + private T[] create(T... element) { + return (T[]) Arrays.asList(element).toArray(); + } // private T[] create2(T... element) { // T[] data = new T[element.length]; diff --git a/class/src/test/java/com/github/kuangcp/generic/distribution/DogRef.java b/class/src/test/java/com/github/kuangcp/generic/sample/DogRef.java similarity index 90% rename from class/src/test/java/com/github/kuangcp/generic/distribution/DogRef.java rename to class/src/test/java/com/github/kuangcp/generic/sample/DogRef.java index cc71e91c..b53fbf80 100644 --- a/class/src/test/java/com/github/kuangcp/generic/distribution/DogRef.java +++ b/class/src/test/java/com/github/kuangcp/generic/sample/DogRef.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.generic.distribution; +package com.github.kuangcp.generic.sample; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; diff --git a/class/src/test/java/com/github/kuangcp/generic/distribution/SampleUtilTest.java b/class/src/test/java/com/github/kuangcp/generic/sample/SampleUtilTest.java similarity index 98% rename from class/src/test/java/com/github/kuangcp/generic/distribution/SampleUtilTest.java rename to class/src/test/java/com/github/kuangcp/generic/sample/SampleUtilTest.java index f286c848..5077082f 100644 --- a/class/src/test/java/com/github/kuangcp/generic/distribution/SampleUtilTest.java +++ b/class/src/test/java/com/github/kuangcp/generic/sample/SampleUtilTest.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.generic.distribution; +package com.github.kuangcp.generic.sample; import com.github.kuangcp.time.GetRunTime; import org.junit.After; diff --git a/class/src/test/java/syntax/base/BaseTypeDefaultValueTest.java b/class/src/test/java/syntax/base/BaseTypeDefaultValueTest.java new file mode 100644 index 00000000..b906ecd5 --- /dev/null +++ b/class/src/test/java/syntax/base/BaseTypeDefaultValueTest.java @@ -0,0 +1,35 @@ +package syntax.base; + +import lombok.Data; +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; + +/** + * created by https://gitee.com/gin9 + * + * @author kuangcp on 3/12/19-12:02 AM + */ +@Data +@Slf4j +public class BaseTypeDefaultValueTest { + + private short a; + private int b; + private long c; + private char d; + private boolean e; + + @Test + public void testDefaultValue() { + log.info("{} {} {} {} {}", a, b, c, d, e); + + assertThat(a, equalTo((short) 0)); + assertThat(b, equalTo(0)); + assertThat(c, equalTo(0L)); + assertThat(d, equalTo('\0')); + assertThat(e, equalTo(false)); + } +} diff --git a/class/src/test/java/syntax/base/FinalTest.java b/class/src/test/java/syntax/base/FinalTest.java new file mode 100644 index 00000000..ad6b97c6 --- /dev/null +++ b/class/src/test/java/syntax/base/FinalTest.java @@ -0,0 +1,38 @@ +package syntax.base; + +import org.junit.Test; + +import java.util.Objects; +import java.util.Optional; +import java.util.function.Predicate; + +/** + * + * @author Kuangcp + * 2024-03-12 13:44 + */ +public class FinalTest { + + + public static final String x = "x" + System.getenv(""); + public static final String x2 = "x" + Optional.ofNullable(System.getenv("")).orElse("vvv"); + public static final String x3; + public static final String x4; + static Predicate uu = v -> Objects.equals(v, "uu"); + + // 只有这种情况会编译错误 +// private static final String x5 = "x" + Optional.ofNullable(System.getenv("")).filter(v -> Objects.equals(v, "uu")).orElse("vvv"); + + private static final String x6 = "x" + Optional.ofNullable(System.getenv("")).filter(uu).orElse("vvv"); + + static { + x3 = "x" + Optional.ofNullable(System.getenv("")).filter(uu).orElse("vvv"); + x4 = "x" + Optional.ofNullable(System.getenv("")).filter(v -> Objects.equals(v, "uu")).orElse("vvv"); + } + + @Test + public void testFinalInit() throws Exception { + System.out.println(x); + System.out.println(x2); + } +} diff --git a/class/src/test/java/syntax/basetype/BaseTypeDefaultValueTest.java b/class/src/test/java/syntax/basetype/BaseTypeDefaultValueTest.java deleted file mode 100644 index c1ec96da..00000000 --- a/class/src/test/java/syntax/basetype/BaseTypeDefaultValueTest.java +++ /dev/null @@ -1,35 +0,0 @@ -package syntax.basetype; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.equalTo; - -import lombok.Data; -import lombok.extern.slf4j.Slf4j; -import org.junit.Test; - -/** - * created by https://gitee.com/gin9 - * - * @author kuangcp on 3/12/19-12:02 AM - */ -@Data -@Slf4j -public class BaseTypeDefaultValueTest { - - private short a; - private int b; - private long c; - private char d; - private boolean e; - - @Test - public void testDefaultValue() { - log.info("{} {} {} {} {}", a, b, c, d, e); - - assertThat(a, equalTo((short) 0)); - assertThat(b, equalTo(0)); - assertThat(c, equalTo(0L)); - assertThat(d, equalTo('\0')); - assertThat(e, equalTo(false)); - } -} diff --git a/class/src/test/java/syntax/integer/IntegerCacheTest.java b/class/src/test/java/syntax/integer/IntegerCacheTest.java index 672fbeca..f524a3ae 100644 --- a/class/src/test/java/syntax/integer/IntegerCacheTest.java +++ b/class/src/test/java/syntax/integer/IntegerCacheTest.java @@ -1,48 +1,65 @@ package syntax.integer; -import java.util.Objects; -import java.util.concurrent.ThreadLocalRandom; import org.junit.Test; +import java.util.Objects; + /** * @author kuangcp on 4/2/19-12:50 PM */ public class IntegerCacheTest { - @Test - public void testCache() { - // 缓存仅在 valueOf 方法中生效, 自动拆装箱又是调用的该方法, 所以缓存也存在于自动拆装箱里 - assert Integer.valueOf(1) == Integer.valueOf(1); - assert Integer.valueOf(128) != Integer.valueOf(128); - - assert new Integer(1) != Integer.valueOf(1); - assert new Integer(1) != new Integer(1); - } - - @Test - public void testInappropriateWithBox() { - Integer sum = 0; - for (int i = 0; i < 100; i++) { - sum += i; - // 编译后等价于 sum = Integer.valueOf((int)(sum.intValue() + i)); + @Test + public void testCache() { + // 缓存仅在 valueOf 方法中生效, 自动拆装箱又是调用的该方法, 所以缓存也存在于自动拆装箱里 + assert Integer.valueOf(1) == Integer.valueOf(1); + assert Integer.valueOf(128) != Integer.valueOf(128); + + assert new Integer(1) != Integer.valueOf(1); + assert new Integer(1) != new Integer(1); + } + + @Test + public void testInappropriateWithBox() { + Integer sum = 0; + for (int i = 0; i < 100; i++) { + sum += i; + // 编译后等价于 sum = Integer.valueOf((int)(sum.intValue() + i)); + } + System.out.println(sum); + } + + // 自动拆装箱 NPE问题 + @Test(expected = NullPointerException.class) + public void testBoxWithNPE() { + Integer num = null; + if (num == 1) { + System.out.println(); + } } - System.out.println(sum); - } - - // 自动拆装箱 NPE问题 - @Test(expected = NullPointerException.class) - public void testBoxWithNPE() { - Integer num = null; - if (num == 1) { - System.out.println(); + + //java中的基本类型的包装类、其中 Byte、Boolean、Short、Character、Integer、Long 实现了常量池技术 + // (除了Boolean,都只对小于128的值才支持) + @Test + public void testInteger() { +// 对于-128~127的Integer对象才会到IntegerCache里获取缓存,使用常量池技术 + Integer i1 = 100; + Integer i2 = 100; + // 上面两行代码,使用自动装箱特性,编译成 + // Integer i1 = Integer.valueOf(100); + // Integer i2 = Integer.valueOf(100); + System.out.println(i1 == i2); + + Integer i3 = 128; + Integer i4 = 128; + System.out.println(i3 == i4); } - } - @Test - public void testAvoidBoxWithNPE() { - Integer num = null; - if (Objects.equals(num, 1)) { - System.out.println("true"); + @Test + public void testAvoidBoxWithNPE() { + Integer num = null; + if (Objects.equals(num, 1)) { + System.out.println("true"); + } } - } } From 07ecc206aeda00ab1787c2b5a1fd6ef8c3108926 Mon Sep 17 00:00:00 2001 From: kuangcp Date: Wed, 13 Mar 2024 18:53:59 +0800 Subject: [PATCH 336/476] bitmap --- class/pom.xml | 5 ++ .../kuangcp/generic/ArrayUtilsTest.java | 38 ++++++------- .../github/kuangcp/inherit/SameFieldTest.java | 54 +++++++++---------- .../java/syntax/bit/RoaringBitmapTest.java | 21 ++++++++ pom.xml | 5 ++ 5 files changed, 77 insertions(+), 46 deletions(-) create mode 100644 class/src/test/java/syntax/bit/RoaringBitmapTest.java diff --git a/class/pom.xml b/class/pom.xml index 4bde3645..04e42253 100644 --- a/class/pom.xml +++ b/class/pom.xml @@ -30,6 +30,11 @@ javassist + + org.roaringbitmap + RoaringBitmap + + com.github.kuangcp kcp-tool diff --git a/class/src/test/java/com/github/kuangcp/generic/ArrayUtilsTest.java b/class/src/test/java/com/github/kuangcp/generic/ArrayUtilsTest.java index 25a954f9..bef570da 100644 --- a/class/src/test/java/com/github/kuangcp/generic/ArrayUtilsTest.java +++ b/class/src/test/java/com/github/kuangcp/generic/ArrayUtilsTest.java @@ -13,29 +13,29 @@ @Slf4j public class ArrayUtilsTest { - @Test - public void testCreateArray() { - String[] stringArray = ArrayUtils.create(String.class, 9); + @Test + public void testCreateArray() { + String[] stringArray = ArrayUtils.create(String.class, 9); - System.out.println(Arrays.toString(stringArray)); + System.out.println(Arrays.toString(stringArray)); - assert Arrays.stream(stringArray).allMatch(Objects::isNull); - } + assert Arrays.stream(stringArray).allMatch(Objects::isNull); + } - @Test - public void testSort() { - List list = Arrays.asList(3, 2, 5); - List result = ArrayUtils.sort(list); + @Test + public void testSort() { + List list = Arrays.asList(3, 2, 5); + List result = ArrayUtils.sort(list); - log.info("result={}", result); - } + log.info("result={}", result); + } - @Test - public void testSortToArray() { - List list = Arrays.asList(3, 2, 5); - // ArrayUtils.sortToArray(list) 实际返回的是 (Comparable[]) 但是这里需要强转为 Integer[] 这个转型失败了 - Integer[] result = ArrayUtils.sortToArray(list); + @Test + public void testSortToArray() { + List list = Arrays.asList(3, 2, 5); + // ArrayUtils.sortToArray(list) 实际返回的是 (Comparable[]) 但是这里需要强转为 Integer[] 这个转型失败了 + Integer[] result = ArrayUtils.sortToArray(list); - log.info("result={}", Arrays.toString(result)); - } + log.info("result={}", Arrays.toString(result)); + } } diff --git a/class/src/test/java/com/github/kuangcp/inherit/SameFieldTest.java b/class/src/test/java/com/github/kuangcp/inherit/SameFieldTest.java index 487082dc..f5484e28 100644 --- a/class/src/test/java/com/github/kuangcp/inherit/SameFieldTest.java +++ b/class/src/test/java/com/github/kuangcp/inherit/SameFieldTest.java @@ -1,12 +1,12 @@ package com.github.kuangcp.inherit; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.core.IsEqual.equalTo; - import lombok.extern.slf4j.Slf4j; import org.hamcrest.core.IsNot; import org.junit.Test; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.IsEqual.equalTo; + /** * RTTI C++ 中的概念, Java中没有, 但是 Thinking in java 里面用了这个概念, 实际上是有歧义的 * 多态是隐式地利用RTTI,反射则是显式地使用RTTI @@ -18,40 +18,40 @@ */ public class SameFieldTest { - @Slf4j - public static class A { + @Slf4j + public static class A { - int size = 1; + int size = 1; - public int getSize() { - log.info("invoke"); - return size; + public int getSize() { + log.info("invoke"); + return size; + } } - } - @Slf4j - public static class B extends A { + @Slf4j + public static class B extends A { - int size = 3; + int size = 3; - public int getSize() { - log.info("invoke"); - return size; + public int getSize() { + log.info("invoke"); + return size; + } } - } - @Test - public void testSame() { - A a = new B(); - B b = new B(); + @Test + public void testSame() { + A a = new B(); + B b = new B(); - System.out.println(a.getClass()); - System.out.println(b.getClass()); + System.out.println(a.getClass()); + System.out.println(b.getClass()); - assertThat(a.size + b.size, equalTo(4)); + assertThat(a.size + b.size, equalTo(4)); - assertThat(a.size, IsNot.not(a.getSize())); + assertThat(a.size, IsNot.not(a.getSize())); - assertThat(a.getSize() + b.getSize(), equalTo(6)); - } + assertThat(a.getSize() + b.getSize(), equalTo(6)); + } } diff --git a/class/src/test/java/syntax/bit/RoaringBitmapTest.java b/class/src/test/java/syntax/bit/RoaringBitmapTest.java new file mode 100644 index 00000000..d7b8dfef --- /dev/null +++ b/class/src/test/java/syntax/bit/RoaringBitmapTest.java @@ -0,0 +1,21 @@ +package syntax.bit; + +import org.junit.Test; +import org.roaringbitmap.longlong.Roaring64NavigableMap; + +/** + * https://www.cnblogs.com/yougewe/p/16390651.html + * @author Kuangcp + * 2024-03-13 18:49 + */ +public class RoaringBitmapTest { + + @Test + public void testRoaringBitmap() { + Roaring64NavigableMap bitmapObj = new Roaring64NavigableMap(); + bitmapObj.add(5L); + boolean exists = bitmapObj.contains(5); + long eleSize = bitmapObj.getLongCardinality(); + System.out.println("exits:" + exists + ", eleSize:" + eleSize); + } +} diff --git a/pom.xml b/pom.xml index 3ade9de7..30bd671d 100644 --- a/pom.xml +++ b/pom.xml @@ -408,6 +408,11 @@ jmockdata 4.1.2 + + org.roaringbitmap + RoaringBitmap + 1.0.5 + io.github.resilience4j From b915d745b053aded944bd3c91ab6f6bc19b62bae Mon Sep 17 00:00:00 2001 From: kuangcp Date: Mon, 25 Mar 2024 16:28:39 +0800 Subject: [PATCH 337/476] benchmark netty ws --- .../java/thread/ThreadStatusTransferTest.java | 17 ++++----- .../netty/websocket/ChannelSupervise.java | 6 +++- .../netty/websocket/NioWebSocketHandler.java | 4 +-- .../src/main/java/netty/websocket/Server.java | 8 +++++ .../src/main/java/netty/websocket/client.html | 2 +- .../main/java/reactor/websocket/Client.java | 4 +-- netty/src/main/resources/logback.xml | 36 +++++++++++++++++++ 7 files changed, 63 insertions(+), 14 deletions(-) create mode 100644 netty/src/main/resources/logback.xml diff --git a/concurrency/src/test/java/thread/ThreadStatusTransferTest.java b/concurrency/src/test/java/thread/ThreadStatusTransferTest.java index a7eb4f10..0e434ef7 100644 --- a/concurrency/src/test/java/thread/ThreadStatusTransferTest.java +++ b/concurrency/src/test/java/thread/ThreadStatusTransferTest.java @@ -1,6 +1,7 @@ package thread; import java.util.concurrent.TimeUnit; + import org.junit.Test; import thread.ThreadStatusTransfer.Notify; import thread.ThreadStatusTransfer.Wait; @@ -10,14 +11,14 @@ */ public class ThreadStatusTransferTest { - @Test - public void testMain() throws InterruptedException { - Thread waitThread = new Thread(new Wait(), "WaitThread"); - waitThread.start(); + @Test + public void testMain() throws InterruptedException { + Thread waitThread = new Thread(new Wait(), "WaitThread"); + waitThread.start(); - TimeUnit.SECONDS.sleep(1); + TimeUnit.SECONDS.sleep(100); - Thread notifyThread = new Thread(new Notify(), "NotifyThread"); - notifyThread.start(); - } + Thread notifyThread = new Thread(new Notify(), "NotifyThread"); + notifyThread.start(); + } } diff --git a/netty/src/main/java/netty/websocket/ChannelSupervise.java b/netty/src/main/java/netty/websocket/ChannelSupervise.java index 40cd0332..068d36f4 100644 --- a/netty/src/main/java/netty/websocket/ChannelSupervise.java +++ b/netty/src/main/java/netty/websocket/ChannelSupervise.java @@ -42,6 +42,10 @@ public static void send2All(TextWebSocketFrame tws) { } private static void printState() { - log.info("channelSize={} global={}", CHANNEL_MAP.size(), GLOBAL_GROUP.size()); + log.debug("channelSize={} global={}", CHANNEL_MAP.size(), GLOBAL_GROUP.size()); + } + + public static void watchState() { + log.info("online {}", CHANNEL_MAP.size()); } } diff --git a/netty/src/main/java/netty/websocket/NioWebSocketHandler.java b/netty/src/main/java/netty/websocket/NioWebSocketHandler.java index 42ea83df..2dc00250 100644 --- a/netty/src/main/java/netty/websocket/NioWebSocketHandler.java +++ b/netty/src/main/java/netty/websocket/NioWebSocketHandler.java @@ -120,7 +120,7 @@ public static String getBasePath(String uri) { private void fullHttpRequestHandler(ChannelHandlerContext ctx, FullHttpRequest request) { String uri = request.uri(); Map params = getParams(uri); - log.info("客户端请求参数:{}", params); +// log.info("客户端请求参数:{}", params); String userIdStr = params.get("userId"); if (StringUtils.isNotBlank(userIdStr)) { @@ -151,7 +151,7 @@ protected void channelRead0(ChannelHandlerContext ctx, WebSocketFrame frame) { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { - log.info("客户端请求数据类型:{}", msg.getClass()); +// log.info("客户端请求数据类型:{}", msg.getClass()); if (msg instanceof FullHttpRequest) { fullHttpRequestHandler(ctx, (FullHttpRequest) msg); } diff --git a/netty/src/main/java/netty/websocket/Server.java b/netty/src/main/java/netty/websocket/Server.java index 33282437..6e36fdf6 100644 --- a/netty/src/main/java/netty/websocket/Server.java +++ b/netty/src/main/java/netty/websocket/Server.java @@ -6,6 +6,11 @@ import io.netty.channel.socket.nio.NioServerSocketChannel; import lombok.extern.slf4j.Slf4j; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + /** * @author https://github.com/kuangcp on 2021-05-18 08:32 */ @@ -13,6 +18,9 @@ public class Server { private void init() { + ScheduledExecutorService pool = Executors.newScheduledThreadPool(1); + pool.scheduleAtFixedRate(ChannelSupervise::watchState, 5, 3, TimeUnit.SECONDS); + log.info("正在启动WebSocket服务器"); NioEventLoopGroup boss = new NioEventLoopGroup(); NioEventLoopGroup work = new NioEventLoopGroup(); diff --git a/netty/src/main/java/netty/websocket/client.html b/netty/src/main/java/netty/websocket/client.html index 3d29fade..fbb938d4 100644 --- a/netty/src/main/java/netty/websocket/client.html +++ b/netty/src/main/java/netty/websocket/client.html @@ -10,7 +10,7 @@ + + + + + + + +
+ + + + + \ No newline at end of file diff --git a/gui/src/test/java/com/github/kuangcp/tank/v3/PlayStageMgrTest.java b/gui/src/test/java/com/github/kuangcp/tank/cmd/PlayStageMgrTest.java similarity index 100% rename from gui/src/test/java/com/github/kuangcp/tank/v3/PlayStageMgrTest.java rename to gui/src/test/java/com/github/kuangcp/tank/cmd/PlayStageMgrTest.java diff --git a/gui/src/test/java/com/github/kuangcp/tank/util/LoopEventExecutorTest.java b/gui/src/test/java/com/github/kuangcp/tank/util/LoopEventExecutorTest.java index 001e741c..d0f124c9 100644 --- a/gui/src/test/java/com/github/kuangcp/tank/util/LoopEventExecutorTest.java +++ b/gui/src/test/java/com/github/kuangcp/tank/util/LoopEventExecutorTest.java @@ -1,11 +1,10 @@ package com.github.kuangcp.tank.util; -import com.github.kuangcp.tank.constant.DirectType; import com.github.kuangcp.tank.domain.EnemyTank; import com.github.kuangcp.tank.domain.Hero; import com.github.kuangcp.tank.util.executor.AbstractLoopEvent; import com.github.kuangcp.tank.util.executor.LoopEventExecutor; -import com.github.kuangcp.tank.v3.PlayStageMgr; +import com.github.kuangcp.tank.mgr.PlayStageMgr; import lombok.extern.slf4j.Slf4j; import org.junit.Test; From b12ef830f4f9ded3ff8162b546c1f2a14daa37da Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Sat, 6 Jul 2024 23:59:31 +0800 Subject: [PATCH 401/476] doc --- .../kuangcp/bloom/filter/BloomFilter.java | 2 +- .../kuangcp/bloom/filter/HashFunctions.java | 2 +- .../github/kuangcp/random/MT19937Random.java | 2 +- .../kuangcp/strcuture/stackapp/GetMin.java | 2 +- .../github/kuangcp/time/wheel/LinkedList.java | 2 +- .../com/github/kuangcp/time/wheel/Node.java | 2 +- .../github/kuangcp/time/wheel/TaskNode.java | 2 +- .../github/kuangcp/time/wheel/TimeWheel.java | 2 +- .../kuangcp/bloom/filter/BloomFilterTest.java | 2 +- .../MinCostClimbingStairs.java | 2 +- .../kuangcp/random/MT19937RandomTest.java | 2 +- .../strcuture/stackapp/GetMinTest.java | 2 +- .../stackapp/StackReplaceRecurrenceTest.java | 2 +- .../kuangcp/time/wheel/TimeWheelTest.java | 2 +- .../instantiation/ComplexConstructor.java | 2 +- .../instantiation/StaticFieldInit.java | 2 +- .../com/github/kuangcp/read/CommentTest.java | 2 +- .../com/github/kuangcp/serialize/Address.java | 2 +- .../serialize/json/speed/SpeedBenchMark.java | 2 +- .../java/jvm/gc/cms/TriggerFullGCForCMS.java | 2 +- .../main/java/jvm/gc/g1/DesignRegionSize.java | 2 +- .../java/jvm/gc/g1/StringDeduplication.java | 2 +- class/src/main/java/jvm/oom/ThreadOOM.java | 2 +- .../exception/OmitExceptionStackTest.java | 2 +- .../generic/constraint/GenericArrayTest.java | 2 +- .../instantiation/ComplexConstructorTest.java | 2 +- .../instantiation/StaticFieldInitTest.java | 2 +- .../com/github/kuangcp/reference/Apple.java | 2 +- .../kuangcp/reference/WeakReferenceTest.java | 2 +- .../github/kuangcp/reflects/LombokTest.java | 2 +- .../kuangcp/serialize/json/WriteJsonTest.java | 2 +- .../com/github/kuangcp/set/TreeSetTest.java | 2 +- .../java/jvm/load/ShowClassLoaderTest.java | 2 +- .../java/jvm/oom/DirectMemoryOOMTest.java | 2 +- class/src/test/java/log/LogLevelTest.java | 2 +- .../security/aes/wx/WxMsgAESUtilTest.java | 2 +- .../src/test/java/syntax/InstanceOfTest.java | 2 +- .../java/syntax/string/StringFormatTest.java | 2 +- .../situation/timoutpool/CreateNewPool.java | 2 +- .../situation/timoutpool/TimeoutFuture.java | 2 +- .../java/situation/timoutpool/base/Param.java | 2 +- .../situation/timoutpool/base/Result.java | 2 +- .../timoutpool/base/TaskExecutor.java | 2 +- .../thread/waitnotify/CallBackHolder.java | 2 +- .../src/main/java/web/Application.java | 2 +- .../web/situation/TimeoutPoolController.java | 2 +- .../kuangcp/juc/CountDownLatchTest.java | 2 +- .../test/java/resilience4j/BreakerTest.java | 2 +- .../situation/timoutpool/TimeoutPoolTest.java | 2 +- .../thread/waitnotify/CallBackHolderTest.java | 2 +- .../github/kuangcp/hi/SimpleJobListener.java | 2 +- .../com/github/kuangcp/hi/SimpleSink.java | 2 +- .../com/github/kuangcp/hi/SimpleSource.java | 2 +- .../github/kuangcp/hi/SimpleStatistic.java | 2 +- .../test/java/guava/eventbus/BlockTest.java | 2 +- .../kuangcp/notepad/base/HandlerType.java | 2 +- .../kuangcp/notepad/handler/BaseHandler.java | 2 +- .../kuangcp/notepad/handler/FileHandler.java | 2 +- .../handler/NotepadActionListener.java | 2 +- .../kuangcp/tank/backup/v1/MainPanelV1.java | 44 ++---- .../kuangcp/tank/backup/v2/MainPanelV2.java | 12 +- .../kuangcp/tank/constant/ButtonCommand.java | 2 +- .../kuangcp/tank/constant/DirectType.java | 19 ++- .../kuangcp/tank/constant/SettingCommand.java | 2 +- .../github/kuangcp/tank/domain/EnemyTank.java | 20 +-- .../com/github/kuangcp/tank/domain/Hero.java | 72 +++++----- .../kuangcp/tank/domain/StageBorder.java | 11 +- .../com/github/kuangcp/tank/domain/Tank.java | 126 +++++------------- .../kuangcp/tank/domain/VisualItem.java | 2 +- .../tank/domain/robot/EnemyActionContext.java | 2 +- .../kuangcp/tank/domain/robot/RobotRate.java | 2 +- .../tank/domain/robot/RoundActionEnum.java | 2 +- .../github/kuangcp/tank/frame/MainFrame.java | 1 - .../com/github/kuangcp/tank/mgr/BombMgr.java | 2 +- .../github/kuangcp/tank/mgr/PlayStageMgr.java | 4 +- .../github/kuangcp/tank/mgr/RoundMapMgr.java | 2 +- .../kuangcp/tank/panel/TankGroundPanel.java | 18 ++- .../tank/resource/AbstractImgListMgr.java | 2 +- .../kuangcp/tank/resource/AvatarImgMgr.java | 2 +- .../kuangcp/tank/resource/ColorMgr.java | 2 +- .../kuangcp/tank/resource/DefeatImgMgr.java | 2 +- .../kuangcp/tank/resource/PropertiesMgr.java | 2 +- .../kuangcp/tank/resource/ResourceMgr.java | 2 +- .../kuangcp/tank/resource/VictoryImgMgr.java | 2 +- .../kuangcp/tank/thread/ExitFlagRunnable.java | 2 +- .../github/kuangcp/tank/util/ExecutePool.java | 2 +- .../kuangcp/tank/util/HeroKeyListener.java | 89 ------------- .../kuangcp/tank/util/HoldingKeyEventMgr.java | 88 +++++------- .../util/executor/AbstractDelayEvent.java | 2 +- .../tank/util/executor/AbstractLoopEvent.java | 2 +- .../util/executor/CommonEventExecutor.java | 2 +- .../tank/util/executor/DelayEvent.java | 2 +- .../tank/util/executor/DelayExecutor.java | 2 +- .../kuangcp/tank/util/executor/LoopEvent.java | 2 +- .../tank/util/executor/LoopEventExecutor.java | 2 +- .../tank/util/executor/MonitorExecutor.java | 2 +- .../virusbroadcast/DefaultThreadFactory.java | 2 +- .../virusbroadcast/constant/PersonState.java | 2 +- .../constant/PersonStateEnum.java | 2 +- .../kuangcp/tank/cmd/PlayStageMgrTest.java | 2 +- .../github/kuangcp/tank/util/ListConTest.java | 2 +- .../tank/util/LoopEventExecutorTest.java | 2 +- .../hdfs/hi/GeneralFileActionDemo.java | 2 +- .../hdfs/hi/GeneralFileActionDemoTest.java | 2 +- .../lambda/bug/MultipleExtendsTest.java | 2 +- .../kuangcp/stream/CollectorToMapTest.java | 2 +- .../kuangcp/stream/CreateStreamTest.java | 2 +- .../github/kuangcp/stream/FlatMapTest.java | 2 +- .../stream/collector/CollectorTest.java | 2 +- .../github/kuangcp/stream/map/MapTest.java | 2 +- .../kuangcp/time/DateTimeFormatterTest.java | 2 +- .../com/github/kuangcp/time/DurationTest.java | 2 +- .../github/kuangcp/time/LocalDateTest.java | 2 +- .../github/kuangcp/hi/ConsumerDemoTest.java | 2 +- .../github/kuangcp/hi/ProducerDemoTest.java | 2 +- .../kuangcp/sharding/manual/AuthUtil.java | 2 +- .../manual/ConsistentHashingAlgorithm.java | 2 +- .../manual/ShardingAlgorithmEnum.java | 2 +- .../manual/ShardingAlgorithmType.java | 2 +- .../sharding/manual/ShardingTable.java | 2 +- .../manual/ShardingTableInterceptor.java | 2 +- .../com/github/kuangcp/stream/Report.java | 2 +- .../github/kuangcp/stream/dao/ReportDao.java | 2 +- .../ConsistentHashingAlgorithmTest.java | 2 +- .../stream/CursorSessionSpringBootTest.java | 2 +- .../netty/websocket/ChannelSupervise.java | 2 +- .../NioWebSocketChannelInitializer.java | 2 +- .../netty/websocket/NioWebSocketHandler.java | 2 +- .../src/main/java/netty/websocket/Server.java | 2 +- .../java/netty/timeServer/TimeClientTest.java | 2 +- .../kuangcp/bio/multicast/Consumer.java | 2 +- .../bio/multicast/MulticastConstant.java | 2 +- .../kuangcp/bio/multicast/Producer.java | 2 +- .../kuangcp/runable/SlowRequestTest.java | 2 +- .../singleton/StaticLazyWithSyncBlock.java | 2 +- .../com/github/kuangcp/math/Martingale.java | 2 +- .../asm/cglib/SimpleTransformer.java | 2 +- .../github/kuangcp/math/MartingaleTest.java | 2 +- .../asm/cglib/SimpleTransformerTest.java | 2 +- .../commoncode/CommonRelationTest.java | 2 +- .../proxy/dao/common/InterceptorLogic.java | 2 +- .../kuangcp/proxy/dao/common/Permission.java | 2 +- .../dao/jdkproxy/PermissionInterceptor.java | 2 +- .../com/github/kuangcp/util/BeanCopyTest.java | 2 +- .../kuangcp/validation/ErrorMsgConstant.java | 2 +- 145 files changed, 287 insertions(+), 483 deletions(-) delete mode 100644 gui/src/main/java/com/github/kuangcp/tank/util/HeroKeyListener.java diff --git a/algorithms/src/main/java/com/github/kuangcp/bloom/filter/BloomFilter.java b/algorithms/src/main/java/com/github/kuangcp/bloom/filter/BloomFilter.java index 10407445..06690cc0 100644 --- a/algorithms/src/main/java/com/github/kuangcp/bloom/filter/BloomFilter.java +++ b/algorithms/src/main/java/com/github/kuangcp/bloom/filter/BloomFilter.java @@ -9,7 +9,7 @@ /** * https://blog.csdn.net/hguisu/article/details/7866173 * - * @author https://github.com/kuangcp on 2019-08-04 15:57 + * @author Kuangcp on 2019-08-04 15:57 */ @Slf4j public class BloomFilter { diff --git a/algorithms/src/main/java/com/github/kuangcp/bloom/filter/HashFunctions.java b/algorithms/src/main/java/com/github/kuangcp/bloom/filter/HashFunctions.java index 3191cbcb..c9770abe 100644 --- a/algorithms/src/main/java/com/github/kuangcp/bloom/filter/HashFunctions.java +++ b/algorithms/src/main/java/com/github/kuangcp/bloom/filter/HashFunctions.java @@ -7,7 +7,7 @@ /** * https://stackoverflow.com/questions/34595/what-is-a-good-hash-function * - * @author https://github.com/kuangcp on 2019-08-04 16:02 + * @author Kuangcp on 2019-08-04 16:02 */ public class HashFunctions { diff --git a/algorithms/src/main/java/com/github/kuangcp/random/MT19937Random.java b/algorithms/src/main/java/com/github/kuangcp/random/MT19937Random.java index 8e6666de..aa9ec517 100644 --- a/algorithms/src/main/java/com/github/kuangcp/random/MT19937Random.java +++ b/algorithms/src/main/java/com/github/kuangcp/random/MT19937Random.java @@ -1,7 +1,7 @@ package com.github.kuangcp.random; /** - * @author https://github.com/kuangcp on 2020-03-19 18:09 + * @author Kuangcp on 2020-03-19 18:09 */ public class MT19937Random { diff --git a/algorithms/src/main/java/com/github/kuangcp/strcuture/stackapp/GetMin.java b/algorithms/src/main/java/com/github/kuangcp/strcuture/stackapp/GetMin.java index a19b737a..626e463b 100644 --- a/algorithms/src/main/java/com/github/kuangcp/strcuture/stackapp/GetMin.java +++ b/algorithms/src/main/java/com/github/kuangcp/strcuture/stackapp/GetMin.java @@ -5,7 +5,7 @@ import lombok.Data; /** - * @author https://github.com/kuangcp on 2019-12-29 16:45 + * @author Kuangcp on 2019-12-29 16:45 */ @Data public class GetMin { diff --git a/algorithms/src/main/java/com/github/kuangcp/time/wheel/LinkedList.java b/algorithms/src/main/java/com/github/kuangcp/time/wheel/LinkedList.java index 0dadb502..25947d3f 100644 --- a/algorithms/src/main/java/com/github/kuangcp/time/wheel/LinkedList.java +++ b/algorithms/src/main/java/com/github/kuangcp/time/wheel/LinkedList.java @@ -6,7 +6,7 @@ import java.util.stream.Collectors; /** - * @author https://github.com/kuangcp on 2019-10-28 13:10 + * @author Kuangcp on 2019-10-28 13:10 */ class LinkedList { diff --git a/algorithms/src/main/java/com/github/kuangcp/time/wheel/Node.java b/algorithms/src/main/java/com/github/kuangcp/time/wheel/Node.java index 72f2d5f9..5f8961f8 100644 --- a/algorithms/src/main/java/com/github/kuangcp/time/wheel/Node.java +++ b/algorithms/src/main/java/com/github/kuangcp/time/wheel/Node.java @@ -1,7 +1,7 @@ package com.github.kuangcp.time.wheel; /** - * @author https://github.com/kuangcp on 2019-10-28 23:53 + * @author Kuangcp on 2019-10-28 23:53 */ public interface Node { diff --git a/algorithms/src/main/java/com/github/kuangcp/time/wheel/TaskNode.java b/algorithms/src/main/java/com/github/kuangcp/time/wheel/TaskNode.java index 48366b3b..b226f676 100644 --- a/algorithms/src/main/java/com/github/kuangcp/time/wheel/TaskNode.java +++ b/algorithms/src/main/java/com/github/kuangcp/time/wheel/TaskNode.java @@ -4,7 +4,7 @@ import lombok.Data; /** - * @author https://github.com/kuangcp on 2019-10-28 13:10 + * @author Kuangcp on 2019-10-28 13:10 */ @Data @AllArgsConstructor diff --git a/algorithms/src/main/java/com/github/kuangcp/time/wheel/TimeWheel.java b/algorithms/src/main/java/com/github/kuangcp/time/wheel/TimeWheel.java index d1648b95..1546499f 100644 --- a/algorithms/src/main/java/com/github/kuangcp/time/wheel/TimeWheel.java +++ b/algorithms/src/main/java/com/github/kuangcp/time/wheel/TimeWheel.java @@ -14,7 +14,7 @@ *

* 类似于 HashMap的设计结构 设计支持 30 天内延迟任务 * - * @author https://github.com/kuangcp on 2019-10-11 20:53 + * @author Kuangcp on 2019-10-11 20:53 */ @Slf4j public class TimeWheel { diff --git a/algorithms/src/test/java/com/github/kuangcp/bloom/filter/BloomFilterTest.java b/algorithms/src/test/java/com/github/kuangcp/bloom/filter/BloomFilterTest.java index 5133f46f..b04c2748 100644 --- a/algorithms/src/test/java/com/github/kuangcp/bloom/filter/BloomFilterTest.java +++ b/algorithms/src/test/java/com/github/kuangcp/bloom/filter/BloomFilterTest.java @@ -5,7 +5,7 @@ import org.junit.Test; /** - * @author https://github.com/kuangcp on 2019-08-04 16:34 + * @author Kuangcp on 2019-08-04 16:34 */ @Slf4j public class BloomFilterTest { diff --git a/algorithms/src/test/java/com/github/kuangcp/dynamicprogramming/MinCostClimbingStairs.java b/algorithms/src/test/java/com/github/kuangcp/dynamicprogramming/MinCostClimbingStairs.java index 64b7ff39..c02ecf01 100644 --- a/algorithms/src/test/java/com/github/kuangcp/dynamicprogramming/MinCostClimbingStairs.java +++ b/algorithms/src/test/java/com/github/kuangcp/dynamicprogramming/MinCostClimbingStairs.java @@ -6,7 +6,7 @@ import org.junit.Test; /** - * @author https://github.com/kuangcp on 2020-09-03 12:39 + * @author Kuangcp on 2020-09-03 12:39 * * https://leetcode-cn.com/problems/min-cost-climbing-stairs/description/ */ diff --git a/algorithms/src/test/java/com/github/kuangcp/random/MT19937RandomTest.java b/algorithms/src/test/java/com/github/kuangcp/random/MT19937RandomTest.java index c1503616..dfa2d2ad 100644 --- a/algorithms/src/test/java/com/github/kuangcp/random/MT19937RandomTest.java +++ b/algorithms/src/test/java/com/github/kuangcp/random/MT19937RandomTest.java @@ -8,7 +8,7 @@ import org.junit.Test; /** - * @author https://github.com/kuangcp on 2020-03-20 09:58 + * @author Kuangcp on 2020-03-20 09:58 */ public class MT19937RandomTest { diff --git a/algorithms/src/test/java/com/github/kuangcp/strcuture/stackapp/GetMinTest.java b/algorithms/src/test/java/com/github/kuangcp/strcuture/stackapp/GetMinTest.java index 846e85a6..ee7909e4 100644 --- a/algorithms/src/test/java/com/github/kuangcp/strcuture/stackapp/GetMinTest.java +++ b/algorithms/src/test/java/com/github/kuangcp/strcuture/stackapp/GetMinTest.java @@ -6,7 +6,7 @@ import org.junit.Test; /** - * @author https://github.com/kuangcp on 2019-12-29 23:45 + * @author Kuangcp on 2019-12-29 23:45 */ public class GetMinTest { diff --git a/algorithms/src/test/java/com/github/kuangcp/strcuture/stackapp/StackReplaceRecurrenceTest.java b/algorithms/src/test/java/com/github/kuangcp/strcuture/stackapp/StackReplaceRecurrenceTest.java index 2a8a81ae..4a092326 100644 --- a/algorithms/src/test/java/com/github/kuangcp/strcuture/stackapp/StackReplaceRecurrenceTest.java +++ b/algorithms/src/test/java/com/github/kuangcp/strcuture/stackapp/StackReplaceRecurrenceTest.java @@ -8,7 +8,7 @@ import static org.junit.Assert.assertThat; /** - * @author https://github.com/kuangcp on 2019-11-19 21:38 + * @author Kuangcp on 2019-11-19 21:38 */ diff --git a/algorithms/src/test/java/com/github/kuangcp/time/wheel/TimeWheelTest.java b/algorithms/src/test/java/com/github/kuangcp/time/wheel/TimeWheelTest.java index a3b97dbb..e760cc1e 100644 --- a/algorithms/src/test/java/com/github/kuangcp/time/wheel/TimeWheelTest.java +++ b/algorithms/src/test/java/com/github/kuangcp/time/wheel/TimeWheelTest.java @@ -16,7 +16,7 @@ import java.util.stream.IntStream; /** - * @author https://github.com/kuangcp on 2019-10-27 12:15 + * @author Kuangcp on 2019-10-27 12:15 */ @Ignore @Slf4j diff --git a/class/src/main/java/com/github/kuangcp/instantiation/ComplexConstructor.java b/class/src/main/java/com/github/kuangcp/instantiation/ComplexConstructor.java index 29edf30b..ec541513 100644 --- a/class/src/main/java/com/github/kuangcp/instantiation/ComplexConstructor.java +++ b/class/src/main/java/com/github/kuangcp/instantiation/ComplexConstructor.java @@ -3,7 +3,7 @@ import lombok.extern.slf4j.Slf4j; /** - * @author https://github.com/kuangcp + * @author Kuangcp */ @Slf4j class ComplexConstructor { diff --git a/class/src/main/java/com/github/kuangcp/instantiation/StaticFieldInit.java b/class/src/main/java/com/github/kuangcp/instantiation/StaticFieldInit.java index f57cbab9..8b760809 100644 --- a/class/src/main/java/com/github/kuangcp/instantiation/StaticFieldInit.java +++ b/class/src/main/java/com/github/kuangcp/instantiation/StaticFieldInit.java @@ -4,7 +4,7 @@ * 看起来 count 似乎是先使用再声明的 静态变量是类加载时期就分配到了数据区, 在内存中只有一个, 不会分配多次, 其后所有的操作都是值改变, 地址不会变 ?? 类初始化的时候, * 先去查找类中的所有静态声明, 然后分配空间, 这时候只有空间, 还没有赋值, 然后依据代码的顺序执行, 进行赋值 IDEA 里面避免这个问题是比较容易的, 会有明显的警告 * - * @author https://github.com/kuangcp + * @author Kuangcp * @date 2019-05-15 09:03 */ public class StaticFieldInit { diff --git a/class/src/main/java/com/github/kuangcp/read/CommentTest.java b/class/src/main/java/com/github/kuangcp/read/CommentTest.java index 7b20cfed..fa9d012f 100644 --- a/class/src/main/java/com/github/kuangcp/read/CommentTest.java +++ b/class/src/main/java/com/github/kuangcp/read/CommentTest.java @@ -1,7 +1,7 @@ package com.github.kuangcp.read; /** - * @author https://github.com/kuangcp on 2020-05-17 17:41 + * @author Kuangcp on 2020-05-17 17:41 */ public class CommentTest { diff --git a/class/src/main/java/com/github/kuangcp/serialize/Address.java b/class/src/main/java/com/github/kuangcp/serialize/Address.java index 4091e982..b9a83917 100644 --- a/class/src/main/java/com/github/kuangcp/serialize/Address.java +++ b/class/src/main/java/com/github/kuangcp/serialize/Address.java @@ -4,7 +4,7 @@ import lombok.Data; /** - * @author https://github.com/kuangcp on 2020-08-13 15:19 + * @author Kuangcp on 2020-08-13 15:19 */ @Data public class Address implements Serializable { diff --git a/class/src/main/java/com/github/kuangcp/serialize/json/speed/SpeedBenchMark.java b/class/src/main/java/com/github/kuangcp/serialize/json/speed/SpeedBenchMark.java index bea326e1..474c4b75 100644 --- a/class/src/main/java/com/github/kuangcp/serialize/json/speed/SpeedBenchMark.java +++ b/class/src/main/java/com/github/kuangcp/serialize/json/speed/SpeedBenchMark.java @@ -10,7 +10,7 @@ import org.openjdk.jmh.annotations.Warmup; /** - * @author https://github.com/kuangcp on 2020-05-06 00:33 + * @author Kuangcp on 2020-05-06 00:33 */ @BenchmarkMode(Mode.Throughput) @Warmup(iterations = 3) diff --git a/class/src/main/java/jvm/gc/cms/TriggerFullGCForCMS.java b/class/src/main/java/jvm/gc/cms/TriggerFullGCForCMS.java index c2de40c0..b75d548d 100644 --- a/class/src/main/java/jvm/gc/cms/TriggerFullGCForCMS.java +++ b/class/src/main/java/jvm/gc/cms/TriggerFullGCForCMS.java @@ -9,7 +9,7 @@ import java.util.concurrent.TimeUnit; /** - * @author https://github.com/kuangcp on 2021-05-15 18:53 + * @author Kuangcp on 2021-05-15 18:53 *

* 问题:内存溢出后会死循环创建 dump 直到填满硬盘,CMSScavengeBeforeRemark 并不能在 CMSGC 的时候触发 FullGC *

diff --git a/class/src/main/java/jvm/gc/g1/DesignRegionSize.java b/class/src/main/java/jvm/gc/g1/DesignRegionSize.java index 131a809c..1890e72f 100644 --- a/class/src/main/java/jvm/gc/g1/DesignRegionSize.java +++ b/class/src/main/java/jvm/gc/g1/DesignRegionSize.java @@ -1,7 +1,7 @@ package jvm.gc.g1; /** - * @author https://github.com/kuangcp on 2021-08-30 03:33 + * @author Kuangcp on 2021-08-30 03:33 */ public class DesignRegionSize { } diff --git a/class/src/main/java/jvm/gc/g1/StringDeduplication.java b/class/src/main/java/jvm/gc/g1/StringDeduplication.java index 6be094cb..a03581ab 100644 --- a/class/src/main/java/jvm/gc/g1/StringDeduplication.java +++ b/class/src/main/java/jvm/gc/g1/StringDeduplication.java @@ -3,7 +3,7 @@ import java.util.concurrent.TimeUnit; /** - * @author https://github.com/kuangcp on 2021-05-16 23:36 + * @author Kuangcp on 2021-05-16 23:36 *

* -Xms15m -Xmx15m -XX:+PrintGCDetails -XX:+UseG1GC -XX:+PrintStringTableStatistics -XX:+UseStringDeduplication -XX:+PrintStringDeduplicationStatistics */ diff --git a/class/src/main/java/jvm/oom/ThreadOOM.java b/class/src/main/java/jvm/oom/ThreadOOM.java index 9b571c9e..bfbf9e8f 100644 --- a/class/src/main/java/jvm/oom/ThreadOOM.java +++ b/class/src/main/java/jvm/oom/ThreadOOM.java @@ -5,7 +5,7 @@ *

* OutOfMemoryError: unable to create new native thread * - * @author https://github.com/kuangcp on 2019-12-25 20:05 + * @author Kuangcp on 2019-12-25 20:05 */ public class ThreadOOM { diff --git a/class/src/test/java/com/github/kuangcp/exception/OmitExceptionStackTest.java b/class/src/test/java/com/github/kuangcp/exception/OmitExceptionStackTest.java index f3996bc2..419dcec3 100644 --- a/class/src/test/java/com/github/kuangcp/exception/OmitExceptionStackTest.java +++ b/class/src/test/java/com/github/kuangcp/exception/OmitExceptionStackTest.java @@ -7,7 +7,7 @@ * https://www.oracle.com/technetwork/java/javase/relnotes-139183.html * https://blog.csdn.net/zheng0518/article/details/52450537 * - * @author https://github.com/kuangcp on 2019-12-03 09:41 + * @author Kuangcp on 2019-12-03 09:41 */ @Slf4j public class OmitExceptionStackTest { diff --git a/class/src/test/java/com/github/kuangcp/generic/constraint/GenericArrayTest.java b/class/src/test/java/com/github/kuangcp/generic/constraint/GenericArrayTest.java index 6b2ec989..75224a06 100644 --- a/class/src/test/java/com/github/kuangcp/generic/constraint/GenericArrayTest.java +++ b/class/src/test/java/com/github/kuangcp/generic/constraint/GenericArrayTest.java @@ -10,7 +10,7 @@ * 不能实例化 参数化类型的数组, 声明是可以的 正确的解决方式应该是 参数化集合 * 不能实例化 泛型数组 * - * @author https://github.com/kuangcp + * @author Kuangcp * 2019-05-25 21:02 */ public class GenericArrayTest { diff --git a/class/src/test/java/com/github/kuangcp/instantiation/ComplexConstructorTest.java b/class/src/test/java/com/github/kuangcp/instantiation/ComplexConstructorTest.java index a3701bb7..69f9b714 100644 --- a/class/src/test/java/com/github/kuangcp/instantiation/ComplexConstructorTest.java +++ b/class/src/test/java/com/github/kuangcp/instantiation/ComplexConstructorTest.java @@ -8,7 +8,7 @@ import org.junit.Test; /** - * @author https://github.com/kuangcp + * @author Kuangcp * @date 2019-05-15 09:21 */ @Slf4j diff --git a/class/src/test/java/com/github/kuangcp/instantiation/StaticFieldInitTest.java b/class/src/test/java/com/github/kuangcp/instantiation/StaticFieldInitTest.java index bbe9ce1c..e9450b00 100644 --- a/class/src/test/java/com/github/kuangcp/instantiation/StaticFieldInitTest.java +++ b/class/src/test/java/com/github/kuangcp/instantiation/StaticFieldInitTest.java @@ -8,7 +8,7 @@ import org.junit.Test; /** - * @author https://github.com/kuangcp + * @author Kuangcp * @date 2019-05-15 09:11 */ @Slf4j diff --git a/class/src/test/java/com/github/kuangcp/reference/Apple.java b/class/src/test/java/com/github/kuangcp/reference/Apple.java index 2c9c3c2b..7bacfcf1 100644 --- a/class/src/test/java/com/github/kuangcp/reference/Apple.java +++ b/class/src/test/java/com/github/kuangcp/reference/Apple.java @@ -5,7 +5,7 @@ import lombok.extern.slf4j.Slf4j; /** - * @author https://github.com/kuangcp on 2020-09-24 12:47 + * @author Kuangcp on 2020-09-24 12:47 */ @Data @Slf4j diff --git a/class/src/test/java/com/github/kuangcp/reference/WeakReferenceTest.java b/class/src/test/java/com/github/kuangcp/reference/WeakReferenceTest.java index 6e604b93..f6a85263 100644 --- a/class/src/test/java/com/github/kuangcp/reference/WeakReferenceTest.java +++ b/class/src/test/java/com/github/kuangcp/reference/WeakReferenceTest.java @@ -6,7 +6,7 @@ import org.junit.Test; /** - * @author https://github.com/kuangcp on 2020-09-24 12:35 + * @author Kuangcp on 2020-09-24 12:35 * @see java.lang.ThreadLocal.ThreadLocalMap.Entry */ @Slf4j diff --git a/class/src/test/java/com/github/kuangcp/reflects/LombokTest.java b/class/src/test/java/com/github/kuangcp/reflects/LombokTest.java index 0035349c..ce8f87e8 100644 --- a/class/src/test/java/com/github/kuangcp/reflects/LombokTest.java +++ b/class/src/test/java/com/github/kuangcp/reflects/LombokTest.java @@ -6,7 +6,7 @@ import org.junit.Test; /** - * @author https://github.com/kuangcp on 2019-12-15 22:42 + * @author Kuangcp on 2019-12-15 22:42 */ public class LombokTest { diff --git a/class/src/test/java/com/github/kuangcp/serialize/json/WriteJsonTest.java b/class/src/test/java/com/github/kuangcp/serialize/json/WriteJsonTest.java index 729c9a22..4cbf2695 100644 --- a/class/src/test/java/com/github/kuangcp/serialize/json/WriteJsonTest.java +++ b/class/src/test/java/com/github/kuangcp/serialize/json/WriteJsonTest.java @@ -22,7 +22,7 @@ import org.openjdk.jmh.runner.options.OptionsBuilder; /** - * @author https://github.com/kuangcp on 2020-05-06 01:12 + * @author Kuangcp on 2020-05-06 01:12 */ @BenchmarkMode(Mode.Throughput) @Warmup(iterations = 1) diff --git a/class/src/test/java/com/github/kuangcp/set/TreeSetTest.java b/class/src/test/java/com/github/kuangcp/set/TreeSetTest.java index fad86e1c..534cc05d 100644 --- a/class/src/test/java/com/github/kuangcp/set/TreeSetTest.java +++ b/class/src/test/java/com/github/kuangcp/set/TreeSetTest.java @@ -8,7 +8,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.IsEqual.equalTo; /** - * @author https://github.com/kuangcp + * @author Kuangcp * @date 2019-05-24 09:22 */ public class TreeSetTest { diff --git a/class/src/test/java/jvm/load/ShowClassLoaderTest.java b/class/src/test/java/jvm/load/ShowClassLoaderTest.java index e413259e..70186acd 100644 --- a/class/src/test/java/jvm/load/ShowClassLoaderTest.java +++ b/class/src/test/java/jvm/load/ShowClassLoaderTest.java @@ -6,7 +6,7 @@ import java.util.ArrayList; /** - * @author https://github.com/kuangcp on 2020-09-12 19:48 + * @author Kuangcp on 2020-09-12 19:48 */ @Slf4j public class ShowClassLoaderTest { diff --git a/class/src/test/java/jvm/oom/DirectMemoryOOMTest.java b/class/src/test/java/jvm/oom/DirectMemoryOOMTest.java index 37555265..e6d85512 100644 --- a/class/src/test/java/jvm/oom/DirectMemoryOOMTest.java +++ b/class/src/test/java/jvm/oom/DirectMemoryOOMTest.java @@ -3,7 +3,7 @@ import org.junit.Test; /** - * @author https://github.com/kuangcp on 2020-11-24 14:45 + * @author Kuangcp on 2020-11-24 14:45 */ public class DirectMemoryOOMTest { diff --git a/class/src/test/java/log/LogLevelTest.java b/class/src/test/java/log/LogLevelTest.java index 5733f890..79de36b7 100644 --- a/class/src/test/java/log/LogLevelTest.java +++ b/class/src/test/java/log/LogLevelTest.java @@ -4,7 +4,7 @@ import org.junit.Test; /** - * @author https://github.com/kuangcp on 2020-07-11 17:01 + * @author Kuangcp on 2020-07-11 17:01 */ @Slf4j public class LogLevelTest { diff --git a/class/src/test/java/security/aes/wx/WxMsgAESUtilTest.java b/class/src/test/java/security/aes/wx/WxMsgAESUtilTest.java index 6e99d638..60fc1b8f 100644 --- a/class/src/test/java/security/aes/wx/WxMsgAESUtilTest.java +++ b/class/src/test/java/security/aes/wx/WxMsgAESUtilTest.java @@ -8,7 +8,7 @@ import static org.junit.Assert.assertThat; /** - * @author https://github.com/kuangcp on 2020-11-01 21:35 + * @author Kuangcp on 2020-11-01 21:35 */ public class WxMsgAESUtilTest { diff --git a/class/src/test/java/syntax/InstanceOfTest.java b/class/src/test/java/syntax/InstanceOfTest.java index c72f24e5..35458394 100644 --- a/class/src/test/java/syntax/InstanceOfTest.java +++ b/class/src/test/java/syntax/InstanceOfTest.java @@ -7,7 +7,7 @@ import org.junit.Test; /** - * @author https://github.com/Kuangcp + * @author Kuangcp * @date 2019-05-10 08:18 */ public class InstanceOfTest { diff --git a/class/src/test/java/syntax/string/StringFormatTest.java b/class/src/test/java/syntax/string/StringFormatTest.java index 6e6d4d5b..b4eec48e 100644 --- a/class/src/test/java/syntax/string/StringFormatTest.java +++ b/class/src/test/java/syntax/string/StringFormatTest.java @@ -5,7 +5,7 @@ import org.junit.Test; /** - * @author https://github.com/kuangcp on 2019-12-17 11:53 + * @author Kuangcp on 2019-12-17 11:53 */ public class StringFormatTest { diff --git a/concurrency/src/main/java/situation/timoutpool/CreateNewPool.java b/concurrency/src/main/java/situation/timoutpool/CreateNewPool.java index eeea0895..8affb810 100644 --- a/concurrency/src/main/java/situation/timoutpool/CreateNewPool.java +++ b/concurrency/src/main/java/situation/timoutpool/CreateNewPool.java @@ -16,7 +16,7 @@ * 方案: 使用临时线程池 * 缺陷 遇到突发请求流量时线程大增,有拖垮服务器的风险 * - * @author https://github.com/kuangcp on 2021-09-04 23:23 + * @author Kuangcp on 2021-09-04 23:23 */ @Slf4j public class CreateNewPool implements TaskExecutor { diff --git a/concurrency/src/main/java/situation/timoutpool/TimeoutFuture.java b/concurrency/src/main/java/situation/timoutpool/TimeoutFuture.java index afc7f0e3..1cdfb338 100644 --- a/concurrency/src/main/java/situation/timoutpool/TimeoutFuture.java +++ b/concurrency/src/main/java/situation/timoutpool/TimeoutFuture.java @@ -12,7 +12,7 @@ import java.util.concurrent.TimeUnit; /** - * @author https://github.com/kuangcp on 2021-09-05 02:45 + * @author Kuangcp on 2021-09-05 02:45 */ @Slf4j public class TimeoutFuture implements TaskExecutor { diff --git a/concurrency/src/main/java/situation/timoutpool/base/Param.java b/concurrency/src/main/java/situation/timoutpool/base/Param.java index 8f2ae467..652bb2ff 100644 --- a/concurrency/src/main/java/situation/timoutpool/base/Param.java +++ b/concurrency/src/main/java/situation/timoutpool/base/Param.java @@ -4,7 +4,7 @@ import lombok.Data; /** - * @author https://github.com/kuangcp on 2021-09-04 23:27 + * @author Kuangcp on 2021-09-04 23:27 */ @Data @Builder diff --git a/concurrency/src/main/java/situation/timoutpool/base/Result.java b/concurrency/src/main/java/situation/timoutpool/base/Result.java index 01ef087f..af4f94a1 100644 --- a/concurrency/src/main/java/situation/timoutpool/base/Result.java +++ b/concurrency/src/main/java/situation/timoutpool/base/Result.java @@ -6,7 +6,7 @@ import java.util.List; /** - * @author https://github.com/kuangcp on 2021-09-04 23:28 + * @author Kuangcp on 2021-09-04 23:28 */ @Data @Builder diff --git a/concurrency/src/main/java/situation/timoutpool/base/TaskExecutor.java b/concurrency/src/main/java/situation/timoutpool/base/TaskExecutor.java index 328c6bb3..2794f58a 100644 --- a/concurrency/src/main/java/situation/timoutpool/base/TaskExecutor.java +++ b/concurrency/src/main/java/situation/timoutpool/base/TaskExecutor.java @@ -4,7 +4,7 @@ import java.util.concurrent.TimeUnit; /** - * @author https://github.com/kuangcp on 2021-09-04 23:23 + * @author Kuangcp on 2021-09-04 23:23 */ public interface TaskExecutor { diff --git a/concurrency/src/main/java/thread/waitnotify/CallBackHolder.java b/concurrency/src/main/java/thread/waitnotify/CallBackHolder.java index 2215d06a..f70ee066 100644 --- a/concurrency/src/main/java/thread/waitnotify/CallBackHolder.java +++ b/concurrency/src/main/java/thread/waitnotify/CallBackHolder.java @@ -8,7 +8,7 @@ /** * wait and notify * - * @author https://github.com/kuangcp on 2020-05-09 11:01 + * @author Kuangcp on 2020-05-09 11:01 */ @Slf4j public class CallBackHolder { diff --git a/concurrency/src/main/java/web/Application.java b/concurrency/src/main/java/web/Application.java index b02929fd..d0ee4c52 100644 --- a/concurrency/src/main/java/web/Application.java +++ b/concurrency/src/main/java/web/Application.java @@ -4,7 +4,7 @@ import com.hellokaton.blade.Blade; /** - * @author https://github.com/kuangcp on 2021-09-05 00:18 + * @author Kuangcp on 2021-09-05 00:18 */ public class Application { diff --git a/concurrency/src/main/java/web/situation/TimeoutPoolController.java b/concurrency/src/main/java/web/situation/TimeoutPoolController.java index ffde728b..0e4e34ed 100644 --- a/concurrency/src/main/java/web/situation/TimeoutPoolController.java +++ b/concurrency/src/main/java/web/situation/TimeoutPoolController.java @@ -14,7 +14,7 @@ import java.util.concurrent.TimeUnit; /** - * @author https://github.com/kuangcp on 2021-09-05 00:56 + * @author Kuangcp on 2021-09-05 00:56 */ @Path @Slf4j diff --git a/concurrency/src/test/java/com/github/kuangcp/juc/CountDownLatchTest.java b/concurrency/src/test/java/com/github/kuangcp/juc/CountDownLatchTest.java index 30bd7661..950ec01b 100644 --- a/concurrency/src/test/java/com/github/kuangcp/juc/CountDownLatchTest.java +++ b/concurrency/src/test/java/com/github/kuangcp/juc/CountDownLatchTest.java @@ -6,7 +6,7 @@ import java.util.concurrent.*; /** - * @author https://github.com/kuangcp on 2021-09-04 23:02 + * @author Kuangcp on 2021-09-04 23:02 */ @Slf4j public class CountDownLatchTest { diff --git a/concurrency/src/test/java/resilience4j/BreakerTest.java b/concurrency/src/test/java/resilience4j/BreakerTest.java index ae86ab45..0c2239da 100644 --- a/concurrency/src/test/java/resilience4j/BreakerTest.java +++ b/concurrency/src/test/java/resilience4j/BreakerTest.java @@ -11,7 +11,7 @@ /** - * @author https://github.com/kuangcp on 2022-08-03 19:20 + * @author Kuangcp on 2022-08-03 19:20 */ public class BreakerTest { diff --git a/concurrency/src/test/java/situation/timoutpool/TimeoutPoolTest.java b/concurrency/src/test/java/situation/timoutpool/TimeoutPoolTest.java index f406b404..8ecf8c21 100644 --- a/concurrency/src/test/java/situation/timoutpool/TimeoutPoolTest.java +++ b/concurrency/src/test/java/situation/timoutpool/TimeoutPoolTest.java @@ -12,7 +12,7 @@ /** - * @author https://github.com/kuangcp on 2021-09-04 23:42 + * @author Kuangcp on 2021-09-04 23:42 */ @Slf4j public class TimeoutPoolTest { diff --git a/concurrency/src/test/java/thread/waitnotify/CallBackHolderTest.java b/concurrency/src/test/java/thread/waitnotify/CallBackHolderTest.java index fa86b4cc..892a4d49 100644 --- a/concurrency/src/test/java/thread/waitnotify/CallBackHolderTest.java +++ b/concurrency/src/test/java/thread/waitnotify/CallBackHolderTest.java @@ -13,7 +13,7 @@ import java.util.stream.IntStream; /** - * @author https://github.com/kuangcp on 2020-05-09 11:01 + * @author Kuangcp on 2020-05-09 11:01 */ @Slf4j public class CallBackHolderTest { diff --git a/flink/src/main/java/com/github/kuangcp/hi/SimpleJobListener.java b/flink/src/main/java/com/github/kuangcp/hi/SimpleJobListener.java index 2e3ad108..bd9a14f1 100644 --- a/flink/src/main/java/com/github/kuangcp/hi/SimpleJobListener.java +++ b/flink/src/main/java/com/github/kuangcp/hi/SimpleJobListener.java @@ -10,7 +10,7 @@ /** * 如果该对象能注入到 下面类的属性上, 问题就简单一些了 * - * @author https://github.com/kuangcp on 2019-07-08 11:22 + * @author Kuangcp on 2019-07-08 11:22 * @see ExecutionGraph jobStatusListeners */ @Deprecated diff --git a/flink/src/main/java/com/github/kuangcp/hi/SimpleSink.java b/flink/src/main/java/com/github/kuangcp/hi/SimpleSink.java index 58f3970a..d9fec7ae 100644 --- a/flink/src/main/java/com/github/kuangcp/hi/SimpleSink.java +++ b/flink/src/main/java/com/github/kuangcp/hi/SimpleSink.java @@ -12,7 +12,7 @@ /** * 模拟输出到文件 * - * @author https://github.com/kuangcp + * @author Kuangcp * @since 2019-05-30 20:36 */ @lombok.Data diff --git a/flink/src/main/java/com/github/kuangcp/hi/SimpleSource.java b/flink/src/main/java/com/github/kuangcp/hi/SimpleSource.java index 91a86288..6b990a5f 100644 --- a/flink/src/main/java/com/github/kuangcp/hi/SimpleSource.java +++ b/flink/src/main/java/com/github/kuangcp/hi/SimpleSource.java @@ -10,7 +10,7 @@ /** * 模拟数据库分页查询 * - * @author https://github.com/kuangcp 2019-06-16 15:12 + * @author Kuangcp 2019-06-16 15:12 */ @Slf4j class SimpleSource { diff --git a/flink/src/main/java/com/github/kuangcp/hi/SimpleStatistic.java b/flink/src/main/java/com/github/kuangcp/hi/SimpleStatistic.java index 29afd40f..3f4a64dc 100644 --- a/flink/src/main/java/com/github/kuangcp/hi/SimpleStatistic.java +++ b/flink/src/main/java/com/github/kuangcp/hi/SimpleStatistic.java @@ -16,7 +16,7 @@ /** * Batch 的 调用是通过 REST API的, 只能在发起请求的地方依据返回值来判断是否执行完成和成功 * - * @author https://github.com/kuangcp + * @author Kuangcp * @since 2019-05-16 16:26 */ @Slf4j diff --git a/guava/src/test/java/guava/eventbus/BlockTest.java b/guava/src/test/java/guava/eventbus/BlockTest.java index 10ed2d3a..473b0874 100644 --- a/guava/src/test/java/guava/eventbus/BlockTest.java +++ b/guava/src/test/java/guava/eventbus/BlockTest.java @@ -11,7 +11,7 @@ import java.util.concurrent.Executors; /** - * @author https://github.com/kuangcp on 2021-12-07 07:29 + * @author Kuangcp on 2021-12-07 07:29 */ @Slf4j public class BlockTest { diff --git a/gui/src/main/java/com/github/kuangcp/notepad/base/HandlerType.java b/gui/src/main/java/com/github/kuangcp/notepad/base/HandlerType.java index 1dca998c..ef9ac2f9 100644 --- a/gui/src/main/java/com/github/kuangcp/notepad/base/HandlerType.java +++ b/gui/src/main/java/com/github/kuangcp/notepad/base/HandlerType.java @@ -1,7 +1,7 @@ package com.github.kuangcp.notepad.base; /** - * @author https://github.com/kuangcp on 2019-07-28 13:11 + * @author Kuangcp on 2019-07-28 13:11 */ public interface HandlerType { diff --git a/gui/src/main/java/com/github/kuangcp/notepad/handler/BaseHandler.java b/gui/src/main/java/com/github/kuangcp/notepad/handler/BaseHandler.java index 71be22c9..07288ee2 100644 --- a/gui/src/main/java/com/github/kuangcp/notepad/handler/BaseHandler.java +++ b/gui/src/main/java/com/github/kuangcp/notepad/handler/BaseHandler.java @@ -3,7 +3,7 @@ import java.awt.event.ActionEvent; /** - * @author https://github.com/kuangcp on 2019-07-28 13:16 + * @author Kuangcp on 2019-07-28 13:16 */ abstract class BaseHandler { diff --git a/gui/src/main/java/com/github/kuangcp/notepad/handler/FileHandler.java b/gui/src/main/java/com/github/kuangcp/notepad/handler/FileHandler.java index 8f20704e..2be1767e 100644 --- a/gui/src/main/java/com/github/kuangcp/notepad/handler/FileHandler.java +++ b/gui/src/main/java/com/github/kuangcp/notepad/handler/FileHandler.java @@ -16,7 +16,7 @@ import lombok.extern.slf4j.Slf4j; /** - * @author https://github.com/kuangcp on 2019-07-28 13:10 + * @author Kuangcp on 2019-07-28 13:10 */ @Slf4j class FileHandler extends BaseHandler { diff --git a/gui/src/main/java/com/github/kuangcp/notepad/handler/NotepadActionListener.java b/gui/src/main/java/com/github/kuangcp/notepad/handler/NotepadActionListener.java index a3c223af..327803b7 100644 --- a/gui/src/main/java/com/github/kuangcp/notepad/handler/NotepadActionListener.java +++ b/gui/src/main/java/com/github/kuangcp/notepad/handler/NotepadActionListener.java @@ -10,7 +10,7 @@ import lombok.extern.slf4j.Slf4j; /** - * @author https://github.com/kuangcp on 2019-07-28 13:16 + * @author Kuangcp on 2019-07-28 13:16 */ @Slf4j public class NotepadActionListener implements ActionListener { diff --git a/gui/src/main/java/com/github/kuangcp/tank/backup/v1/MainPanelV1.java b/gui/src/main/java/com/github/kuangcp/tank/backup/v1/MainPanelV1.java index 9b0bd014..6690de3d 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/backup/v1/MainPanelV1.java +++ b/gui/src/main/java/com/github/kuangcp/tank/backup/v1/MainPanelV1.java @@ -1,5 +1,6 @@ package com.github.kuangcp.tank.backup.v1; +import com.github.kuangcp.tank.constant.DirectType; import com.github.kuangcp.tank.domain.EnemyTank; import com.github.kuangcp.tank.domain.Hero; import com.github.kuangcp.tank.domain.Tank; @@ -13,7 +14,7 @@ import java.util.Vector; /** - * @author https://github.com/kuangcp on 2021-09-11 23:15 + * @author Kuangcp on 2021-09-11 23:15 */ @Slf4j @SuppressWarnings("serial") @@ -46,11 +47,6 @@ public void drawSelf(Graphics g) { g.setColor(Color.WHITE); super.drawSelf(g); } - - @Override - public void run() { - - } }.drawSelf(g); new Tank(80, 20, 0) { @Override @@ -58,11 +54,6 @@ public void drawSelf(Graphics g) { g.setColor(new Color(93, 217, 41)); super.drawSelf(g); } - - @Override - public void run() { - - } }.drawSelf(g); new Tank(110, 20, 0) { @Override @@ -70,11 +61,6 @@ public void drawSelf(Graphics g) { g.setColor(new Color(34, 155, 234)); super.drawSelf(g); } - - @Override - public void run() { - - } }.drawSelf(g); new Tank(140, 20, 0) { @Override @@ -82,11 +68,6 @@ public void drawSelf(Graphics g) { g.setColor(new Color(155, 62, 202)); super.drawSelf(g); } - - @Override - public void run() { - - } }.drawSelf(g); new Tank(170, 20, 0) { @Override @@ -94,11 +75,6 @@ public void drawSelf(Graphics g) { g.setColor(new Color(240, 57, 23)); super.drawSelf(g); } - - @Override - public void run() { - - } }.drawSelf(g); //画出子弹 @@ -126,27 +102,27 @@ public void run() { public void keyPressed(KeyEvent e) { //加了if条件后 实现了墙的限制(如果是游戏中的道具,该怎么办) if (e.getKeyCode() == KeyEvent.VK_A) { - hero.setDirect(2); + hero.setDirect(DirectType.LEFT); if ((hero.getX() - 10) > 0) - hero.moveLeft(); + hero.move(); } if (e.getKeyCode() == KeyEvent.VK_D) { - hero.setDirect(3); + hero.setDirect(DirectType.RIGHT); if ((hero.getX() + 15) < 405) - hero.moveRight(); + hero.move(); } if (e.getKeyCode() == KeyEvent.VK_W) { - hero.setDirect(0); + hero.setDirect(DirectType.UP); if ((hero.getY() - 13) > 0) - hero.moveUp(); + hero.move(); } if (e.getKeyCode() == KeyEvent.VK_S) { - hero.setDirect(1); + hero.setDirect(DirectType.DOWN); if ((hero.getY() - 15) < 275) - hero.moveDown(); + hero.move(); } //必须重新绘制窗口,不然上面的方法不能视觉上动起来 diff --git a/gui/src/main/java/com/github/kuangcp/tank/backup/v2/MainPanelV2.java b/gui/src/main/java/com/github/kuangcp/tank/backup/v2/MainPanelV2.java index 8b68bd9d..4b717d36 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/backup/v2/MainPanelV2.java +++ b/gui/src/main/java/com/github/kuangcp/tank/backup/v2/MainPanelV2.java @@ -21,7 +21,7 @@ import java.util.concurrent.TimeUnit; /** - * @author https://github.com/kuangcp on 2021-09-11 23:15 + * @author Kuangcp on 2021-09-11 23:15 */ @Slf4j @SuppressWarnings("serial") @@ -103,26 +103,24 @@ public void keyPressed(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_A) { hero.setDirect(2); if (PlayStageMgr.instance.willInBorder(hero)) - hero.moveLeft(); + hero.move(); } if (e.getKeyCode() == KeyEvent.VK_D) { hero.setDirect(3); if (PlayStageMgr.instance.willInBorder(hero)) - hero.moveRight(); + hero.move(); } if (e.getKeyCode() == KeyEvent.VK_W) { hero.setDirect(0); if (PlayStageMgr.instance.willInBorder(hero)) - hero.moveUp(); - + hero.move(); } if (e.getKeyCode() == KeyEvent.VK_S) { hero.setDirect(1); if (PlayStageMgr.instance.willInBorder(hero)) - hero.moveDown(); - + hero.move(); } //必须重新绘制窗口,不然上面的方法不能视觉上动起来 this.repaint(); diff --git a/gui/src/main/java/com/github/kuangcp/tank/constant/ButtonCommand.java b/gui/src/main/java/com/github/kuangcp/tank/constant/ButtonCommand.java index 16b2956a..3b527340 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/constant/ButtonCommand.java +++ b/gui/src/main/java/com/github/kuangcp/tank/constant/ButtonCommand.java @@ -1,7 +1,7 @@ package com.github.kuangcp.tank.constant; /** - * @author https://github.com/kuangcp on 2021-09-11 17:45 + * @author Kuangcp on 2021-09-11 17:45 */ public interface ButtonCommand { diff --git a/gui/src/main/java/com/github/kuangcp/tank/constant/DirectType.java b/gui/src/main/java/com/github/kuangcp/tank/constant/DirectType.java index c487e44a..031e5b66 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/constant/DirectType.java +++ b/gui/src/main/java/com/github/kuangcp/tank/constant/DirectType.java @@ -3,22 +3,26 @@ import java.util.concurrent.ThreadLocalRandom; /** - * @author https://github.com/kuangcp on 2021-09-06 03:08 + * @author Kuangcp on 2021-09-06 03:08 */ public interface DirectType { + int NONE = -1; + int UP = 0; int DOWN = 1; int LEFT = 2; int RIGHT = 3; - int MAX = RIGHT; + int MAX = 3; int[] UP_SELECT = new int[]{UP, LEFT, RIGHT}; int[] DOWN_SELECT = new int[]{DOWN, LEFT, RIGHT}; int[] LEFT_SELECT = new int[]{UP, DOWN, LEFT}; int[] RIGHT_SELECT = new int[]{UP, DOWN, RIGHT}; + int[] loop = new int[]{UP, RIGHT, DOWN, LEFT}; + static int[] turnSelection(int direct) { switch (direct) { case UP: @@ -34,16 +38,19 @@ static int[] turnSelection(int direct) { } } - static boolean isUp(int direct){ + static boolean isUp(int direct) { return direct == UP; } - static boolean isDown(int direct){ + + static boolean isDown(int direct) { return direct == DOWN; } - static boolean isLeft(int direct){ + + static boolean isLeft(int direct) { return direct == LEFT; } - static boolean isRight(int direct){ + + static boolean isRight(int direct) { return direct == RIGHT; } diff --git a/gui/src/main/java/com/github/kuangcp/tank/constant/SettingCommand.java b/gui/src/main/java/com/github/kuangcp/tank/constant/SettingCommand.java index 2d69b158..3dd0e4e0 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/constant/SettingCommand.java +++ b/gui/src/main/java/com/github/kuangcp/tank/constant/SettingCommand.java @@ -1,7 +1,7 @@ package com.github.kuangcp.tank.constant; /** - * @author https://github.com/kuangcp on 2021-09-06 01:01 + * @author Kuangcp on 2021-09-06 01:01 */ public interface SettingCommand { diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java b/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java index 1d8f3b8d..437cfa96 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java @@ -230,23 +230,7 @@ private void finalMoveAction() { return; } - switch (this.direct) { - case DirectType.UP: - y -= this.speed; - actionContext.addCount(); - break; - case DirectType.DOWN: - y += this.speed; - actionContext.addCount(); - break; - case DirectType.LEFT: - x -= this.speed; - actionContext.addCount(); - break; - case DirectType.RIGHT: - x += this.speed; - actionContext.addCount(); - break; - } + actionContext.addCount(); + this.move(); } } diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/Hero.java b/gui/src/main/java/com/github/kuangcp/tank/domain/Hero.java index faf6a02b..d2e7f7bf 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/domain/Hero.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/Hero.java @@ -3,14 +3,20 @@ import com.github.kuangcp.tank.constant.DirectType; import com.github.kuangcp.tank.mgr.PlayStageMgr; import com.github.kuangcp.tank.resource.ColorMgr; +import com.github.kuangcp.tank.util.HoldingKeyEventMgr; import com.github.kuangcp.tank.util.Roll; +import com.github.kuangcp.tank.util.TankTool; import com.github.kuangcp.tank.util.executor.LoopEventExecutor; +import lombok.Getter; +import lombok.Setter; import lombok.extern.slf4j.Slf4j; import java.awt.*; import java.util.Vector; @Slf4j +@Getter +@Setter public class Hero extends Tank { //子弹集合 @@ -34,7 +40,32 @@ public Hero(int x, int y, int speed) { @Override public void run() { + final HoldingKeyEventMgr keyEvent = HoldingKeyEventMgr.instance; + + while (this.isAlive()) { + if (keyEvent.hasPressMoveEvent()) { +// log.info("eventGroup={}", eventGroup); + + final int lastDirect = this.getDirect(); + final int direct = keyEvent.getDirect(); + + final boolean ablePass = PlayStageMgr.ablePassByHinder(this); + if ((ablePass || lastDirect != direct)) { + this.setDirect(direct); + if (PlayStageMgr.instance.willInBorder(this) + && PlayStageMgr.instance.ableToMove(this)) { + this.move(); + } + } + } + + if (keyEvent.isShot()) { + this.shotEnemy(); + } + // 动作的延迟 1000 / 77 fps + TankTool.yieldMsTime(33); + } } /** @@ -111,51 +142,10 @@ public void shotEnemy() { lastShotMs = nowMs; } - public void moveUp() { - this.y -= this.speed; - } - - public void moveDown() { - this.y += this.speed; - } - - public void moveLeft() { - this.x -= this.speed; - } - - public void moveRight() { - this.x += this.speed; - } - - - public int getLife() { - return life; - } - - public void setLife(int life) { - this.life = life; - } - - public int getSpeed() { - return speed; - } - - public void setSpeed(int speed) { - this.speed = speed; - } - public void addSpeed(int delta) { this.speed += delta; } - public int getPrize() { - return prize; - } - - public void setPrize(int prize) { - this.prize = prize; - } - public void addPrize(int delta) { this.prize += delta; } diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/StageBorder.java b/gui/src/main/java/com/github/kuangcp/tank/domain/StageBorder.java index 183f243d..01ad3b1f 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/domain/StageBorder.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/StageBorder.java @@ -3,7 +3,7 @@ import lombok.Getter; /** - * @author https://github.com/kuangcp on 2021-09-21 23:35 + * @author Kuangcp on 2021-09-21 23:35 */ @Getter public class StageBorder { @@ -18,4 +18,13 @@ public StageBorder(int minX, int maxX, int minY, int maxY) { this.minY = minY; this.maxY = maxY; } + + public int getTotalX() { + return minX + maxX; + } + + public int getTotalY() { + return minY + maxY; + } + } diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/Tank.java b/gui/src/main/java/com/github/kuangcp/tank/domain/Tank.java index 81ed4607..14691eb0 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/domain/Tank.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/Tank.java @@ -2,6 +2,8 @@ import com.github.kuangcp.tank.constant.DirectType; import com.github.kuangcp.tank.util.executor.AbstractLoopEvent; +import lombok.Getter; +import lombok.Setter; import lombok.extern.slf4j.Slf4j; import java.awt.*; @@ -10,11 +12,13 @@ * 最起初的坦克类 * 往后有继承 */ +@Setter +@Getter @Slf4j public abstract class Tank extends AbstractLoopEvent implements VisualItem { int x; // 坦克中心的横坐标 int y; // 坦克中心的纵坐标 - int direct = 0; // 初始方向 + int direct = DirectType.NONE; // 初始方向 int type = 0; // 坦克的种类 int speed; // 前进的步长 boolean alive = true;// 是否存活 @@ -24,95 +28,36 @@ public abstract class Tank extends AbstractLoopEvent implements VisualItem { int halfWidth = 10; int halfHeight = 15; - public int getLife() { - return life; - } - - public void setLife(int life) { - this.life = life; - } - public void addLife(int delta) { this.life += delta; } - public boolean isAbort() { - return abort; - } - - public void setAbort(boolean abort) { - this.abort = abort; - } - - public boolean isAlive() { - return alive; - } - - public void setAlive(boolean isLive) { - this.alive = isLive; - } - - public int getSpeed() { - return speed; - } - - public void setSpeed(int s) { - speed = s; - } - - public int getDirect() { - return direct; - } - - public void setDirect(int direct) { - this.direct = direct; - } - - public int getType() { - return type; - } - - public void setType(int type) { - this.type = type; - } - - public int getX() { - return x; - } - - public int getHalfWidth() { - return halfWidth; - } - - public void setHalfWidth(int halfWidth) { - this.halfWidth = halfWidth; - } - - public int getHalfHeight() { - return halfHeight; - } - - public void setHalfHeight(int halfHeight) { - this.halfHeight = halfHeight; - } - - public void setX(int x) { + public Tank(int x, int y, int speed) { this.x = x; + this.y = y; + this.speed = speed; } - public int getY() { - return y; - } + @Override + public void run() { - public void setY(int y) { - this.y = y; } - - public Tank(int x, int y, int speed) { - this.x = x; - this.y = y; - this.speed = speed; + public void move() { + switch (this.direct) { + case DirectType.UP: + y -= this.speed; + break; + case DirectType.DOWN: + y += this.speed; + break; + case DirectType.LEFT: + x -= this.speed; + break; + case DirectType.RIGHT: + x += this.speed; + break; + } } /** @@ -121,15 +66,9 @@ public Tank(int x, int y, int speed) { public void drawSelf(Graphics g) { //系统画图函数的参照点 (全是取的左上角) int topX, topY; - switch (direct) { case DirectType.UP: { - topX = this.x - this.halfWidth; - topY = this.y - this.halfHeight; - - this.drawVerBorder(g, topX, topY); - //5.画出炮管 - g.fill3DRect(topX + this.halfWidth - 1, topY - 1, 2, this.halfWidth + 1, false); + drawUp(g); break; } case DirectType.DOWN: { @@ -137,7 +76,6 @@ public void drawSelf(Graphics g) { topY = this.y - this.halfHeight; this.drawVerBorder(g, topX, topY); - //5.画出炮管 g.fill3DRect(topX + this.halfWidth - 1, topY + this.halfWidth * 2 + 1, 2, this.halfWidth + 1, false); break; } @@ -147,7 +85,6 @@ public void drawSelf(Graphics g) { this.drawHorBorder(g, topX, topY); - //5.画出炮管 g.fill3DRect(topX - 1, topY + this.halfWidth - 1, this.halfWidth + 1, 2, false); break; } @@ -157,13 +94,22 @@ public void drawSelf(Graphics g) { this.drawHorBorder(g, topX, topY); - //5.画出炮管 g.fill3DRect(topX + this.halfHeight + 2, topY + this.halfWidth - 1, this.halfWidth + 1, 2, false); break; } + default: + drawUp(g); } } + private void drawUp(Graphics g) { + int topX = this.x - this.halfWidth; + int topY = this.y - this.halfHeight; + + this.drawVerBorder(g, topX, topY); + g.fill3DRect(topX + this.halfWidth - 1, topY - 1, 2, this.halfWidth + 1, false); + } + /** * 水平方向 */ diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/VisualItem.java b/gui/src/main/java/com/github/kuangcp/tank/domain/VisualItem.java index 076ccb12..7dba1b18 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/domain/VisualItem.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/VisualItem.java @@ -3,7 +3,7 @@ import java.awt.*; /** - * @author https://github.com/kuangcp on 2021-09-23 00:57 + * @author Kuangcp on 2021-09-23 00:57 */ public interface VisualItem { diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/robot/EnemyActionContext.java b/gui/src/main/java/com/github/kuangcp/tank/domain/robot/EnemyActionContext.java index 65257095..bbd4f962 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/domain/robot/EnemyActionContext.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/robot/EnemyActionContext.java @@ -3,7 +3,7 @@ import java.util.concurrent.ThreadLocalRandom; /** - * @author https://github.com/kuangcp on 2021-09-22 23:56 + * @author Kuangcp on 2021-09-22 23:56 */ public class EnemyActionContext { diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/robot/RobotRate.java b/gui/src/main/java/com/github/kuangcp/tank/domain/robot/RobotRate.java index e5672aef..d4cd99b7 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/domain/robot/RobotRate.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/robot/RobotRate.java @@ -1,7 +1,7 @@ package com.github.kuangcp.tank.domain.robot; /** - * @author https://github.com/kuangcp on 2021-09-23 00:45 + * @author Kuangcp on 2021-09-23 00:45 */ public interface RobotRate { diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/robot/RoundActionEnum.java b/gui/src/main/java/com/github/kuangcp/tank/domain/robot/RoundActionEnum.java index db1c9f9d..ab739329 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/domain/robot/RoundActionEnum.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/robot/RoundActionEnum.java @@ -1,7 +1,7 @@ package com.github.kuangcp.tank.domain.robot; /** - * @author https://github.com/kuangcp on 2021-09-23 00:14 + * @author Kuangcp on 2021-09-23 00:14 */ public enum RoundActionEnum { MOVE, SHOT, STAY diff --git a/gui/src/main/java/com/github/kuangcp/tank/frame/MainFrame.java b/gui/src/main/java/com/github/kuangcp/tank/frame/MainFrame.java index 43cbf088..f00fe367 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/frame/MainFrame.java +++ b/gui/src/main/java/com/github/kuangcp/tank/frame/MainFrame.java @@ -29,7 +29,6 @@ * 11 有游戏的音效(操纵声音文件) */ @Slf4j -@SuppressWarnings("serial") public class MainFrame extends JFrame implements Runnable { public volatile TankGroundPanel groundPanel;//坦克的主画板 diff --git a/gui/src/main/java/com/github/kuangcp/tank/mgr/BombMgr.java b/gui/src/main/java/com/github/kuangcp/tank/mgr/BombMgr.java index d60dc778..8f2ed611 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/mgr/BombMgr.java +++ b/gui/src/main/java/com/github/kuangcp/tank/mgr/BombMgr.java @@ -18,7 +18,7 @@ import java.util.Objects; /** - * @author https://github.com/kuangcp on 2021-09-11 16:28 + * @author Kuangcp on 2021-09-11 16:28 */ @Slf4j public class BombMgr extends AbstractImgListMgr { diff --git a/gui/src/main/java/com/github/kuangcp/tank/mgr/PlayStageMgr.java b/gui/src/main/java/com/github/kuangcp/tank/mgr/PlayStageMgr.java index 6b764641..f5a1bf6f 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/mgr/PlayStageMgr.java +++ b/gui/src/main/java/com/github/kuangcp/tank/mgr/PlayStageMgr.java @@ -18,7 +18,7 @@ import java.util.Objects; /** - * @author https://github.com/kuangcp on 2021-09-11 23:24 + * @author Kuangcp on 2021-09-11 23:24 */ @Slf4j public class PlayStageMgr { @@ -40,7 +40,7 @@ public class PlayStageMgr { /** * 敌人的数量 */ - static int enemySize = 10; + public static int enemySize = 10; /** * 无敌状态 时间 */ diff --git a/gui/src/main/java/com/github/kuangcp/tank/mgr/RoundMapMgr.java b/gui/src/main/java/com/github/kuangcp/tank/mgr/RoundMapMgr.java index e9fa784c..4c4dc8f4 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/mgr/RoundMapMgr.java +++ b/gui/src/main/java/com/github/kuangcp/tank/mgr/RoundMapMgr.java @@ -7,7 +7,7 @@ import java.util.List; /** - * @author https://github.com/kuangcp on 2021-09-25 15:57 + * @author Kuangcp on 2021-09-25 15:57 */ public class RoundMapMgr { diff --git a/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java b/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java index c03aa1ef..ba2c272b 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java +++ b/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java @@ -5,22 +5,21 @@ import com.github.kuangcp.tank.domain.EnemyTank; import com.github.kuangcp.tank.domain.Hero; import com.github.kuangcp.tank.domain.Iron; +import com.github.kuangcp.tank.domain.StageBorder; +import com.github.kuangcp.tank.frame.MainFrame; +import com.github.kuangcp.tank.frame.SettingFrame; import com.github.kuangcp.tank.mgr.BombMgr; +import com.github.kuangcp.tank.mgr.PlayStageMgr; +import com.github.kuangcp.tank.mgr.RoundMapMgr; import com.github.kuangcp.tank.resource.AvatarImgMgr; import com.github.kuangcp.tank.resource.ColorMgr; import com.github.kuangcp.tank.util.ExecutePool; import com.github.kuangcp.tank.util.HoldingKeyEventMgr; -import com.github.kuangcp.tank.util.HeroKeyListener; import com.github.kuangcp.tank.util.TankTool; import com.github.kuangcp.tank.util.executor.AbstractDelayEvent; import com.github.kuangcp.tank.util.executor.DelayExecutor; import com.github.kuangcp.tank.util.executor.LoopEventExecutor; import com.github.kuangcp.tank.util.executor.MonitorExecutor; -import com.github.kuangcp.tank.frame.MainFrame; -import com.github.kuangcp.tank.mgr.PlayStageMgr; -import com.github.kuangcp.tank.mgr.RoundMapMgr; -import com.github.kuangcp.tank.frame.SettingFrame; -import com.github.kuangcp.tank.domain.StageBorder; import lombok.extern.slf4j.Slf4j; import javax.swing.*; @@ -31,12 +30,10 @@ import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.TimeUnit; -@SuppressWarnings("serial") @Slf4j public class TankGroundPanel extends JPanel implements java.awt.event.KeyListener, Runnable { public volatile Hero hero; - public HeroKeyListener heroKeyListener; public static boolean newStage = true; private volatile boolean invokeNewStage = false; @@ -70,8 +67,9 @@ public void startNewRound() { PlayStageMgr.init(hero, enemyList, bricks, irons); //多键监听实现 - heroKeyListener = new HeroKeyListener(HoldingKeyEventMgr.instance, hero, this); - ExecutePool.exclusiveLoopPool.execute(heroKeyListener); +// heroKeyListener = new HeroKeyListener(HoldingKeyEventMgr.instance, hero, this); +// ExecutePool.exclusiveLoopPool.execute(heroKeyListener); + ExecutePool.exclusiveLoopPool.execute(hero); // 创建 敌人的坦克 EnemyTank ett = null; diff --git a/gui/src/main/java/com/github/kuangcp/tank/resource/AbstractImgListMgr.java b/gui/src/main/java/com/github/kuangcp/tank/resource/AbstractImgListMgr.java index 97e443a4..499331d8 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/resource/AbstractImgListMgr.java +++ b/gui/src/main/java/com/github/kuangcp/tank/resource/AbstractImgListMgr.java @@ -7,7 +7,7 @@ import java.io.IOException; /** - * @author https://github.com/kuangcp on 2021-09-13 01:17 + * @author Kuangcp on 2021-09-13 01:17 */ @Slf4j public abstract class AbstractImgListMgr { diff --git a/gui/src/main/java/com/github/kuangcp/tank/resource/AvatarImgMgr.java b/gui/src/main/java/com/github/kuangcp/tank/resource/AvatarImgMgr.java index 43955040..3cd58dd8 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/resource/AvatarImgMgr.java +++ b/gui/src/main/java/com/github/kuangcp/tank/resource/AvatarImgMgr.java @@ -3,7 +3,7 @@ import lombok.extern.slf4j.Slf4j; /** - * @author https://github.com/kuangcp on 2021-09-11 16:41 + * @author Kuangcp on 2021-09-11 16:41 */ @Slf4j public class AvatarImgMgr extends AbstractImgListMgr { diff --git a/gui/src/main/java/com/github/kuangcp/tank/resource/ColorMgr.java b/gui/src/main/java/com/github/kuangcp/tank/resource/ColorMgr.java index 2e509910..c0827220 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/resource/ColorMgr.java +++ b/gui/src/main/java/com/github/kuangcp/tank/resource/ColorMgr.java @@ -3,7 +3,7 @@ import java.awt.*; /** - * @author https://github.com/kuangcp on 2021-10-17 23:06 + * @author Kuangcp on 2021-10-17 23:06 */ public class ColorMgr { public static final ColorMgr instance = new ColorMgr(); diff --git a/gui/src/main/java/com/github/kuangcp/tank/resource/DefeatImgMgr.java b/gui/src/main/java/com/github/kuangcp/tank/resource/DefeatImgMgr.java index 24c9e5ed..344e0585 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/resource/DefeatImgMgr.java +++ b/gui/src/main/java/com/github/kuangcp/tank/resource/DefeatImgMgr.java @@ -1,7 +1,7 @@ package com.github.kuangcp.tank.resource; /** - * @author https://github.com/kuangcp on 2021-09-13 01:32 + * @author Kuangcp on 2021-09-13 01:32 */ public class DefeatImgMgr extends AbstractImgListMgr { diff --git a/gui/src/main/java/com/github/kuangcp/tank/resource/PropertiesMgr.java b/gui/src/main/java/com/github/kuangcp/tank/resource/PropertiesMgr.java index 2c6661b0..328455e5 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/resource/PropertiesMgr.java +++ b/gui/src/main/java/com/github/kuangcp/tank/resource/PropertiesMgr.java @@ -6,7 +6,7 @@ import java.util.Properties; /** - * @author https://github.com/kuangcp on 2021-09-21 10:05 + * @author Kuangcp on 2021-09-21 10:05 */ @Slf4j public class PropertiesMgr { diff --git a/gui/src/main/java/com/github/kuangcp/tank/resource/ResourceMgr.java b/gui/src/main/java/com/github/kuangcp/tank/resource/ResourceMgr.java index 9458ced0..3f49393f 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/resource/ResourceMgr.java +++ b/gui/src/main/java/com/github/kuangcp/tank/resource/ResourceMgr.java @@ -7,7 +7,7 @@ import java.io.IOException; /** - * @author https://github.com/kuangcp on 2021-09-13 01:35 + * @author Kuangcp on 2021-09-13 01:35 */ @Slf4j public class ResourceMgr { diff --git a/gui/src/main/java/com/github/kuangcp/tank/resource/VictoryImgMgr.java b/gui/src/main/java/com/github/kuangcp/tank/resource/VictoryImgMgr.java index 2591143c..c734a1cf 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/resource/VictoryImgMgr.java +++ b/gui/src/main/java/com/github/kuangcp/tank/resource/VictoryImgMgr.java @@ -3,7 +3,7 @@ import lombok.extern.slf4j.Slf4j; /** - * @author https://github.com/kuangcp on 2021-09-13 01:13 + * @author Kuangcp on 2021-09-13 01:13 */ @Slf4j public class VictoryImgMgr extends AbstractImgListMgr { diff --git a/gui/src/main/java/com/github/kuangcp/tank/thread/ExitFlagRunnable.java b/gui/src/main/java/com/github/kuangcp/tank/thread/ExitFlagRunnable.java index eb27f65d..219cf667 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/thread/ExitFlagRunnable.java +++ b/gui/src/main/java/com/github/kuangcp/tank/thread/ExitFlagRunnable.java @@ -1,7 +1,7 @@ package com.github.kuangcp.tank.thread; /** - * @author https://github.com/kuangcp on 2021-09-11 18:33 + * @author Kuangcp on 2021-09-11 18:33 */ public interface ExitFlagRunnable extends Runnable { diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/ExecutePool.java b/gui/src/main/java/com/github/kuangcp/tank/util/ExecutePool.java index 7e69e413..41b0105f 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/util/ExecutePool.java +++ b/gui/src/main/java/com/github/kuangcp/tank/util/ExecutePool.java @@ -7,7 +7,7 @@ import java.util.concurrent.atomic.AtomicLong; /** - * @author https://github.com/kuangcp on 2021-09-11 17:32 + * @author Kuangcp on 2021-09-11 17:32 */ public class ExecutePool { diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/HeroKeyListener.java b/gui/src/main/java/com/github/kuangcp/tank/util/HeroKeyListener.java deleted file mode 100644 index aa6d587a..00000000 --- a/gui/src/main/java/com/github/kuangcp/tank/util/HeroKeyListener.java +++ /dev/null @@ -1,89 +0,0 @@ -package com.github.kuangcp.tank.util; - - -import com.github.kuangcp.tank.constant.DirectType; -import com.github.kuangcp.tank.domain.Hero; -import com.github.kuangcp.tank.panel.TankGroundPanel; -import com.github.kuangcp.tank.thread.ExitFlagRunnable; -import com.github.kuangcp.tank.mgr.PlayStageMgr; -import lombok.extern.slf4j.Slf4j; - -/** - * 思路如下: - * 在按下键的pressed函数中 激活键 - * 在离开键Release函数中把离开的键从 取消激活 - * 另开一个线程来一直遍历 字段,达到同时监控两个键的的动作的效果 - *

- * 实现结果: 一边跑,一边放子弹完全不是事儿,方向的切换也十分流畅 - */ -@Slf4j -public class HeroKeyListener implements ExitFlagRunnable { - - Hero hero; - TankGroundPanel tankGroundPanel; - HoldingKeyEventMgr eventGroup; - private volatile boolean exit = false; - - public HeroKeyListener(HoldingKeyEventMgr eventGroup, Hero hero, TankGroundPanel tankGroundPanel) { - this.eventGroup = eventGroup; - this.hero = hero; - this.tankGroundPanel = tankGroundPanel; - } - - public void exit() { - this.exit = true; - } - - @Override - public void run() { - while (hero.isAlive() && !exit) { - if (eventGroup.hasPressMoveEvent()) { -// log.info("eventGroup={}", eventGroup); - } - - final int lastDirect = hero.getDirect(); - - if (eventGroup.isLeft()) { - final boolean ablePass = PlayStageMgr.ablePassByHinder(hero); - if ((ablePass || !DirectType.isLeft(lastDirect))) { - hero.setDirect(DirectType.LEFT); - if (PlayStageMgr.instance.willInBorder(hero) && PlayStageMgr.instance.ableToMove(hero)) { - hero.moveLeft(); - } - } - } else if (eventGroup.isRight()) { - final boolean ablePass = PlayStageMgr.ablePassByHinder(hero); - if (ablePass || !DirectType.isRight(lastDirect)) { - - hero.setDirect(DirectType.RIGHT); - if (PlayStageMgr.instance.willInBorder(hero) && PlayStageMgr.instance.ableToMove(hero)) { - hero.moveRight(); - } - } - } else if (eventGroup.isDown()) { - final boolean ablePass = PlayStageMgr.ablePassByHinder(hero); - if (ablePass || !DirectType.isDown(lastDirect)) { - hero.setDirect(DirectType.DOWN); - if (PlayStageMgr.instance.willInBorder(hero) && PlayStageMgr.instance.ableToMove(hero)) { - hero.moveDown(); - } - } - } else if (eventGroup.isUp()) { - final boolean ablePass = PlayStageMgr.ablePassByHinder(hero); - if (ablePass || !DirectType.isUp(lastDirect)) { - hero.setDirect(DirectType.UP); - if (PlayStageMgr.instance.willInBorder(hero) && PlayStageMgr.instance.ableToMove(hero)) { - hero.moveUp(); - } - } - } - - if (eventGroup.isShot()) { - hero.shotEnemy(); - } - - // 动作的延迟 1000 / 77 fps - TankTool.yieldMsTime(33); - } - } -} diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/HoldingKeyEventMgr.java b/gui/src/main/java/com/github/kuangcp/tank/util/HoldingKeyEventMgr.java index 3c04f288..edc64b78 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/util/HoldingKeyEventMgr.java +++ b/gui/src/main/java/com/github/kuangcp/tank/util/HoldingKeyEventMgr.java @@ -1,6 +1,9 @@ package com.github.kuangcp.tank.util; +import com.github.kuangcp.tank.constant.DirectType; import com.github.kuangcp.tank.panel.TankGroundPanel; +import lombok.Getter; +import lombok.Setter; import java.awt.event.KeyEvent; import java.util.HashMap; @@ -8,9 +11,11 @@ import java.util.Optional; /** - * @author https://github.com/kuangcp on 2021-09-06 02:58 + * @author Kuangcp on 2021-09-06 02:58 * @see TankGroundPanel#keyPressed */ +@Getter +@Setter public class HoldingKeyEventMgr { public static HoldingKeyEventMgr instance = new HoldingKeyEventMgr(); @@ -20,6 +25,7 @@ public class HoldingKeyEventMgr { private volatile boolean down; private volatile boolean left; private volatile boolean right; + private volatile boolean shot; private volatile boolean ctrl; @@ -28,17 +34,30 @@ public class HoldingKeyEventMgr { private final Map pressMap = new HashMap<>(); public HoldingKeyEventMgr() { + releaseMap.put(KeyEvent.VK_A, () -> this.left = false); releaseMap.put(KeyEvent.VK_D, () -> this.right = false); releaseMap.put(KeyEvent.VK_S, () -> this.down = false); releaseMap.put(KeyEvent.VK_W, () -> this.up = false); - releaseMap.put(KeyEvent.VK_J, () -> this.shot = false); - releaseMap.put(KeyEvent.VK_CONTROL, () -> this.ctrl = false); pressMap.put(KeyEvent.VK_A, () -> this.left = true); pressMap.put(KeyEvent.VK_D, () -> this.right = true); pressMap.put(KeyEvent.VK_S, () -> this.down = true); pressMap.put(KeyEvent.VK_W, () -> this.up = true); + +// releaseMap.put(KeyEvent.VK_A, () -> this.direct = -1); +// releaseMap.put(KeyEvent.VK_D, () -> this.direct = -1); +// releaseMap.put(KeyEvent.VK_S, () -> this.direct = -1); +// releaseMap.put(KeyEvent.VK_W, () -> this.direct = -1); + + releaseMap.put(KeyEvent.VK_J, () -> this.shot = false); + releaseMap.put(KeyEvent.VK_CONTROL, () -> this.ctrl = false); + +// pressMap.put(KeyEvent.VK_A, () -> this.direct = DirectType.LEFT); +// pressMap.put(KeyEvent.VK_D, () -> this.direct = DirectType.RIGHT); +// pressMap.put(KeyEvent.VK_S, () -> this.direct = DirectType.DOWN); +// pressMap.put(KeyEvent.VK_W, () -> this.direct = DirectType.UP); + pressMap.put(KeyEvent.VK_CONTROL, () -> this.ctrl = true); } @@ -50,56 +69,23 @@ public void handleRelease(KeyEvent re) { Optional.ofNullable(releaseMap.get(re.getKeyCode())).ifPresent(Runnable::run); } - public boolean isUp() { - return up; - } - - public void setUp(boolean up) { - this.up = up; - } - - public boolean isDown() { - return down; - } - - public void setDown(boolean down) { - this.down = down; - } - - public boolean isLeft() { - return left; - } - - public void setLeft(boolean left) { - this.left = left; - } - - public boolean isRight() { - return right; - } - - public void setRight(boolean right) { - this.right = right; - } - - public boolean isShot() { - return shot; - } - - public void setShot(boolean shot) { - this.shot = shot; - } - - public boolean isCtrl() { - return ctrl; - } - - public void setCtrl(boolean ctrl) { - this.ctrl = ctrl; - } - public boolean hasPressMoveEvent() { return up || down || left || right; +// return direct != DirectType.NONE; + } + + public int getDirect() { + if (up) { + return DirectType.UP; + } else if (right) { + return DirectType.RIGHT; + } else if (down) { + return DirectType.DOWN; + } else if (left) { + return DirectType.LEFT; + } else { + return DirectType.NONE; + } } @Override diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/executor/AbstractDelayEvent.java b/gui/src/main/java/com/github/kuangcp/tank/util/executor/AbstractDelayEvent.java index a47cabb2..84a5cbaa 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/util/executor/AbstractDelayEvent.java +++ b/gui/src/main/java/com/github/kuangcp/tank/util/executor/AbstractDelayEvent.java @@ -4,7 +4,7 @@ import java.util.concurrent.TimeUnit; /** - * @author https://github.com/kuangcp on 2021-09-17 01:39 + * @author Kuangcp on 2021-09-17 01:39 */ public abstract class AbstractDelayEvent implements DelayEvent { diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/executor/AbstractLoopEvent.java b/gui/src/main/java/com/github/kuangcp/tank/util/executor/AbstractLoopEvent.java index 3c466d3d..407894c5 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/util/executor/AbstractLoopEvent.java +++ b/gui/src/main/java/com/github/kuangcp/tank/util/executor/AbstractLoopEvent.java @@ -8,7 +8,7 @@ import java.util.concurrent.TimeUnit; /** - * @author https://github.com/kuangcp on 2021-09-16 01:44 + * @author Kuangcp on 2021-09-16 01:44 */ @Slf4j public abstract class AbstractLoopEvent implements LoopEvent { diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/executor/CommonEventExecutor.java b/gui/src/main/java/com/github/kuangcp/tank/util/executor/CommonEventExecutor.java index 2fd3b971..9443bbbe 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/util/executor/CommonEventExecutor.java +++ b/gui/src/main/java/com/github/kuangcp/tank/util/executor/CommonEventExecutor.java @@ -6,7 +6,7 @@ import java.util.concurrent.TimeUnit; /** - * @author https://github.com/kuangcp on 2021-09-17 01:06 + * @author Kuangcp on 2021-09-17 01:06 */ @Slf4j public class CommonEventExecutor { diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/executor/DelayEvent.java b/gui/src/main/java/com/github/kuangcp/tank/util/executor/DelayEvent.java index eda7a48b..905dd9f8 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/util/executor/DelayEvent.java +++ b/gui/src/main/java/com/github/kuangcp/tank/util/executor/DelayEvent.java @@ -3,7 +3,7 @@ import java.util.concurrent.Delayed; /** - * @author https://github.com/kuangcp on 2021-09-17 01:35 + * @author Kuangcp on 2021-09-17 01:35 */ public interface DelayEvent extends Runnable, Delayed { } diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/executor/DelayExecutor.java b/gui/src/main/java/com/github/kuangcp/tank/util/executor/DelayExecutor.java index 1aee74ab..c9ef0d27 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/util/executor/DelayExecutor.java +++ b/gui/src/main/java/com/github/kuangcp/tank/util/executor/DelayExecutor.java @@ -7,7 +7,7 @@ import java.util.concurrent.ExecutorService; /** - * @author https://github.com/kuangcp on 2021-09-17 01:35 + * @author Kuangcp on 2021-09-17 01:35 */ public class DelayExecutor { diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/executor/LoopEvent.java b/gui/src/main/java/com/github/kuangcp/tank/util/executor/LoopEvent.java index 28dc9709..ac6dce71 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/util/executor/LoopEvent.java +++ b/gui/src/main/java/com/github/kuangcp/tank/util/executor/LoopEvent.java @@ -3,7 +3,7 @@ import java.util.concurrent.Delayed; /** - * @author https://github.com/kuangcp on 2021-09-16 01:21 + * @author Kuangcp on 2021-09-16 01:21 */ public interface LoopEvent extends Runnable, Delayed { diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/executor/LoopEventExecutor.java b/gui/src/main/java/com/github/kuangcp/tank/util/executor/LoopEventExecutor.java index 0bcbf38a..a254f970 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/util/executor/LoopEventExecutor.java +++ b/gui/src/main/java/com/github/kuangcp/tank/util/executor/LoopEventExecutor.java @@ -8,7 +8,7 @@ import java.util.concurrent.ExecutorService; /** - * @author https://github.com/kuangcp on 2021-09-16 01:20 + * @author Kuangcp on 2021-09-16 01:20 */ @Slf4j public class LoopEventExecutor { diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/executor/MonitorExecutor.java b/gui/src/main/java/com/github/kuangcp/tank/util/executor/MonitorExecutor.java index 8d019a21..1238222d 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/util/executor/MonitorExecutor.java +++ b/gui/src/main/java/com/github/kuangcp/tank/util/executor/MonitorExecutor.java @@ -8,7 +8,7 @@ import java.util.concurrent.ExecutorService; /** - * @author https://github.com/kuangcp on 2021-09-17 00:58 + * @author Kuangcp on 2021-09-17 00:58 */ @Slf4j public class MonitorExecutor { diff --git a/gui/src/main/java/com/github/kuangcp/virusbroadcast/DefaultThreadFactory.java b/gui/src/main/java/com/github/kuangcp/virusbroadcast/DefaultThreadFactory.java index 72d41369..ebda11da 100644 --- a/gui/src/main/java/com/github/kuangcp/virusbroadcast/DefaultThreadFactory.java +++ b/gui/src/main/java/com/github/kuangcp/virusbroadcast/DefaultThreadFactory.java @@ -4,7 +4,7 @@ import java.util.concurrent.atomic.AtomicInteger; /** - * @author https://github.com/kuangcp on 2021-09-05 03:05 + * @author Kuangcp on 2021-09-05 03:05 */ class DefaultThreadFactory implements ThreadFactory { diff --git a/gui/src/main/java/com/github/kuangcp/virusbroadcast/constant/PersonState.java b/gui/src/main/java/com/github/kuangcp/virusbroadcast/constant/PersonState.java index a1d0694e..f49bacc6 100644 --- a/gui/src/main/java/com/github/kuangcp/virusbroadcast/constant/PersonState.java +++ b/gui/src/main/java/com/github/kuangcp/virusbroadcast/constant/PersonState.java @@ -1,7 +1,7 @@ package com.github.kuangcp.virusbroadcast.constant; /** - * @author https://github.com/kuangcp on 2020-02-04 16:53 + * @author Kuangcp on 2020-02-04 16:53 */ public interface PersonState { diff --git a/gui/src/main/java/com/github/kuangcp/virusbroadcast/constant/PersonStateEnum.java b/gui/src/main/java/com/github/kuangcp/virusbroadcast/constant/PersonStateEnum.java index 3fe4d74a..62d1c20a 100644 --- a/gui/src/main/java/com/github/kuangcp/virusbroadcast/constant/PersonStateEnum.java +++ b/gui/src/main/java/com/github/kuangcp/virusbroadcast/constant/PersonStateEnum.java @@ -7,7 +7,7 @@ import java.util.function.BiConsumer; /** - * @author https://github.com/kuangcp on 2020-02-14 12:43 + * @author Kuangcp on 2020-02-14 12:43 */ @Getter public enum PersonStateEnum { diff --git a/gui/src/test/java/com/github/kuangcp/tank/cmd/PlayStageMgrTest.java b/gui/src/test/java/com/github/kuangcp/tank/cmd/PlayStageMgrTest.java index 804f922d..f87dd1f0 100644 --- a/gui/src/test/java/com/github/kuangcp/tank/cmd/PlayStageMgrTest.java +++ b/gui/src/test/java/com/github/kuangcp/tank/cmd/PlayStageMgrTest.java @@ -8,7 +8,7 @@ //import java.util.concurrent.TimeUnit; // ///** -// * @author https://github.com/kuangcp on 2021-09-12 02:05 +// * @author Kuangcp on 2021-09-12 02:05 // */ //public class PlayStageMgrTest { // diff --git a/gui/src/test/java/com/github/kuangcp/tank/util/ListConTest.java b/gui/src/test/java/com/github/kuangcp/tank/util/ListConTest.java index 08292949..0fc4b465 100644 --- a/gui/src/test/java/com/github/kuangcp/tank/util/ListConTest.java +++ b/gui/src/test/java/com/github/kuangcp/tank/util/ListConTest.java @@ -1,7 +1,7 @@ package com.github.kuangcp.tank.util; /** - * @author https://github.com/kuangcp on 2021-10-17 21:45 + * @author Kuangcp on 2021-10-17 21:45 */ public class ListConTest { } diff --git a/gui/src/test/java/com/github/kuangcp/tank/util/LoopEventExecutorTest.java b/gui/src/test/java/com/github/kuangcp/tank/util/LoopEventExecutorTest.java index d0f124c9..baf5ed2f 100644 --- a/gui/src/test/java/com/github/kuangcp/tank/util/LoopEventExecutorTest.java +++ b/gui/src/test/java/com/github/kuangcp/tank/util/LoopEventExecutorTest.java @@ -20,7 +20,7 @@ import java.util.concurrent.TimeUnit; /** - * @author https://github.com/kuangcp on 2021-09-16 01:28 + * @author Kuangcp on 2021-09-16 01:28 */ @Slf4j public class LoopEventExecutorTest { diff --git a/hadoop/src/main/java/com/github/kuangcp/hdfs/hi/GeneralFileActionDemo.java b/hadoop/src/main/java/com/github/kuangcp/hdfs/hi/GeneralFileActionDemo.java index 49aaa1f6..6ddceb73 100644 --- a/hadoop/src/main/java/com/github/kuangcp/hdfs/hi/GeneralFileActionDemo.java +++ b/hadoop/src/main/java/com/github/kuangcp/hdfs/hi/GeneralFileActionDemo.java @@ -14,7 +14,7 @@ import java.util.stream.Collectors; /** - * @author https://github.com/kuangcp + * @author Kuangcp * @date 2019-05-20 11:15 */ @Slf4j diff --git a/hadoop/src/test/java/com/github/kuangcp/hdfs/hi/GeneralFileActionDemoTest.java b/hadoop/src/test/java/com/github/kuangcp/hdfs/hi/GeneralFileActionDemoTest.java index 18682e25..a239f711 100644 --- a/hadoop/src/test/java/com/github/kuangcp/hdfs/hi/GeneralFileActionDemoTest.java +++ b/hadoop/src/test/java/com/github/kuangcp/hdfs/hi/GeneralFileActionDemoTest.java @@ -10,7 +10,7 @@ import static org.hamcrest.core.IsEqual.equalTo; /** - * @author https://github.com/kuangcp + * @author Kuangcp * @date 2019-05-20 11:28 */ @Slf4j diff --git a/java8/src/test/java/com/github/kuangcp/lambda/bug/MultipleExtendsTest.java b/java8/src/test/java/com/github/kuangcp/lambda/bug/MultipleExtendsTest.java index 2a60a331..dbf1e9b4 100644 --- a/java8/src/test/java/com/github/kuangcp/lambda/bug/MultipleExtendsTest.java +++ b/java8/src/test/java/com/github/kuangcp/lambda/bug/MultipleExtendsTest.java @@ -6,7 +6,7 @@ import org.junit.Test; /** - * @author https://github.com/kuangcp on 2019-11-28 19:43 + * @author Kuangcp on 2019-11-28 19:43 */ public class MultipleExtendsTest { diff --git a/java8/src/test/java/com/github/kuangcp/stream/CollectorToMapTest.java b/java8/src/test/java/com/github/kuangcp/stream/CollectorToMapTest.java index ce0391f0..9ba146a1 100644 --- a/java8/src/test/java/com/github/kuangcp/stream/CollectorToMapTest.java +++ b/java8/src/test/java/com/github/kuangcp/stream/CollectorToMapTest.java @@ -10,7 +10,7 @@ import org.junit.Test; /** - * @author https://github.com/kuangcp + * @author Kuangcp */ @Slf4j public class CollectorToMapTest { diff --git a/java8/src/test/java/com/github/kuangcp/stream/CreateStreamTest.java b/java8/src/test/java/com/github/kuangcp/stream/CreateStreamTest.java index 9847ed76..9fc48de5 100644 --- a/java8/src/test/java/com/github/kuangcp/stream/CreateStreamTest.java +++ b/java8/src/test/java/com/github/kuangcp/stream/CreateStreamTest.java @@ -14,7 +14,7 @@ import org.junit.Test; /** - * @author https://github.com/kuangcp on 2020-01-08 16:41 + * @author Kuangcp on 2020-01-08 16:41 */ public class CreateStreamTest { diff --git a/java8/src/test/java/com/github/kuangcp/stream/FlatMapTest.java b/java8/src/test/java/com/github/kuangcp/stream/FlatMapTest.java index 62e861a9..7b6cc0ca 100644 --- a/java8/src/test/java/com/github/kuangcp/stream/FlatMapTest.java +++ b/java8/src/test/java/com/github/kuangcp/stream/FlatMapTest.java @@ -14,7 +14,7 @@ import org.junit.Test; /** - * @author https://github.com/kuangcp on 2019-07-05 11:07 + * @author Kuangcp on 2019-07-05 11:07 */ public class FlatMapTest { diff --git a/java8/src/test/java/com/github/kuangcp/stream/collector/CollectorTest.java b/java8/src/test/java/com/github/kuangcp/stream/collector/CollectorTest.java index fd0c924e..4c255b8a 100644 --- a/java8/src/test/java/com/github/kuangcp/stream/collector/CollectorTest.java +++ b/java8/src/test/java/com/github/kuangcp/stream/collector/CollectorTest.java @@ -10,7 +10,7 @@ import static org.junit.Assert.assertThat; /** - * @author https://github.com/kuangcp on 2019-12-12 16:48 + * @author Kuangcp on 2019-12-12 16:48 */ public class CollectorTest { diff --git a/java8/src/test/java/com/github/kuangcp/stream/map/MapTest.java b/java8/src/test/java/com/github/kuangcp/stream/map/MapTest.java index 82c0a0ff..d54f9142 100644 --- a/java8/src/test/java/com/github/kuangcp/stream/map/MapTest.java +++ b/java8/src/test/java/com/github/kuangcp/stream/map/MapTest.java @@ -10,7 +10,7 @@ import org.junit.Test; /** - * @author https://github.com/kuangcp on 2019-11-25 19:22 + * @author Kuangcp on 2019-11-25 19:22 */ @Slf4j public class MapTest { diff --git a/java8/src/test/java/com/github/kuangcp/time/DateTimeFormatterTest.java b/java8/src/test/java/com/github/kuangcp/time/DateTimeFormatterTest.java index e25e72a1..b2d6eeaa 100644 --- a/java8/src/test/java/com/github/kuangcp/time/DateTimeFormatterTest.java +++ b/java8/src/test/java/com/github/kuangcp/time/DateTimeFormatterTest.java @@ -5,7 +5,7 @@ import org.junit.Test; /** - * @author https://github.com/kuangcp on 2019-12-11 20:41 + * @author Kuangcp on 2019-12-11 20:41 */ public class DateTimeFormatterTest { diff --git a/java8/src/test/java/com/github/kuangcp/time/DurationTest.java b/java8/src/test/java/com/github/kuangcp/time/DurationTest.java index b1d25e90..310c115a 100644 --- a/java8/src/test/java/com/github/kuangcp/time/DurationTest.java +++ b/java8/src/test/java/com/github/kuangcp/time/DurationTest.java @@ -6,7 +6,7 @@ import org.junit.Test; /** - * @author https://github.com/kuangcp on 2019-10-27 11:43 + * @author Kuangcp on 2019-10-27 11:43 */ @Slf4j public class DurationTest { diff --git a/java8/src/test/java/com/github/kuangcp/time/LocalDateTest.java b/java8/src/test/java/com/github/kuangcp/time/LocalDateTest.java index e48de4a9..bac166ae 100644 --- a/java8/src/test/java/com/github/kuangcp/time/LocalDateTest.java +++ b/java8/src/test/java/com/github/kuangcp/time/LocalDateTest.java @@ -5,7 +5,7 @@ import org.junit.Test; /** - * @author https://github.com/kuangcp on 2019-07-09 17:10 + * @author Kuangcp on 2019-07-09 17:10 */ @Slf4j public class LocalDateTest { diff --git a/kafka/src/test/java/com/github/kuangcp/hi/ConsumerDemoTest.java b/kafka/src/test/java/com/github/kuangcp/hi/ConsumerDemoTest.java index a86b1f4d..8bfa79d3 100644 --- a/kafka/src/test/java/com/github/kuangcp/hi/ConsumerDemoTest.java +++ b/kafka/src/test/java/com/github/kuangcp/hi/ConsumerDemoTest.java @@ -4,7 +4,7 @@ import org.junit.Test; /** - * @author https://github.com/kuangcp + * @author Kuangcp * @date 2019-05-21 16:20 */ public class ConsumerDemoTest { diff --git a/kafka/src/test/java/com/github/kuangcp/hi/ProducerDemoTest.java b/kafka/src/test/java/com/github/kuangcp/hi/ProducerDemoTest.java index e2fbf236..63f0bf03 100644 --- a/kafka/src/test/java/com/github/kuangcp/hi/ProducerDemoTest.java +++ b/kafka/src/test/java/com/github/kuangcp/hi/ProducerDemoTest.java @@ -3,7 +3,7 @@ import org.junit.Test; /** - * @author https://github.com/kuangcp + * @author Kuangcp * @date 2019-05-21 16:19 */ public class ProducerDemoTest { diff --git a/mybatis/src/main/java/com/github/kuangcp/sharding/manual/AuthUtil.java b/mybatis/src/main/java/com/github/kuangcp/sharding/manual/AuthUtil.java index 9cfa1288..b91c26f9 100644 --- a/mybatis/src/main/java/com/github/kuangcp/sharding/manual/AuthUtil.java +++ b/mybatis/src/main/java/com/github/kuangcp/sharding/manual/AuthUtil.java @@ -3,7 +3,7 @@ import org.springframework.stereotype.Component; /** - * @author https://github.com/kuangcp on 2021-07-11 18:53 + * @author Kuangcp on 2021-07-11 18:53 */ @Component public class AuthUtil { diff --git a/mybatis/src/main/java/com/github/kuangcp/sharding/manual/ConsistentHashingAlgorithm.java b/mybatis/src/main/java/com/github/kuangcp/sharding/manual/ConsistentHashingAlgorithm.java index 2d75d946..917af766 100644 --- a/mybatis/src/main/java/com/github/kuangcp/sharding/manual/ConsistentHashingAlgorithm.java +++ b/mybatis/src/main/java/com/github/kuangcp/sharding/manual/ConsistentHashingAlgorithm.java @@ -5,7 +5,7 @@ import java.util.*; /** - * @author https://github.com/kuangcp on 2021-07-11 19:13 + * @author Kuangcp on 2021-07-11 19:13 */ public class ConsistentHashingAlgorithm { private MessageDigest md5; diff --git a/mybatis/src/main/java/com/github/kuangcp/sharding/manual/ShardingAlgorithmEnum.java b/mybatis/src/main/java/com/github/kuangcp/sharding/manual/ShardingAlgorithmEnum.java index 70aa3726..dddcae2d 100644 --- a/mybatis/src/main/java/com/github/kuangcp/sharding/manual/ShardingAlgorithmEnum.java +++ b/mybatis/src/main/java/com/github/kuangcp/sharding/manual/ShardingAlgorithmEnum.java @@ -8,7 +8,7 @@ import java.util.function.BiFunction; /** - * @author https://github.com/kuangcp on 2021-07-11 18:41 + * @author Kuangcp on 2021-07-11 18:41 */ @Getter @AllArgsConstructor diff --git a/mybatis/src/main/java/com/github/kuangcp/sharding/manual/ShardingAlgorithmType.java b/mybatis/src/main/java/com/github/kuangcp/sharding/manual/ShardingAlgorithmType.java index d90db68f..b8ee3714 100644 --- a/mybatis/src/main/java/com/github/kuangcp/sharding/manual/ShardingAlgorithmType.java +++ b/mybatis/src/main/java/com/github/kuangcp/sharding/manual/ShardingAlgorithmType.java @@ -1,7 +1,7 @@ package com.github.kuangcp.sharding.manual; /** - * @author https://github.com/kuangcp on 2021-07-11 18:59 + * @author Kuangcp on 2021-07-11 18:59 */ public interface ShardingAlgorithmType { int MOD = 1; diff --git a/mybatis/src/main/java/com/github/kuangcp/sharding/manual/ShardingTable.java b/mybatis/src/main/java/com/github/kuangcp/sharding/manual/ShardingTable.java index eb71605c..aa778f20 100644 --- a/mybatis/src/main/java/com/github/kuangcp/sharding/manual/ShardingTable.java +++ b/mybatis/src/main/java/com/github/kuangcp/sharding/manual/ShardingTable.java @@ -3,7 +3,7 @@ import java.lang.annotation.*; /** - * @author https://github.com/kuangcp on 2021-07-11 18:19 + * @author Kuangcp on 2021-07-11 18:19 */ @Documented @Retention(RetentionPolicy.RUNTIME) diff --git a/mybatis/src/main/java/com/github/kuangcp/sharding/manual/ShardingTableInterceptor.java b/mybatis/src/main/java/com/github/kuangcp/sharding/manual/ShardingTableInterceptor.java index 9a4f3296..2ebb6b8f 100644 --- a/mybatis/src/main/java/com/github/kuangcp/sharding/manual/ShardingTableInterceptor.java +++ b/mybatis/src/main/java/com/github/kuangcp/sharding/manual/ShardingTableInterceptor.java @@ -19,7 +19,7 @@ import java.util.Properties; /** - * @author https://github.com/kuangcp on 2021-07-11 18:19 + * @author Kuangcp on 2021-07-11 18:19 */ @Slf4j @Component diff --git a/mybatis/src/main/java/com/github/kuangcp/stream/Report.java b/mybatis/src/main/java/com/github/kuangcp/stream/Report.java index 38e33f83..947cf969 100644 --- a/mybatis/src/main/java/com/github/kuangcp/stream/Report.java +++ b/mybatis/src/main/java/com/github/kuangcp/stream/Report.java @@ -7,7 +7,7 @@ import java.util.Date; /** - * @author https://github.com/kuangcp on 2021-07-12 23:50 + * @author Kuangcp on 2021-07-12 23:50 */ @Data @Builder diff --git a/mybatis/src/main/java/com/github/kuangcp/stream/dao/ReportDao.java b/mybatis/src/main/java/com/github/kuangcp/stream/dao/ReportDao.java index ffad2a2c..db339ce5 100644 --- a/mybatis/src/main/java/com/github/kuangcp/stream/dao/ReportDao.java +++ b/mybatis/src/main/java/com/github/kuangcp/stream/dao/ReportDao.java @@ -14,7 +14,7 @@ import java.util.List; /** - * @author https://github.com/kuangcp on 2021-07-12 23:52 + * @author Kuangcp on 2021-07-12 23:52 */ @Repository public interface ReportDao extends BaseMapper { diff --git a/mybatis/src/test/java/com/github/kuangcp/sharding/manual/ConsistentHashingAlgorithmTest.java b/mybatis/src/test/java/com/github/kuangcp/sharding/manual/ConsistentHashingAlgorithmTest.java index 8b474b9b..b608c01e 100644 --- a/mybatis/src/test/java/com/github/kuangcp/sharding/manual/ConsistentHashingAlgorithmTest.java +++ b/mybatis/src/test/java/com/github/kuangcp/sharding/manual/ConsistentHashingAlgorithmTest.java @@ -8,7 +8,7 @@ import java.util.stream.Collectors; /** - * @author https://github.com/kuangcp on 2021-07-11 19:32 + * @author Kuangcp on 2021-07-11 19:32 */ @Slf4j public class ConsistentHashingAlgorithmTest { diff --git a/mybatis/src/test/java/com/github/kuangcp/stream/CursorSessionSpringBootTest.java b/mybatis/src/test/java/com/github/kuangcp/stream/CursorSessionSpringBootTest.java index 421828c4..d5db8979 100644 --- a/mybatis/src/test/java/com/github/kuangcp/stream/CursorSessionSpringBootTest.java +++ b/mybatis/src/test/java/com/github/kuangcp/stream/CursorSessionSpringBootTest.java @@ -17,7 +17,7 @@ import java.util.stream.IntStream; /** - * @author https://github.com/kuangcp on 2021-07-12 23:29 + * @author Kuangcp on 2021-07-12 23:29 */ @Slf4j public class CursorSessionSpringBootTest extends SpringBootTestStarter { diff --git a/netty/src/main/java/netty/websocket/ChannelSupervise.java b/netty/src/main/java/netty/websocket/ChannelSupervise.java index 068d36f4..e3c7d8ab 100644 --- a/netty/src/main/java/netty/websocket/ChannelSupervise.java +++ b/netty/src/main/java/netty/websocket/ChannelSupervise.java @@ -12,7 +12,7 @@ import java.util.concurrent.ConcurrentMap; /** - * @author https://github.com/kuangcp on 2021-05-18 08:34 + * @author Kuangcp on 2021-05-18 08:34 */ @Slf4j public class ChannelSupervise { diff --git a/netty/src/main/java/netty/websocket/NioWebSocketChannelInitializer.java b/netty/src/main/java/netty/websocket/NioWebSocketChannelInitializer.java index 776e0d5f..b1f314dd 100644 --- a/netty/src/main/java/netty/websocket/NioWebSocketChannelInitializer.java +++ b/netty/src/main/java/netty/websocket/NioWebSocketChannelInitializer.java @@ -13,7 +13,7 @@ import static io.netty.handler.codec.http.HttpHeaders.Names.WEBSOCKET_PROTOCOL; /** - * @author https://github.com/kuangcp on 2021-05-18 08:33 + * @author Kuangcp on 2021-05-18 08:33 */ public class NioWebSocketChannelInitializer extends ChannelInitializer { diff --git a/netty/src/main/java/netty/websocket/NioWebSocketHandler.java b/netty/src/main/java/netty/websocket/NioWebSocketHandler.java index 2dc00250..28e1c7a7 100644 --- a/netty/src/main/java/netty/websocket/NioWebSocketHandler.java +++ b/netty/src/main/java/netty/websocket/NioWebSocketHandler.java @@ -15,7 +15,7 @@ import java.util.concurrent.ConcurrentHashMap; /** - * @author https://github.com/kuangcp on 2021-05-18 08:33 + * @author Kuangcp on 2021-05-18 08:33 */ @Slf4j public class NioWebSocketHandler extends SimpleChannelInboundHandler { diff --git a/netty/src/main/java/netty/websocket/Server.java b/netty/src/main/java/netty/websocket/Server.java index e6b2ac7e..2c7da839 100644 --- a/netty/src/main/java/netty/websocket/Server.java +++ b/netty/src/main/java/netty/websocket/Server.java @@ -15,7 +15,7 @@ * 客户端使用 client.html * 压测可使用 https://github.com/Kuangcp/GoBase/tree/master/toolbox/web-socket * - * @author https://github.com/kuangcp on 2021-05-18 08:32 + * @author Kuangcp on 2021-05-18 08:32 */ @Slf4j public class Server { diff --git a/netty/src/test/java/netty/timeServer/TimeClientTest.java b/netty/src/test/java/netty/timeServer/TimeClientTest.java index b14949d6..363e7e95 100644 --- a/netty/src/test/java/netty/timeServer/TimeClientTest.java +++ b/netty/src/test/java/netty/timeServer/TimeClientTest.java @@ -8,7 +8,7 @@ import java.util.concurrent.Executors; /** - * @author https://github.com/kuangcp on 2020-08-22 14:28 + * @author Kuangcp on 2020-08-22 14:28 */ @Slf4j public class TimeClientTest { diff --git a/network/src/main/java/com/github/kuangcp/bio/multicast/Consumer.java b/network/src/main/java/com/github/kuangcp/bio/multicast/Consumer.java index d89d30cc..ede51799 100644 --- a/network/src/main/java/com/github/kuangcp/bio/multicast/Consumer.java +++ b/network/src/main/java/com/github/kuangcp/bio/multicast/Consumer.java @@ -7,7 +7,7 @@ import java.net.MulticastSocket; /** - * @author https://github.com/kuangcp on 2020-01-15 20:34 + * @author Kuangcp on 2020-01-15 20:34 */ @Slf4j public class Consumer { diff --git a/network/src/main/java/com/github/kuangcp/bio/multicast/MulticastConstant.java b/network/src/main/java/com/github/kuangcp/bio/multicast/MulticastConstant.java index 7ea59cc3..1e54457e 100644 --- a/network/src/main/java/com/github/kuangcp/bio/multicast/MulticastConstant.java +++ b/network/src/main/java/com/github/kuangcp/bio/multicast/MulticastConstant.java @@ -1,7 +1,7 @@ package com.github.kuangcp.bio.multicast; /** - * @author https://github.com/kuangcp on 2021-05-08 08:13 + * @author Kuangcp on 2021-05-08 08:13 */ public interface MulticastConstant { diff --git a/network/src/main/java/com/github/kuangcp/bio/multicast/Producer.java b/network/src/main/java/com/github/kuangcp/bio/multicast/Producer.java index 82b8bb4c..088d33c5 100644 --- a/network/src/main/java/com/github/kuangcp/bio/multicast/Producer.java +++ b/network/src/main/java/com/github/kuangcp/bio/multicast/Producer.java @@ -7,7 +7,7 @@ import java.net.MulticastSocket; /** - * @author https://github.com/kuangcp on 2020-01-15 20:30 + * @author Kuangcp on 2020-01-15 20:30 */ @Slf4j public class Producer { diff --git a/network/src/test/java/com/github/kuangcp/runable/SlowRequestTest.java b/network/src/test/java/com/github/kuangcp/runable/SlowRequestTest.java index 1c3f294b..0d42d193 100644 --- a/network/src/test/java/com/github/kuangcp/runable/SlowRequestTest.java +++ b/network/src/test/java/com/github/kuangcp/runable/SlowRequestTest.java @@ -9,7 +9,7 @@ import org.junit.Test; /** - * @author https://github.com/kuangcp on 2020-05-17 22:07 + * @author Kuangcp on 2020-05-17 22:07 */ @Slf4j public class SlowRequestTest { diff --git a/pattern/src/main/java/com/github/kuangcp/singleton/StaticLazyWithSyncBlock.java b/pattern/src/main/java/com/github/kuangcp/singleton/StaticLazyWithSyncBlock.java index 7353e2bc..6995ca27 100644 --- a/pattern/src/main/java/com/github/kuangcp/singleton/StaticLazyWithSyncBlock.java +++ b/pattern/src/main/java/com/github/kuangcp/singleton/StaticLazyWithSyncBlock.java @@ -2,7 +2,7 @@ /** * TODO 验证 - * @author https://github.com/kuangcp + * @author Kuangcp * @date 2019-05-08 14:26 */ public class StaticLazyWithSyncBlock { diff --git a/question/src/main/java/com/github/kuangcp/math/Martingale.java b/question/src/main/java/com/github/kuangcp/math/Martingale.java index a0564fe1..7abadfff 100644 --- a/question/src/main/java/com/github/kuangcp/math/Martingale.java +++ b/question/src/main/java/com/github/kuangcp/math/Martingale.java @@ -4,7 +4,7 @@ import lombok.extern.slf4j.Slf4j; /** - * @author https://github.com/kuangcp + * @author Kuangcp * 2019-06-16 11:19 */ @Slf4j diff --git a/question/src/main/java/com/github/kuangcp/situation/asm/cglib/SimpleTransformer.java b/question/src/main/java/com/github/kuangcp/situation/asm/cglib/SimpleTransformer.java index e33426f1..d1792425 100644 --- a/question/src/main/java/com/github/kuangcp/situation/asm/cglib/SimpleTransformer.java +++ b/question/src/main/java/com/github/kuangcp/situation/asm/cglib/SimpleTransformer.java @@ -3,7 +3,7 @@ import net.sf.cglib.beans.BeanCopier; /** - * @author https://github.com/kuangcp on 2019-06-13 09:12 + * @author Kuangcp on 2019-06-13 09:12 */ class SimpleTransformer { diff --git a/question/src/test/java/com/github/kuangcp/math/MartingaleTest.java b/question/src/test/java/com/github/kuangcp/math/MartingaleTest.java index 565afc66..5a677ae4 100644 --- a/question/src/test/java/com/github/kuangcp/math/MartingaleTest.java +++ b/question/src/test/java/com/github/kuangcp/math/MartingaleTest.java @@ -4,7 +4,7 @@ import org.junit.Test; /** - * @author https://github.com/kuangcp + * @author Kuangcp * 2019-06-16 11:36 */ @Slf4j diff --git a/question/src/test/java/com/github/kuangcp/situation/asm/cglib/SimpleTransformerTest.java b/question/src/test/java/com/github/kuangcp/situation/asm/cglib/SimpleTransformerTest.java index 351e2997..7994f738 100644 --- a/question/src/test/java/com/github/kuangcp/situation/asm/cglib/SimpleTransformerTest.java +++ b/question/src/test/java/com/github/kuangcp/situation/asm/cglib/SimpleTransformerTest.java @@ -4,7 +4,7 @@ import org.junit.Test; /** - * @author https://github.com/kuangcp + * @author Kuangcp */ @Slf4j public class SimpleTransformerTest { diff --git a/question/src/test/java/com/github/kuangcp/situation/commoncode/CommonRelationTest.java b/question/src/test/java/com/github/kuangcp/situation/commoncode/CommonRelationTest.java index 39446cbd..5e3a97db 100644 --- a/question/src/test/java/com/github/kuangcp/situation/commoncode/CommonRelationTest.java +++ b/question/src/test/java/com/github/kuangcp/situation/commoncode/CommonRelationTest.java @@ -26,7 +26,7 @@ import java.util.stream.Stream; /** - * @author https://github.com/kuangcp on 2022-06-30 10:07 + * @author Kuangcp on 2022-06-30 10:07 */ @Slf4j public class CommonRelationTest { diff --git a/spring/src/main/java/com/github/kuangcp/proxy/dao/common/InterceptorLogic.java b/spring/src/main/java/com/github/kuangcp/proxy/dao/common/InterceptorLogic.java index 4369ff96..d3e6bd21 100644 --- a/spring/src/main/java/com/github/kuangcp/proxy/dao/common/InterceptorLogic.java +++ b/spring/src/main/java/com/github/kuangcp/proxy/dao/common/InterceptorLogic.java @@ -7,7 +7,7 @@ * 代理部分公共逻辑 * 思考将 Transaction 和 Permission 抽象一层就成为了 Aspect AOP 这套设计了 * - * @author https://github.com/kuangcp on 2019-09-29 01:20 + * @author Kuangcp on 2019-09-29 01:20 */ @Slf4j public class InterceptorLogic { diff --git a/spring/src/main/java/com/github/kuangcp/proxy/dao/common/Permission.java b/spring/src/main/java/com/github/kuangcp/proxy/dao/common/Permission.java index c2241c1a..f2ce2681 100644 --- a/spring/src/main/java/com/github/kuangcp/proxy/dao/common/Permission.java +++ b/spring/src/main/java/com/github/kuangcp/proxy/dao/common/Permission.java @@ -3,7 +3,7 @@ import lombok.extern.slf4j.Slf4j; /** - * @author https://github.com/kuangcp on 2019-09-29 01:30 + * @author Kuangcp on 2019-09-29 01:30 */ @Slf4j public class Permission { diff --git a/spring/src/main/java/com/github/kuangcp/proxy/dao/jdkproxy/PermissionInterceptor.java b/spring/src/main/java/com/github/kuangcp/proxy/dao/jdkproxy/PermissionInterceptor.java index bc74b7ea..9c6013d6 100644 --- a/spring/src/main/java/com/github/kuangcp/proxy/dao/jdkproxy/PermissionInterceptor.java +++ b/spring/src/main/java/com/github/kuangcp/proxy/dao/jdkproxy/PermissionInterceptor.java @@ -11,7 +11,7 @@ /** * 动态增强,织入权限控制 * - * @author https://github.com/kuangcp on 2019-09-29 01:33 + * @author Kuangcp on 2019-09-29 01:33 */ @Slf4j @AllArgsConstructor diff --git a/spring/src/test/java/com/github/kuangcp/util/BeanCopyTest.java b/spring/src/test/java/com/github/kuangcp/util/BeanCopyTest.java index 7203a54d..95c537dc 100644 --- a/spring/src/test/java/com/github/kuangcp/util/BeanCopyTest.java +++ b/spring/src/test/java/com/github/kuangcp/util/BeanCopyTest.java @@ -7,7 +7,7 @@ import org.springframework.beans.BeanUtils; /** - * @author https://github.com/kuangcp on 2020-04-21 20:31 + * @author Kuangcp on 2020-04-21 20:31 */ public class BeanCopyTest { diff --git a/web/src/main/java/com/github/kuangcp/validation/ErrorMsgConstant.java b/web/src/main/java/com/github/kuangcp/validation/ErrorMsgConstant.java index a4ec6f21..d0c51128 100644 --- a/web/src/main/java/com/github/kuangcp/validation/ErrorMsgConstant.java +++ b/web/src/main/java/com/github/kuangcp/validation/ErrorMsgConstant.java @@ -1,7 +1,7 @@ package com.github.kuangcp.validation; /** - * @author https://github.com/kuangcp on 2021-05-08 06:16 + * @author Kuangcp on 2021-05-08 06:16 */ public interface ErrorMsgConstant { From 064bb8c6e7d4fb993fd7db4f3a0187ba55aa469e Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Sun, 7 Jul 2024 00:44:45 +0800 Subject: [PATCH 402/476] copy server --- gui/pom.xml | 10 + .../github/kuangcp/tank/mgr/PlayStageMgr.java | 2 +- .../kuangcp/tank/ws/ChannelSupervise.java | 51 +++++ .../com/github/kuangcp/tank/ws/Const.java | 10 + .../ws/NioWebSocketChannelInitializer.java | 36 ++++ .../kuangcp/tank/ws/NioWebSocketHandler.java | 199 ++++++++++++++++++ .../java/com/github/kuangcp/tank/ws/Readme.md | 10 + .../com/github/kuangcp/tank/ws/Server.java | 50 +++++ 8 files changed, 367 insertions(+), 1 deletion(-) create mode 100644 gui/src/main/java/com/github/kuangcp/tank/ws/ChannelSupervise.java create mode 100644 gui/src/main/java/com/github/kuangcp/tank/ws/Const.java create mode 100644 gui/src/main/java/com/github/kuangcp/tank/ws/NioWebSocketChannelInitializer.java create mode 100644 gui/src/main/java/com/github/kuangcp/tank/ws/NioWebSocketHandler.java create mode 100644 gui/src/main/java/com/github/kuangcp/tank/ws/Readme.md create mode 100644 gui/src/main/java/com/github/kuangcp/tank/ws/Server.java diff --git a/gui/pom.xml b/gui/pom.xml index c7621822..bd92fd93 100644 --- a/gui/pom.xml +++ b/gui/pom.xml @@ -17,11 +17,21 @@ UTF-8 UTF-8 + + com.github.kuangcp kcp-tool + + io.netty + netty-all + + + org.apache.commons + commons-lang3 + diff --git a/gui/src/main/java/com/github/kuangcp/tank/mgr/PlayStageMgr.java b/gui/src/main/java/com/github/kuangcp/tank/mgr/PlayStageMgr.java index f5a1bf6f..9e7c4667 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/mgr/PlayStageMgr.java +++ b/gui/src/main/java/com/github/kuangcp/tank/mgr/PlayStageMgr.java @@ -44,7 +44,7 @@ public class PlayStageMgr { /** * 无敌状态 时间 */ - static long invincibleMs = 5000L; + static long invincibleMs = 5_000L; // 场景 上下文 public List enemyTanks; diff --git a/gui/src/main/java/com/github/kuangcp/tank/ws/ChannelSupervise.java b/gui/src/main/java/com/github/kuangcp/tank/ws/ChannelSupervise.java new file mode 100644 index 00000000..550c2a38 --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/ws/ChannelSupervise.java @@ -0,0 +1,51 @@ +package com.github.kuangcp.tank.ws; + +import io.netty.channel.Channel; +import io.netty.channel.ChannelId; +import io.netty.channel.group.ChannelGroup; +import io.netty.channel.group.DefaultChannelGroup; +import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; +import io.netty.util.concurrent.GlobalEventExecutor; +import lombok.extern.slf4j.Slf4j; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +/** + * @author Kuangcp on 2021-05-18 08:34 + */ +@Slf4j +public class ChannelSupervise { + + private static final ChannelGroup GLOBAL_GROUP = new DefaultChannelGroup( + GlobalEventExecutor.INSTANCE); + private static final ConcurrentMap CHANNEL_MAP = new ConcurrentHashMap<>(); + + public static void addChannel(Channel channel) { + GLOBAL_GROUP.add(channel); + CHANNEL_MAP.put(channel.id().asShortText(), channel.id()); + printState(); + } + + public static void removeChannel(Channel channel) { + GLOBAL_GROUP.remove(channel); + CHANNEL_MAP.remove(channel.id().asShortText()); + printState(); + } + + public static Channel findChannel(String id) { + return GLOBAL_GROUP.find(CHANNEL_MAP.get(id)); + } + + public static void send2All(TextWebSocketFrame tws) { + GLOBAL_GROUP.writeAndFlush(tws); + } + + private static void printState() { + log.debug("channelSize={} global={}", CHANNEL_MAP.size(), GLOBAL_GROUP.size()); + } + + public static void watchState() { + log.info("online {}", CHANNEL_MAP.size()); + } +} diff --git a/gui/src/main/java/com/github/kuangcp/tank/ws/Const.java b/gui/src/main/java/com/github/kuangcp/tank/ws/Const.java new file mode 100644 index 00000000..b448cecb --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/ws/Const.java @@ -0,0 +1,10 @@ +package com.github.kuangcp.tank.ws; + +/** + * + * @author Kuangcp + * 2024-02-28 20:12 + */ +public interface Const { + String webSocketPath = "/ws"; +} diff --git a/gui/src/main/java/com/github/kuangcp/tank/ws/NioWebSocketChannelInitializer.java b/gui/src/main/java/com/github/kuangcp/tank/ws/NioWebSocketChannelInitializer.java new file mode 100644 index 00000000..b873e3d5 --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/ws/NioWebSocketChannelInitializer.java @@ -0,0 +1,36 @@ +package com.github.kuangcp.tank.ws; + +import io.netty.channel.AdaptiveRecvByteBufAllocator; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelPipeline; +import io.netty.channel.socket.SocketChannel; +import io.netty.handler.codec.http.HttpObjectAggregator; +import io.netty.handler.codec.http.HttpServerCodec; +import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler; +import io.netty.handler.logging.LoggingHandler; +import io.netty.handler.stream.ChunkedWriteHandler; + +import static io.netty.handler.codec.http.HttpHeaders.Names.WEBSOCKET_PROTOCOL; + +/** + * @author Kuangcp on 2021-05-18 08:33 + */ +public class NioWebSocketChannelInitializer extends ChannelInitializer { + + /** + * @see AdaptiveRecvByteBufAllocator.HandleImpl#record(int) 实现扩缩容读写ByteBuf + */ + @Override + protected void initChannel(SocketChannel ch) { + ChannelPipeline pipeline = ch.pipeline(); + pipeline.addLast("logging", new LoggingHandler("DEBUG"));//设置log监听器,并且日志级别为debug,方便观察运行流程 + pipeline.addLast("http-codec", new HttpServerCodec());//设置解码器 + pipeline.addLast("aggregator", new HttpObjectAggregator(65536));//聚合器,使用websocket会用到 + pipeline.addLast("http-chunked", new ChunkedWriteHandler());//用于大数据的分区传输 + pipeline.addLast("handler", new NioWebSocketHandler());//自定义的业务handler + + // checkStartsWith 为true 支持路径带参数 + // maxFrameSize 设置的是最大可申请的ByteBuf,实际上使用时是按需申请和回收内存 + pipeline.addLast("", new WebSocketServerProtocolHandler(Const.webSocketPath, WEBSOCKET_PROTOCOL, true, 65536, false, true)); + } +} diff --git a/gui/src/main/java/com/github/kuangcp/tank/ws/NioWebSocketHandler.java b/gui/src/main/java/com/github/kuangcp/tank/ws/NioWebSocketHandler.java new file mode 100644 index 00000000..4da893d0 --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/ws/NioWebSocketHandler.java @@ -0,0 +1,199 @@ +package com.github.kuangcp.tank.ws; + + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.Channel; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelFutureListener; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.SimpleChannelInboundHandler; +import io.netty.handler.codec.http.DefaultFullHttpResponse; +import io.netty.handler.codec.http.FullHttpRequest; +import io.netty.handler.codec.http.HttpUtil; +import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame; +import io.netty.handler.codec.http.websocketx.PingWebSocketFrame; +import io.netty.handler.codec.http.websocketx.PongWebSocketFrame; +import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; +import io.netty.handler.codec.http.websocketx.WebSocketFrame; +import io.netty.util.CharsetUtil; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * @author Kuangcp on 2021-05-18 08:33 + */ +@Slf4j +public class NioWebSocketHandler extends SimpleChannelInboundHandler { + + private static final Map userMap = new ConcurrentHashMap<>(); + + @Override + public void channelActive(ChannelHandlerContext ctx) throws Exception { + log.debug("客户端加入连接:" + ctx.channel()); + ChannelSupervise.addChannel(ctx.channel()); + } + + @Override + public void channelInactive(ChannelHandlerContext ctx) throws Exception { + log.debug("客户端断开连接:" + ctx.channel()); + ChannelSupervise.removeChannel(ctx.channel()); + } + + @Override + public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { + ctx.flush(); + } + +// private WebSocketServerHandshaker handShaker; +// +// private void handleHttpRequest(ChannelHandlerContext ctx, FullHttpRequest req) { +// //要求Upgrade为websocket,过滤掉get/Post +// if (!req.decoderResult().isSuccess() +// || (!"websocket".equals(req.headers().get("Upgrade")))) { +// //若不是websocket方式,则创建BAD_REQUEST的req,返回给客户端 +// sendHttpResponse(ctx, req, new DefaultFullHttpResponse( +// HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST)); +// return; +// } +// +// WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory( +// "ws://localhost:7094/ws", null, false); +// handShaker = wsFactory.newHandshaker(req); +// if (handShaker == null) { +// WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel()); +// } else { +// handShaker.handshake(ctx.channel(), req); +// } +// } + + /** + * 拒绝不合法的请求,并返回错误信息 + */ + private static void sendHttpResponse(ChannelHandlerContext ctx, FullHttpRequest req, DefaultFullHttpResponse res) { + // 返回应答给客户端 + if (res.status().code() != 200) { + ByteBuf buf = Unpooled.copiedBuffer(res.status().toString(), CharsetUtil.UTF_8); + res.content().writeBytes(buf); + buf.release(); + } + ChannelFuture f = ctx.channel().writeAndFlush(res); + // 如果是非Keep-Alive,关闭连接 + if (!HttpUtil.isKeepAlive(req) || res.status().code() != 200) { + f.addListener(ChannelFutureListener.CLOSE); + } + } + + + /** + * 将路径参数转换成Map对象,如果路径参数出现重复参数名,将以最后的参数值为准 + * + * @param uri 传入的携带参数的路径 + */ + public static Map getParams(String uri) { + Map params = new HashMap<>(10); + + int idx = uri.indexOf("?"); + if (idx != -1) { + String[] paramsArr = uri.substring(idx + 1).split("&"); + + for (String param : paramsArr) { + idx = param.indexOf("="); + params.put(param.substring(0, idx), param.substring(idx + 1)); + } + } + + return params; + } + + /** + * 获取URI中参数以外部分路径 + */ + public static String getBasePath(String uri) { + if (uri == null || uri.isEmpty()) + return null; + + int idx = uri.indexOf("?"); + if (idx == -1) + return uri; + + return uri.substring(0, idx); + } + + + /** + * 唯一的一次http请求,用于升级至websocket 需要正确响应 + */ + private void fullHttpRequestHandler(ChannelHandlerContext ctx, FullHttpRequest request) { + String uri = request.uri(); + Map params = getParams(uri); +// log.info("客户端请求参数:{}", params); + + String userIdStr = params.get("userId"); + if (StringUtils.isNotBlank(userIdStr)) { + long userId = Long.parseLong(userIdStr); + userMap.put(userId, ctx.channel()); + } + + // 判断请求路径是否跟配置中的一致 + if (Const.webSocketPath.equals(getBasePath(uri))) { + // 因为有可能携带了参数,导致客户端一直无法返回握手包,因此在校验通过后,重置请求路径 + request.setUri(Const.webSocketPath); + } else { + ctx.close(); + } + } + + + @Override + protected void channelRead0(ChannelHandlerContext ctx, WebSocketFrame frame) { + if (frame instanceof PingWebSocketFrame) { + pingWebSocketFrameHandler(ctx, (PingWebSocketFrame) frame); + } else if (frame instanceof TextWebSocketFrame) { + textWebSocketFrameHandler(ctx, (TextWebSocketFrame) frame); + } else if (frame instanceof CloseWebSocketFrame) { + closeWebSocketFrameHandler(ctx, (CloseWebSocketFrame) frame); + } + } + + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { +// log.info("客户端请求数据类型:{}", msg.getClass()); + if (msg instanceof FullHttpRequest) { + fullHttpRequestHandler(ctx, (FullHttpRequest) msg); + } + super.channelRead(ctx, msg); + } + + + /** + * 客户端发送断开请求处理 + * + * @param ctx + * @param frame + */ + private void closeWebSocketFrameHandler(ChannelHandlerContext ctx, CloseWebSocketFrame frame) { + ctx.close(); + } + + /** + * 创建连接之后,客户端发送的消息都会在这里处理 + * + * @param ctx + * @param frame + */ + private void textWebSocketFrameHandler(ChannelHandlerContext ctx, TextWebSocketFrame frame) { + ctx.channel().writeAndFlush(frame.retain()); + } + + /** + * 处理客户端心跳包 + */ + private void pingWebSocketFrameHandler(ChannelHandlerContext ctx, PingWebSocketFrame frame) { + ctx.channel().writeAndFlush(new PongWebSocketFrame(frame.content().retain())); + } +} diff --git a/gui/src/main/java/com/github/kuangcp/tank/ws/Readme.md b/gui/src/main/java/com/github/kuangcp/tank/ws/Readme.md new file mode 100644 index 00000000..36b439b8 --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/ws/Readme.md @@ -0,0 +1,10 @@ +# 远程通信 + +> [Source](https://github.com/Kuangcp/JavaBase/tree/master/netty/src/main/java/netty/websocket) + + +TODO + +- 手机端控制 +- 跨主机联机 + diff --git a/gui/src/main/java/com/github/kuangcp/tank/ws/Server.java b/gui/src/main/java/com/github/kuangcp/tank/ws/Server.java new file mode 100644 index 00000000..87241346 --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/ws/Server.java @@ -0,0 +1,50 @@ +package com.github.kuangcp.tank.ws; + +import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.Channel; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.nio.NioServerSocketChannel; +import lombok.extern.slf4j.Slf4j; + +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +/** + * 客户端使用 client.html + * 压测可使用 https://github.com/Kuangcp/GoBase/tree/master/toolbox/web-socket + * + * @author Kuangcp on 2021-05-18 08:32 + */ +@Slf4j +public class Server { + + private void init() { + ScheduledExecutorService pool = Executors.newScheduledThreadPool(1); + pool.scheduleAtFixedRate(ChannelSupervise::watchState, 5, 3, TimeUnit.SECONDS); + + log.info("正在启动WebSocket服务器"); + NioEventLoopGroup boss = new NioEventLoopGroup(); + NioEventLoopGroup work = new NioEventLoopGroup(); + try { + ServerBootstrap bootstrap = new ServerBootstrap(); + bootstrap.group(boss, work); + bootstrap.channel(NioServerSocketChannel.class); + bootstrap.childHandler(new NioWebSocketChannelInitializer()); + Channel channel = bootstrap.bind(7094).sync().channel(); + log.info("WebSocket服务器启动成功:{}", channel); + channel.closeFuture().sync(); + } catch (InterruptedException e) { + log.info("", e); + } finally { + boss.shutdownGracefully(); + work.shutdownGracefully(); + log.info("WebSocket服务器已关闭"); + } + } + + public static void main(String[] args) { + new Server().init(); + } +} + From 284c53941944884a36a62150a6fc8aa48eda5ccf Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Sun, 7 Jul 2024 11:28:46 +0800 Subject: [PATCH 403/476] ws module --- .../com/github/kuangcp/tank/MainTankGame.java | 4 + .../kuangcp/tank/backup/v1/MainPanelV1.java | 137 --------------- .../github/kuangcp/tank/backup/v1/Readme.md | 1 - .../kuangcp/tank/backup/v1/TankGameV1.java | 29 ---- .../kuangcp/tank/backup/v2/MainPanelV2.java | 158 ------------------ .../github/kuangcp/tank/backup/v2/Readme.md | 1 - .../kuangcp/tank/backup/v2/TankGameV2.java | 46 ----- ...ializer.java => WsChannelInitializer.java} | 6 +- ...Supervise.java => WsChannelSupervise.java} | 2 +- .../tank/ws/{Const.java => WsConst.java} | 2 +- ...ioWebSocketHandler.java => WsHandler.java} | 32 +--- .../tank/ws/{Server.java => WsServer.java} | 10 +- 12 files changed, 19 insertions(+), 409 deletions(-) delete mode 100644 gui/src/main/java/com/github/kuangcp/tank/backup/v1/MainPanelV1.java delete mode 100644 gui/src/main/java/com/github/kuangcp/tank/backup/v1/Readme.md delete mode 100644 gui/src/main/java/com/github/kuangcp/tank/backup/v1/TankGameV1.java delete mode 100644 gui/src/main/java/com/github/kuangcp/tank/backup/v2/MainPanelV2.java delete mode 100644 gui/src/main/java/com/github/kuangcp/tank/backup/v2/Readme.md delete mode 100644 gui/src/main/java/com/github/kuangcp/tank/backup/v2/TankGameV2.java rename gui/src/main/java/com/github/kuangcp/tank/ws/{NioWebSocketChannelInitializer.java => WsChannelInitializer.java} (86%) rename gui/src/main/java/com/github/kuangcp/tank/ws/{ChannelSupervise.java => WsChannelSupervise.java} (97%) rename gui/src/main/java/com/github/kuangcp/tank/ws/{Const.java => WsConst.java} (82%) rename gui/src/main/java/com/github/kuangcp/tank/ws/{NioWebSocketHandler.java => WsHandler.java} (81%) rename gui/src/main/java/com/github/kuangcp/tank/ws/{Server.java => WsServer.java} (86%) diff --git a/gui/src/main/java/com/github/kuangcp/tank/MainTankGame.java b/gui/src/main/java/com/github/kuangcp/tank/MainTankGame.java index a5f06284..4bb3f8f4 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/MainTankGame.java +++ b/gui/src/main/java/com/github/kuangcp/tank/MainTankGame.java @@ -3,6 +3,7 @@ import com.github.kuangcp.tank.frame.MainFrame; import com.github.kuangcp.tank.resource.ResourceMgr; import com.github.kuangcp.tank.util.executor.MonitorExecutor; +import com.github.kuangcp.tank.ws.WsServer; import lombok.extern.slf4j.Slf4j; import java.awt.*; @@ -20,6 +21,9 @@ public static void main(String[] args) { try { ResourceMgr.loadResource(); log.info("finish load resources"); + + new Thread(() -> new WsServer().init()).start(); + EventQueue.invokeLater(new MainFrame()); } catch (Exception e) { log.error("", e); diff --git a/gui/src/main/java/com/github/kuangcp/tank/backup/v1/MainPanelV1.java b/gui/src/main/java/com/github/kuangcp/tank/backup/v1/MainPanelV1.java deleted file mode 100644 index 6690de3d..00000000 --- a/gui/src/main/java/com/github/kuangcp/tank/backup/v1/MainPanelV1.java +++ /dev/null @@ -1,137 +0,0 @@ -package com.github.kuangcp.tank.backup.v1; - -import com.github.kuangcp.tank.constant.DirectType; -import com.github.kuangcp.tank.domain.EnemyTank; -import com.github.kuangcp.tank.domain.Hero; -import com.github.kuangcp.tank.domain.Tank; -import lombok.extern.slf4j.Slf4j; - -import javax.swing.*; -import java.awt.*; -import java.awt.event.KeyEvent; -import java.awt.event.KeyListener; -import java.util.Objects; -import java.util.Vector; - -/** - * @author Kuangcp on 2021-09-11 23:15 - */ -@Slf4j -@SuppressWarnings("serial") -class MainPanelV1 extends JPanel implements KeyListener { - - //定义我的坦克 - Hero hero = null; - //定义泛型的集合类 - Vector ets = new Vector<>(); - - //画板的构造函数 放图形的对象 - public MainPanelV1() { - } - - //重写paint - public void paint(Graphics g) { - super.paint(g); - - if (Objects.isNull(hero)) { - hero = new Hero(20, 20, 4); - } - g.fillRect(0, 0, 500, 400); - - //调用函数绘画出主坦克 - hero.drawSelf(g); - - new Tank(50, 20, 0) { - @Override - public void drawSelf(Graphics g) { - g.setColor(Color.WHITE); - super.drawSelf(g); - } - }.drawSelf(g); - new Tank(80, 20, 0) { - @Override - public void drawSelf(Graphics g) { - g.setColor(new Color(93, 217, 41)); - super.drawSelf(g); - } - }.drawSelf(g); - new Tank(110, 20, 0) { - @Override - public void drawSelf(Graphics g) { - g.setColor(new Color(34, 155, 234)); - super.drawSelf(g); - } - }.drawSelf(g); - new Tank(140, 20, 0) { - @Override - public void drawSelf(Graphics g) { - g.setColor(new Color(155, 62, 202)); - super.drawSelf(g); - } - }.drawSelf(g); - new Tank(170, 20, 0) { - @Override - public void drawSelf(Graphics g) { - g.setColor(new Color(240, 57, 23)); - super.drawSelf(g); - } - }.drawSelf(g); - - //画出子弹 - if (hero.bullet != null) { - g.draw3DRect(hero.bullet.sx, hero.bullet.sy, 1, 1, false); - } - - //画出敌人坦克 - for (EnemyTank s : ets) { - s.drawSelf(g); - } - - //画出砖块 - g.setColor(new Color(188, 112, 50)); - for (int i = 0; i < 9; i++) { - g.fill3DRect(60, 60 + 10 * i, 20, 10, false); - } - - g.setColor(Color.yellow); - g.drawRect(0, 0, 400, 300); - - } - - //当按下键盘上的键时监听者的处理函数 - public void keyPressed(KeyEvent e) { - //加了if条件后 实现了墙的限制(如果是游戏中的道具,该怎么办) - if (e.getKeyCode() == KeyEvent.VK_A) { - hero.setDirect(DirectType.LEFT); - if ((hero.getX() - 10) > 0) - hero.move(); - - } - if (e.getKeyCode() == KeyEvent.VK_D) { - hero.setDirect(DirectType.RIGHT); - if ((hero.getX() + 15) < 405) - hero.move(); - } - - if (e.getKeyCode() == KeyEvent.VK_W) { - hero.setDirect(DirectType.UP); - if ((hero.getY() - 13) > 0) - hero.move(); - - } - if (e.getKeyCode() == KeyEvent.VK_S) { - hero.setDirect(DirectType.DOWN); - if ((hero.getY() - 15) < 275) - hero.move(); - - } - //必须重新绘制窗口,不然上面的方法不能视觉上动起来 - this.repaint(); - } - - public void keyReleased(KeyEvent arg0) { - } - - public void keyTyped(KeyEvent arg0) { - } -} diff --git a/gui/src/main/java/com/github/kuangcp/tank/backup/v1/Readme.md b/gui/src/main/java/com/github/kuangcp/tank/backup/v1/Readme.md deleted file mode 100644 index ab19a45b..00000000 --- a/gui/src/main/java/com/github/kuangcp/tank/backup/v1/Readme.md +++ /dev/null @@ -1 +0,0 @@ -画出基本图形 \ No newline at end of file diff --git a/gui/src/main/java/com/github/kuangcp/tank/backup/v1/TankGameV1.java b/gui/src/main/java/com/github/kuangcp/tank/backup/v1/TankGameV1.java deleted file mode 100644 index 30ab39e9..00000000 --- a/gui/src/main/java/com/github/kuangcp/tank/backup/v1/TankGameV1.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.github.kuangcp.tank.backup.v1; - -import javax.swing.*; - -/** - * 1 绘画出坦克 并且能做到对它的移动操作 - * 2 移动最好封装成函数(面向对象思想)这是坦克的行为属性 可扩展 - * 3 加上对坦克运动的边界限定 - */ -@SuppressWarnings("serial") -public class TankGameV1 extends JFrame { - - @SuppressWarnings("unused") - public static void main(String[] args) { - TankGameV1 Tank = new TankGameV1(); - } - - //最外层JFrame的构造函数 - public TankGameV1() { - MainPanelV1 panel = new MainPanelV1(); - this.add(panel); - this.addKeyListener(panel); - - this.setLocation(900, 200); - this.setSize(405, 333); - this.setVisible(true); - this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - } -} \ No newline at end of file diff --git a/gui/src/main/java/com/github/kuangcp/tank/backup/v2/MainPanelV2.java b/gui/src/main/java/com/github/kuangcp/tank/backup/v2/MainPanelV2.java deleted file mode 100644 index 4b717d36..00000000 --- a/gui/src/main/java/com/github/kuangcp/tank/backup/v2/MainPanelV2.java +++ /dev/null @@ -1,158 +0,0 @@ -package com.github.kuangcp.tank.backup.v2; - -import com.github.kuangcp.tank.constant.DirectType; -import com.github.kuangcp.tank.domain.Bullet; -import com.github.kuangcp.tank.domain.EnemyTank; -import com.github.kuangcp.tank.domain.Hero; -import com.github.kuangcp.tank.util.TankTool; -import com.github.kuangcp.tank.util.executor.LoopEventExecutor; -import com.github.kuangcp.tank.mgr.PlayStageMgr; -import com.github.kuangcp.tank.mgr.RoundMapMgr; -import com.github.kuangcp.tank.domain.StageBorder; -import lombok.extern.slf4j.Slf4j; - -import javax.swing.*; -import java.awt.*; -import java.awt.event.KeyEvent; -import java.awt.event.KeyListener; -import java.util.Iterator; -import java.util.Objects; -import java.util.Vector; -import java.util.concurrent.TimeUnit; - -/** - * @author Kuangcp on 2021-09-11 23:15 - */ -@Slf4j -@SuppressWarnings("serial") -class MainPanelV2 extends JPanel implements KeyListener, Runnable { - - //定义我的坦克 - Hero hero; - //定义泛型的集合类 - Vector ets = new Vector<>(); - - //画板的构造函数 放图形的对象 - public MainPanelV2() { -// ets.add(new EnemyTank(250, 60, 1, DirectType.UP)); -// ets.add(new EnemyTank(250, 60, 2, DirectType.UP)); -// ets.add(new EnemyTank(250, 60, 3, DirectType.UP)); -// ets.add(new EnemyTank(250, 260, 4, DirectType.UP)); -// ets.add(new EnemyTank(250, 260, 6, DirectType.UP)); - - ets.add(new EnemyTank(250, 60, DirectType.UP)); - ets.add(new EnemyTank(250, 70, DirectType.UP)); - ets.add(new EnemyTank(250, 80, DirectType.UP)); - ets.add(new EnemyTank(250, 90, DirectType.UP)); - ets.add(new EnemyTank(250, 100, DirectType.UP)); - - for (int i = 0; i < 700; i++) { - ets.add(new EnemyTank(20 + i * 40 % RoundMapMgr.instance.border.getMaxX(), 100 + i * 5 % RoundMapMgr.instance.border.getMaxY(), DirectType.UP)); - } - - for (EnemyTank et : ets) { - LoopEventExecutor.addLoopEvent(et); - } - hero = new Hero(160, 160, 4); - } - - //重写paint - public void paint(Graphics g) { - super.paint(g); - - final StageBorder border = RoundMapMgr.instance.border; - g.fillRect(0, 0, border.getMaxX() + border.getMinX(), border.getMaxY() + border.getMinY()); - - //调用函数绘画出主坦克 - hero.drawSelf(g); - - //画出子弹 - if (hero.bullet != null) { - g.draw3DRect(hero.bullet.sx, hero.bullet.sy, 1, 1, false); - } - - //画出敌人坦克 - for (EnemyTank s : ets) { - s.drawSelf(g); - final Iterator iterator = s.bulletList.iterator(); - while (iterator.hasNext()) { - final Bullet next = iterator.next(); - if (!next.alive) { - iterator.remove(); - } - g.setColor(Color.cyan); - next.drawSelf(g); - } - } - - //画出砖块 - g.setColor(new Color(188, 112, 50)); - for (int i = 0; i < 9; i++) { - g.fill3DRect(60, 60 + 10 * i, 20, 10, false); - } - - // border - g.setColor(Color.yellow); - g.drawRect(border.getMinX(), border.getMinY(), - border.getMaxX() - border.getMinX(), border.getMaxY() - border.getMinY()); - } - - //当按下键盘上的键时监听者的处理函数 - public void keyPressed(KeyEvent e) { - //加了if条件后 实现了墙的限制(如果是游戏中的道具,该怎么办) - if (e.getKeyCode() == KeyEvent.VK_A) { - hero.setDirect(2); - if (PlayStageMgr.instance.willInBorder(hero)) - hero.move(); - - } - if (e.getKeyCode() == KeyEvent.VK_D) { - hero.setDirect(3); - if (PlayStageMgr.instance.willInBorder(hero)) - hero.move(); - } - - if (e.getKeyCode() == KeyEvent.VK_W) { - hero.setDirect(0); - if (PlayStageMgr.instance.willInBorder(hero)) - hero.move(); - } - if (e.getKeyCode() == KeyEvent.VK_S) { - hero.setDirect(1); - if (PlayStageMgr.instance.willInBorder(hero)) - hero.move(); - } - //必须重新绘制窗口,不然上面的方法不能视觉上动起来 - this.repaint(); - } - - public void keyReleased(KeyEvent arg0) { - } - - public void keyTyped(KeyEvent arg0) { - } - - @Override - public void run() { - int maxFPS = 60; - long fpsTime = (long) ((1000.0 / maxFPS) * 1_000_000); - - long now; - //每隔100ms重绘 - while (true) { - now = System.nanoTime(); - - // 进行重绘 - this.repaint(); - final long waste = System.nanoTime() - now; - if (waste > fpsTime) { - continue; - } - - TankTool.yieldTime(fpsTime - waste, TimeUnit.NANOSECONDS); - if (Objects.nonNull(hero) && !hero.isAlive()) { - break; - } - } - } -} diff --git a/gui/src/main/java/com/github/kuangcp/tank/backup/v2/Readme.md b/gui/src/main/java/com/github/kuangcp/tank/backup/v2/Readme.md deleted file mode 100644 index 2430f16e..00000000 --- a/gui/src/main/java/com/github/kuangcp/tank/backup/v2/Readme.md +++ /dev/null @@ -1 +0,0 @@ -敌人算法实验 \ No newline at end of file diff --git a/gui/src/main/java/com/github/kuangcp/tank/backup/v2/TankGameV2.java b/gui/src/main/java/com/github/kuangcp/tank/backup/v2/TankGameV2.java deleted file mode 100644 index d04fcc1a..00000000 --- a/gui/src/main/java/com/github/kuangcp/tank/backup/v2/TankGameV2.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.github.kuangcp.tank.backup.v2; - -import com.github.kuangcp.tank.domain.Hero; -import com.github.kuangcp.tank.util.executor.MonitorExecutor; -import com.github.kuangcp.tank.mgr.PlayStageMgr; -import com.github.kuangcp.tank.mgr.RoundMapMgr; -import com.github.kuangcp.tank.domain.StageBorder; - -import javax.swing.*; -import java.util.Collections; - -/** - * 1 绘画出坦克 并且能做到对它的移动操作 - * 2 移动最好封装成函数(面向对象思想)这是坦克的行为属性 可扩展 - * 3 加上对坦克运动的边界限定 - */ -@SuppressWarnings("serial") -public class TankGameV2 extends JFrame { - - @SuppressWarnings("unused") - public static void main(String[] args) { - TankGameV2 Tank = new TankGameV2(); - } - - //最外层JFrame的构造函数 - public TankGameV2() { - MonitorExecutor.init(); - RoundMapMgr.init(); - PlayStageMgr.init(new Hero(50, 20, 5), Collections.emptyList(), Collections.emptyList(), Collections.emptyList()); - - MainPanelV2 panel = new MainPanelV2(); - this.add(panel); - this.addKeyListener(panel); - final Thread panelThread = new Thread(panel); - panelThread.start(); - - - final StageBorder border = RoundMapMgr.instance.border; - - this.setLocation(200, 50); - this.setSize(border.getMaxX() + border.getMinX(), border.getMaxY() + border.getMinY() * 2); - this.setVisible(true); - - this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - } -} \ No newline at end of file diff --git a/gui/src/main/java/com/github/kuangcp/tank/ws/NioWebSocketChannelInitializer.java b/gui/src/main/java/com/github/kuangcp/tank/ws/WsChannelInitializer.java similarity index 86% rename from gui/src/main/java/com/github/kuangcp/tank/ws/NioWebSocketChannelInitializer.java rename to gui/src/main/java/com/github/kuangcp/tank/ws/WsChannelInitializer.java index b873e3d5..3fc34da0 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/ws/NioWebSocketChannelInitializer.java +++ b/gui/src/main/java/com/github/kuangcp/tank/ws/WsChannelInitializer.java @@ -15,7 +15,7 @@ /** * @author Kuangcp on 2021-05-18 08:33 */ -public class NioWebSocketChannelInitializer extends ChannelInitializer { +public class WsChannelInitializer extends ChannelInitializer { /** * @see AdaptiveRecvByteBufAllocator.HandleImpl#record(int) 实现扩缩容读写ByteBuf @@ -27,10 +27,10 @@ protected void initChannel(SocketChannel ch) { pipeline.addLast("http-codec", new HttpServerCodec());//设置解码器 pipeline.addLast("aggregator", new HttpObjectAggregator(65536));//聚合器,使用websocket会用到 pipeline.addLast("http-chunked", new ChunkedWriteHandler());//用于大数据的分区传输 - pipeline.addLast("handler", new NioWebSocketHandler());//自定义的业务handler + pipeline.addLast("handler", new WsHandler());//自定义的业务handler // checkStartsWith 为true 支持路径带参数 // maxFrameSize 设置的是最大可申请的ByteBuf,实际上使用时是按需申请和回收内存 - pipeline.addLast("", new WebSocketServerProtocolHandler(Const.webSocketPath, WEBSOCKET_PROTOCOL, true, 65536, false, true)); + pipeline.addLast("", new WebSocketServerProtocolHandler(WsConst.webSocketPath, WEBSOCKET_PROTOCOL, true, 65536, false, true)); } } diff --git a/gui/src/main/java/com/github/kuangcp/tank/ws/ChannelSupervise.java b/gui/src/main/java/com/github/kuangcp/tank/ws/WsChannelSupervise.java similarity index 97% rename from gui/src/main/java/com/github/kuangcp/tank/ws/ChannelSupervise.java rename to gui/src/main/java/com/github/kuangcp/tank/ws/WsChannelSupervise.java index 550c2a38..ddf3eccf 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/ws/ChannelSupervise.java +++ b/gui/src/main/java/com/github/kuangcp/tank/ws/WsChannelSupervise.java @@ -15,7 +15,7 @@ * @author Kuangcp on 2021-05-18 08:34 */ @Slf4j -public class ChannelSupervise { +public class WsChannelSupervise { private static final ChannelGroup GLOBAL_GROUP = new DefaultChannelGroup( GlobalEventExecutor.INSTANCE); diff --git a/gui/src/main/java/com/github/kuangcp/tank/ws/Const.java b/gui/src/main/java/com/github/kuangcp/tank/ws/WsConst.java similarity index 82% rename from gui/src/main/java/com/github/kuangcp/tank/ws/Const.java rename to gui/src/main/java/com/github/kuangcp/tank/ws/WsConst.java index b448cecb..d019bd25 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/ws/Const.java +++ b/gui/src/main/java/com/github/kuangcp/tank/ws/WsConst.java @@ -5,6 +5,6 @@ * @author Kuangcp * 2024-02-28 20:12 */ -public interface Const { +public interface WsConst { String webSocketPath = "/ws"; } diff --git a/gui/src/main/java/com/github/kuangcp/tank/ws/NioWebSocketHandler.java b/gui/src/main/java/com/github/kuangcp/tank/ws/WsHandler.java similarity index 81% rename from gui/src/main/java/com/github/kuangcp/tank/ws/NioWebSocketHandler.java rename to gui/src/main/java/com/github/kuangcp/tank/ws/WsHandler.java index 4da893d0..ff992a08 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/ws/NioWebSocketHandler.java +++ b/gui/src/main/java/com/github/kuangcp/tank/ws/WsHandler.java @@ -28,20 +28,20 @@ * @author Kuangcp on 2021-05-18 08:33 */ @Slf4j -public class NioWebSocketHandler extends SimpleChannelInboundHandler { +public class WsHandler extends SimpleChannelInboundHandler { private static final Map userMap = new ConcurrentHashMap<>(); @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { log.debug("客户端加入连接:" + ctx.channel()); - ChannelSupervise.addChannel(ctx.channel()); + WsChannelSupervise.addChannel(ctx.channel()); } @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { log.debug("客户端断开连接:" + ctx.channel()); - ChannelSupervise.removeChannel(ctx.channel()); + WsChannelSupervise.removeChannel(ctx.channel()); } @Override @@ -49,28 +49,6 @@ public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { ctx.flush(); } -// private WebSocketServerHandshaker handShaker; -// -// private void handleHttpRequest(ChannelHandlerContext ctx, FullHttpRequest req) { -// //要求Upgrade为websocket,过滤掉get/Post -// if (!req.decoderResult().isSuccess() -// || (!"websocket".equals(req.headers().get("Upgrade")))) { -// //若不是websocket方式,则创建BAD_REQUEST的req,返回给客户端 -// sendHttpResponse(ctx, req, new DefaultFullHttpResponse( -// HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST)); -// return; -// } -// -// WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory( -// "ws://localhost:7094/ws", null, false); -// handShaker = wsFactory.newHandshaker(req); -// if (handShaker == null) { -// WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel()); -// } else { -// handShaker.handshake(ctx.channel(), req); -// } -// } - /** * 拒绝不合法的请求,并返回错误信息 */ @@ -140,9 +118,9 @@ private void fullHttpRequestHandler(ChannelHandlerContext ctx, FullHttpRequest r } // 判断请求路径是否跟配置中的一致 - if (Const.webSocketPath.equals(getBasePath(uri))) { + if (WsConst.webSocketPath.equals(getBasePath(uri))) { // 因为有可能携带了参数,导致客户端一直无法返回握手包,因此在校验通过后,重置请求路径 - request.setUri(Const.webSocketPath); + request.setUri(WsConst.webSocketPath); } else { ctx.close(); } diff --git a/gui/src/main/java/com/github/kuangcp/tank/ws/Server.java b/gui/src/main/java/com/github/kuangcp/tank/ws/WsServer.java similarity index 86% rename from gui/src/main/java/com/github/kuangcp/tank/ws/Server.java rename to gui/src/main/java/com/github/kuangcp/tank/ws/WsServer.java index 87241346..45c91857 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/ws/Server.java +++ b/gui/src/main/java/com/github/kuangcp/tank/ws/WsServer.java @@ -17,11 +17,11 @@ * @author Kuangcp on 2021-05-18 08:32 */ @Slf4j -public class Server { +public class WsServer { - private void init() { + public void init() { ScheduledExecutorService pool = Executors.newScheduledThreadPool(1); - pool.scheduleAtFixedRate(ChannelSupervise::watchState, 5, 3, TimeUnit.SECONDS); + pool.scheduleAtFixedRate(WsChannelSupervise::watchState, 5, 3, TimeUnit.SECONDS); log.info("正在启动WebSocket服务器"); NioEventLoopGroup boss = new NioEventLoopGroup(); @@ -30,7 +30,7 @@ private void init() { ServerBootstrap bootstrap = new ServerBootstrap(); bootstrap.group(boss, work); bootstrap.channel(NioServerSocketChannel.class); - bootstrap.childHandler(new NioWebSocketChannelInitializer()); + bootstrap.childHandler(new WsChannelInitializer()); Channel channel = bootstrap.bind(7094).sync().channel(); log.info("WebSocket服务器启动成功:{}", channel); channel.closeFuture().sync(); @@ -44,7 +44,7 @@ private void init() { } public static void main(String[] args) { - new Server().init(); + new WsServer().init(); } } From 93fb8a76495f8d280fb10911c61ea90aae364cc6 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Sun, 7 Jul 2024 14:27:10 +0800 Subject: [PATCH 404/476] finish ws direct handle --- gui/pom.xml | 9 +++ .../com/github/kuangcp/tank/domain/Hero.java | 12 ++-- .../kuangcp/tank/panel/TankGroundPanel.java | 10 +-- ...yEventMgr.java => HoldingKeyStateMgr.java} | 69 +++++++++++++++---- .../com/github/kuangcp/tank/util/Json.java | 22 ++++++ .../kuangcp/tank/ws/WsChannelSupervise.java | 2 +- .../com/github/kuangcp/tank/ws/WsHandler.java | 19 ++++- .../github/kuangcp/tank/ws/msg/MsgPack.java | 20 ++++++ gui/src/main/resources/logback.xml | 20 +++--- gui/src/main/resources/tank/.gitignore | 2 + 10 files changed, 146 insertions(+), 39 deletions(-) rename gui/src/main/java/com/github/kuangcp/tank/util/{HoldingKeyEventMgr.java => HoldingKeyStateMgr.java} (59%) create mode 100644 gui/src/main/java/com/github/kuangcp/tank/util/Json.java create mode 100644 gui/src/main/java/com/github/kuangcp/tank/ws/msg/MsgPack.java create mode 100644 gui/src/main/resources/tank/.gitignore diff --git a/gui/pom.xml b/gui/pom.xml index bd92fd93..f086950c 100644 --- a/gui/pom.xml +++ b/gui/pom.xml @@ -32,6 +32,15 @@ org.apache.commons commons-lang3 + + + com.fasterxml.jackson.core + jackson-core + + + com.fasterxml.jackson.core + jackson-databind + diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/Hero.java b/gui/src/main/java/com/github/kuangcp/tank/domain/Hero.java index d2e7f7bf..25904693 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/domain/Hero.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/Hero.java @@ -3,7 +3,7 @@ import com.github.kuangcp.tank.constant.DirectType; import com.github.kuangcp.tank.mgr.PlayStageMgr; import com.github.kuangcp.tank.resource.ColorMgr; -import com.github.kuangcp.tank.util.HoldingKeyEventMgr; +import com.github.kuangcp.tank.util.HoldingKeyStateMgr; import com.github.kuangcp.tank.util.Roll; import com.github.kuangcp.tank.util.TankTool; import com.github.kuangcp.tank.util.executor.LoopEventExecutor; @@ -21,7 +21,7 @@ public class Hero extends Tank { //子弹集合 public Vector bulletList = new Vector<>(); - private long lastShotMs = 0; + private long lastShotTick = 0; private long shotCDMs = 268; private final int originX, originY; @@ -40,7 +40,7 @@ public Hero(int x, int y, int speed) { @Override public void run() { - final HoldingKeyEventMgr keyEvent = HoldingKeyEventMgr.instance; + final HoldingKeyStateMgr keyEvent = HoldingKeyStateMgr.instance; while (this.isAlive()) { if (keyEvent.hasPressMoveEvent()) { @@ -110,7 +110,7 @@ public void resurrect() { public void shotEnemy() { final long nowMs = System.currentTimeMillis(); - if (lastShotMs != 0 && nowMs - lastShotMs < shotCDMs) { + if (lastShotTick != 0 && nowMs - lastShotTick < shotCDMs) { return; } if (this.bulletList.size() >= this.maxLiveShot || !this.isAlive()) { @@ -139,7 +139,7 @@ public void shotEnemy() { //启动子弹线程 // shotExecutePool.execute(bullet); LoopEventExecutor.addLoopEvent(bullet); - lastShotMs = nowMs; + lastShotTick = nowMs; } public void addSpeed(int delta) { @@ -155,7 +155,7 @@ public void addPrize(int delta) { public String toString() { return "Hero{" + "bulletList=" + bulletList + - ", lastShotMs=" + lastShotMs + + ", lastShotTick=" + lastShotTick + ", shotCDMs=" + shotCDMs + ", x=" + x + ", y=" + y + diff --git a/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java b/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java index ba2c272b..85a80b2c 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java +++ b/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java @@ -14,7 +14,7 @@ import com.github.kuangcp.tank.resource.AvatarImgMgr; import com.github.kuangcp.tank.resource.ColorMgr; import com.github.kuangcp.tank.util.ExecutePool; -import com.github.kuangcp.tank.util.HoldingKeyEventMgr; +import com.github.kuangcp.tank.util.HoldingKeyStateMgr; import com.github.kuangcp.tank.util.TankTool; import com.github.kuangcp.tank.util.executor.AbstractDelayEvent; import com.github.kuangcp.tank.util.executor.DelayExecutor; @@ -332,7 +332,7 @@ public void run() { @Override public void keyPressed(KeyEvent e) { // 启动关闭流程 - if (e.getKeyCode() == KeyEvent.VK_Q && HoldingKeyEventMgr.instance.isCtrl()) { + if (e.getKeyCode() == KeyEvent.VK_Q && HoldingKeyStateMgr.instance.isCtrl()) { System.exit(0); } else if (e.getKeyCode() == KeyEvent.VK_T && !invokeNewStage) { invokeNewStage = true; @@ -351,16 +351,16 @@ public void keyPressed(KeyEvent e) { } // 实际用户交互 if (e.getKeyCode() == KeyEvent.VK_J) { - HoldingKeyEventMgr.instance.setShot(true); + HoldingKeyStateMgr.instance.setShot(true); } - HoldingKeyEventMgr.instance.handleDirectPress(e); + HoldingKeyStateMgr.instance.handleDirectPress(e); this.repaint(); } @Override public void keyReleased(KeyEvent re) { - HoldingKeyEventMgr.instance.handleRelease(re); + HoldingKeyStateMgr.instance.handleRelease(re); if (re.getKeyCode() == KeyEvent.VK_T && invokeNewStage) { invokeNewStage = false; diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/HoldingKeyEventMgr.java b/gui/src/main/java/com/github/kuangcp/tank/util/HoldingKeyStateMgr.java similarity index 59% rename from gui/src/main/java/com/github/kuangcp/tank/util/HoldingKeyEventMgr.java rename to gui/src/main/java/com/github/kuangcp/tank/util/HoldingKeyStateMgr.java index edc64b78..377afb42 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/util/HoldingKeyEventMgr.java +++ b/gui/src/main/java/com/github/kuangcp/tank/util/HoldingKeyStateMgr.java @@ -4,21 +4,25 @@ import com.github.kuangcp.tank.panel.TankGroundPanel; import lombok.Getter; import lombok.Setter; +import lombok.extern.slf4j.Slf4j; import java.awt.event.KeyEvent; import java.util.HashMap; import java.util.Map; +import java.util.Objects; import java.util.Optional; /** * @author Kuangcp on 2021-09-06 02:58 - * @see TankGroundPanel#keyPressed + * @see TankGroundPanel#keyPressed 触发 */ @Getter @Setter -public class HoldingKeyEventMgr { +@Slf4j +public class HoldingKeyStateMgr { - public static HoldingKeyEventMgr instance = new HoldingKeyEventMgr(); + private long lastEventTick = 0; + public static final HoldingKeyStateMgr instance = new HoldingKeyStateMgr(); // 允许多键同时被触发 private volatile boolean up; @@ -33,7 +37,7 @@ public class HoldingKeyEventMgr { private final Map releaseMap = new HashMap<>(); private final Map pressMap = new HashMap<>(); - public HoldingKeyEventMgr() { + public HoldingKeyStateMgr() { releaseMap.put(KeyEvent.VK_A, () -> this.left = false); releaseMap.put(KeyEvent.VK_D, () -> this.right = false); @@ -45,28 +49,63 @@ public HoldingKeyEventMgr() { pressMap.put(KeyEvent.VK_S, () -> this.down = true); pressMap.put(KeyEvent.VK_W, () -> this.up = true); -// releaseMap.put(KeyEvent.VK_A, () -> this.direct = -1); -// releaseMap.put(KeyEvent.VK_D, () -> this.direct = -1); -// releaseMap.put(KeyEvent.VK_S, () -> this.direct = -1); -// releaseMap.put(KeyEvent.VK_W, () -> this.direct = -1); releaseMap.put(KeyEvent.VK_J, () -> this.shot = false); releaseMap.put(KeyEvent.VK_CONTROL, () -> this.ctrl = false); -// pressMap.put(KeyEvent.VK_A, () -> this.direct = DirectType.LEFT); -// pressMap.put(KeyEvent.VK_D, () -> this.direct = DirectType.RIGHT); -// pressMap.put(KeyEvent.VK_S, () -> this.direct = DirectType.DOWN); -// pressMap.put(KeyEvent.VK_W, () -> this.direct = DirectType.UP); - pressMap.put(KeyEvent.VK_CONTROL, () -> this.ctrl = true); } public void handleDirectPress(KeyEvent re) { - Optional.ofNullable(pressMap.get(re.getKeyCode())).ifPresent(Runnable::run); + handleDirectPress(re.getKeyCode()); } public void handleRelease(KeyEvent re) { - Optional.ofNullable(releaseMap.get(re.getKeyCode())).ifPresent(Runnable::run); + handleRelease(re.getKeyCode()); + } + + public void handleDirectPress(Integer code) { + Optional.ofNullable(pressMap.get(code)).ifPresent(Runnable::run); + } + + public void handleRelease(Integer code) { + Optional.ofNullable(releaseMap.get(code)).ifPresent(Runnable::run); + } + + // 前端 ws 采样定时调用 + public void handleJoy(Integer code) { + if (Objects.isNull(code)) { + return; + } + final long now = System.currentTimeMillis(); +// if (lastEventTick != 0 && now - lastEventTick < 100) { +// return; +// } + lastEventTick = now; + this.left = false; + this.right = false; + this.up = false; + this.down = false; + + if (code == DirectType.NONE) { + return; + } + + switch (code) { + case DirectType.UP: + this.up = true; + break; + case DirectType.DOWN: + this.down = true; + break; + case DirectType.LEFT: + this.left = true; + break; + case DirectType.RIGHT: + this.right = true; + break; + } +// log.info("code={} left={} right={}", code, this.left, this.right); } public boolean hasPressMoveEvent() { diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/Json.java b/gui/src/main/java/com/github/kuangcp/tank/util/Json.java new file mode 100644 index 00000000..3ce711d4 --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/util/Json.java @@ -0,0 +1,22 @@ +package com.github.kuangcp.tank.util; + +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.SneakyThrows; + +/** + * @author Kuangcp on 2024-07-07 11:50 + */ +public class Json { + + private static final ObjectMapper mapper = new ObjectMapper(); + + @SneakyThrows + public static String toJson(Object obj) { + return mapper.writeValueAsString(obj); + } + + @SneakyThrows + public static T fromJson(String json, Class clazz) { + return mapper.readValue(json, clazz); + } +} diff --git a/gui/src/main/java/com/github/kuangcp/tank/ws/WsChannelSupervise.java b/gui/src/main/java/com/github/kuangcp/tank/ws/WsChannelSupervise.java index ddf3eccf..39296cc4 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/ws/WsChannelSupervise.java +++ b/gui/src/main/java/com/github/kuangcp/tank/ws/WsChannelSupervise.java @@ -46,6 +46,6 @@ private static void printState() { } public static void watchState() { - log.info("online {}", CHANNEL_MAP.size()); +// log.info("online {}", CHANNEL_MAP.size()); } } diff --git a/gui/src/main/java/com/github/kuangcp/tank/ws/WsHandler.java b/gui/src/main/java/com/github/kuangcp/tank/ws/WsHandler.java index ff992a08..969ba94b 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/ws/WsHandler.java +++ b/gui/src/main/java/com/github/kuangcp/tank/ws/WsHandler.java @@ -1,6 +1,10 @@ package com.github.kuangcp.tank.ws; +import com.github.kuangcp.tank.mgr.PlayStageMgr; +import com.github.kuangcp.tank.util.HoldingKeyStateMgr; +import com.github.kuangcp.tank.util.Json; +import com.github.kuangcp.tank.ws.msg.MsgPack; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; @@ -18,6 +22,7 @@ import io.netty.handler.codec.http.websocketx.WebSocketFrame; import io.netty.util.CharsetUtil; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.StringUtils; import java.util.HashMap; @@ -34,7 +39,7 @@ public class WsHandler extends SimpleChannelInboundHandler { @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { - log.debug("客户端加入连接:" + ctx.channel()); + log.info("客户端加入连接:{}", ctx.channel()); WsChannelSupervise.addChannel(ctx.channel()); } @@ -126,7 +131,6 @@ private void fullHttpRequestHandler(ChannelHandlerContext ctx, FullHttpRequest r } } - @Override protected void channelRead0(ChannelHandlerContext ctx, WebSocketFrame frame) { if (frame instanceof PingWebSocketFrame) { @@ -165,7 +169,16 @@ private void closeWebSocketFrameHandler(ChannelHandlerContext ctx, CloseWebSocke * @param frame */ private void textWebSocketFrameHandler(ChannelHandlerContext ctx, TextWebSocketFrame frame) { - ctx.channel().writeAndFlush(frame.retain()); + final String text = frame.text(); + final MsgPack pack = Json.fromJson(text, MsgPack.class); + + HoldingKeyStateMgr.instance.handleJoy(pack.getDirect()); + if (BooleanUtils.isTrue(pack.getShot())) { + PlayStageMgr.instance.hero.shotEnemy(); + } + +// ctx.channel().writeAndFlush(frame.retain()); + ctx.channel().writeAndFlush("OK"); } /** diff --git a/gui/src/main/java/com/github/kuangcp/tank/ws/msg/MsgPack.java b/gui/src/main/java/com/github/kuangcp/tank/ws/msg/MsgPack.java new file mode 100644 index 00000000..915f4594 --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/ws/msg/MsgPack.java @@ -0,0 +1,20 @@ +package com.github.kuangcp.tank.ws.msg; + +import com.fasterxml.jackson.annotation.JsonAlias; +import lombok.Data; + +/** + * @author Kuangcp on 2024-07-07 11:45 + */ +@Data +public class MsgPack { + + /** + * @see com.github.kuangcp.tank.constant.DirectType + */ + @JsonAlias("d") + private Integer direct; + + @JsonAlias("s") + private Boolean shot; +} diff --git a/gui/src/main/resources/logback.xml b/gui/src/main/resources/logback.xml index 78cce9e9..85a9032c 100644 --- a/gui/src/main/resources/logback.xml +++ b/gui/src/main/resources/logback.xml @@ -6,11 +6,13 @@ - %d{HH:mm:ss.SSS} %highlight(%-5level)[%-16thread] %-32logger{32}%highlight(%15M):%yellow(%-3L) %msg%n + + %d{HH:mm:ss.SSS} %highlight(%-5level)[%-16thread] %-32logger{32}%highlight(%15M):%yellow(%-3L) %msg%n + - - DEBUG - + + + @@ -23,13 +25,13 @@ %d{MM-dd HH:mm:ss.SSS} |-%-5level |%thread| %logger{36}:%L - %msg%n - - DEBUG - + + + - + - + \ No newline at end of file diff --git a/gui/src/main/resources/tank/.gitignore b/gui/src/main/resources/tank/.gitignore new file mode 100644 index 00000000..73f74e5f --- /dev/null +++ b/gui/src/main/resources/tank/.gitignore @@ -0,0 +1,2 @@ +static/ +img/ \ No newline at end of file From 8b578b68a835aff2f409445c4d465f64380828bb Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Sun, 7 Jul 2024 14:31:08 +0800 Subject: [PATCH 405/476] client --- gui/src/main/resources/tank/.gitignore | 3 +- gui/src/main/resources/tank/Readme.md | 1 + .../resources/tank/client/assets/dark.png | Bin 0 -> 210 bytes gui/src/main/resources/tank/client/index.html | 27 +++ gui/src/main/resources/tank/client/main.js | 177 ++++++++++++++++++ 5 files changed, 207 insertions(+), 1 deletion(-) create mode 100644 gui/src/main/resources/tank/Readme.md create mode 100644 gui/src/main/resources/tank/client/assets/dark.png create mode 100644 gui/src/main/resources/tank/client/index.html create mode 100644 gui/src/main/resources/tank/client/main.js diff --git a/gui/src/main/resources/tank/.gitignore b/gui/src/main/resources/tank/.gitignore index 73f74e5f..2cb6018a 100644 --- a/gui/src/main/resources/tank/.gitignore +++ b/gui/src/main/resources/tank/.gitignore @@ -1,2 +1,3 @@ static/ -img/ \ No newline at end of file +img/ +client/ diff --git a/gui/src/main/resources/tank/Readme.md b/gui/src/main/resources/tank/Readme.md new file mode 100644 index 00000000..5b894750 --- /dev/null +++ b/gui/src/main/resources/tank/Readme.md @@ -0,0 +1 @@ +客户端 https://www.phaser-china.com/example-detail-628.html diff --git a/gui/src/main/resources/tank/client/assets/dark.png b/gui/src/main/resources/tank/client/assets/dark.png new file mode 100644 index 0000000000000000000000000000000000000000..6e53eb06a36d4bdbcd46ab647afcf9f80b05a694 GIT binary patch literal 210 zcmeAS@N?(olHy`uVBq!ia0vp^H-LC82NRHtU8{BiNO2Z;L>4nJ@Vy3M#=^6WQ-Ojl yo-U3d6?5JmG-L$w4lVfVpUYOi0LY{pXgJNVZ3zQog`A=a$WBjJKbLh*2~7aOHXD8b literal 0 HcmV?d00001 diff --git a/gui/src/main/resources/tank/client/index.html b/gui/src/main/resources/tank/client/index.html new file mode 100644 index 00000000..777ae55e --- /dev/null +++ b/gui/src/main/resources/tank/client/index.html @@ -0,0 +1,27 @@ + + + + + + + + + + phaser example + + + + + + +

+ + + + \ No newline at end of file diff --git a/gui/src/main/resources/tank/client/main.js b/gui/src/main/resources/tank/client/main.js new file mode 100644 index 00000000..3dd8d7a1 --- /dev/null +++ b/gui/src/main/resources/tank/client/main.js @@ -0,0 +1,177 @@ +/** +* @author Richard Davey +* @copyright 2015 Photon Storm Ltd. +* @license {@link http://choosealicense.com/licenses/no-license/|No License} +* +* @description This example requires the Phaser Virtual Joystick Plugin to run. +* For more details please see http://phaser.io/shop/plugins/virtualjoystick +*/ + +var game = new Phaser.Game(1190, 590, Phaser.AUTO, 'phaser-example'); + +function connected() { + let ws = new WebSocket("ws://192.168.1.102:7094/ws?userId=120"); + ws.onopen = function () { + console.log('connected'); + }; + ws.onmessage = function (evt) { + console.log(evt.data) + }; + + ws.onclose = function (evt) { + console.log("error"); + }; + + ws.onerror = function (evt) { + console.log("error"); + }; + + return ws; +} +var ws = connected(); + +function toggleFullScreen() { + var docElm = document.documentElement; + if (docElm.requestFullscreen) { + docElm.requestFullscreen(); + } else if (docElm.msRequestFullscreen) { + docElm.msRequestFullscreen(); + } else if (docElm.mozRequestFullScreen) { + docElm.mozRequestFullScreen(); + } else if (docElm.webkitRequestFullScreen) { + docElm.webkitRequestFullScreen(); + } + } + +// toggleFullScreen() + +var PhaserGame = function () { + + this.background = null; + this.player = null; + this.speed = 300; + + this.weapon1; + this.weapon2; + this.weapon3; + + this.pad; + + this.stick; + + this.buttonA; + this.buttonB; + this.buttonC; + +}; + +PhaserGame.prototype = { + + init: function () { + this.game.renderer.renderSession.roundPixels = true; + this.physics.startSystem(Phaser.Physics.ARCADE); + + }, + + preload: function () { + + this.load.atlas('arcade', 'assets/arcade-joystick.png', 'assets/arcade-joystick.json'); + this.load.image('background', 'assets/back.png'); + this.load.image('player', 'assets/dark.png'); + this.load.image('bullet2', 'assets/bullet2.png'); + this.load.image('bullet9', 'assets/bullet9.png'); + this.load.image('bullet10', 'assets/bullet10.png'); + + }, + + create: function () { + + // this.background = this.add.tileSprite(0, 0, this.game.width, this.game.height, 'background'); + // this.background.autoScroll(-40, 0); + + this.player = this.add.sprite(64, 200, 'player'); + + this.physics.arcade.enable(this.player); + + this.player.body.collideWorldBounds = true; + + this.pad = this.game.plugins.add(Phaser.VirtualJoystick); + + this.stick = this.pad.addStick(150, 430, 100, 'arcade'); + this.stick.deadZone = 0; + this.stick.scale = 0.85; + + this.buttonA = this.pad.addButton(650, 420, 'arcade', 'button1-up', 'button1-down'); + this.buttonA.onDown.add(this.fireBullet, this); + this.buttonA.scale = 0.9; + this.buttonA.repeatRate = 100; + this.buttonA.addKey(Phaser.Keyboard.CONTROL); + + this.buttonB = this.pad.addButton(790, 320, 'arcade', 'button2-up', 'button2-down'); + this.buttonB.onDown.add(this.fireRocket, this); + this.buttonB.scale = 0.9; + this.buttonB.repeatRate = 500; + this.buttonB.addKey(Phaser.Keyboard.Z); + + this.buttonC = this.pad.addButton(920, 420, 'arcade', 'button3-up', 'button3-down'); + this.buttonC.onDown.add(this.fireSpreadShot, this); + this.buttonC.scale = 0.9; + this.buttonC.addKey(Phaser.Keyboard.SPACEBAR); + }, + + fireBullet: function () { + // toggleFullScreen() + // ws = connected(); + // this.weapon1.fire(this.player); + + }, + + fireRocket: function () { + + // this.weapon2.fire(this.player); + + }, + + fireSpreadShot: function () { + + // this.weapon3.fire(this.player); + let x = { + s: true + } + ws.send(JSON.stringify(x)) + }, + + update: function () { + + let key = { + d: -1 + } + if (this.stick.isDown) { + // console.log(this.stick.rotation, this.stick.force); + + // this.physics.arcade.velocityFromRotation(this.stick.rotation, this.stick.force * maxSpeed, this.player.body.velocity); + + if (this.stick.rotation > -2.2 && this.stick.rotation < -0.85) { + key.d = 0 + } + else if (this.stick.rotation > 0.85 && this.stick.rotation < 2.2) { + key.d = 1 + } + else if (this.stick.rotation <= -2.2 || this.stick.rotation >= 2.2) { + key.d = 2 + } + else if (this.stick.rotation >= -0.85 && this.stick.rotation <= 0.85) { + key.d = 3 + } + // console.log(key) + } + else { + // this.player.body.velocity.set(0); + } + + ws.send(JSON.stringify(key)) + } + +}; + +game.state.add('Game', PhaserGame, true); From c885701121e2c7f984575382f5301d4987997bd8 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Sun, 7 Jul 2024 14:31:49 +0800 Subject: [PATCH 406/476] clean --- gui/src/main/resources/tank/.gitignore | 2 ++ gui/src/main/resources/tank/static/index.html | 19 ------------------- 2 files changed, 2 insertions(+), 19 deletions(-) delete mode 100644 gui/src/main/resources/tank/static/index.html diff --git a/gui/src/main/resources/tank/.gitignore b/gui/src/main/resources/tank/.gitignore index 2cb6018a..49de40ee 100644 --- a/gui/src/main/resources/tank/.gitignore +++ b/gui/src/main/resources/tank/.gitignore @@ -1,3 +1,5 @@ static/ img/ client/ +backup/ + diff --git a/gui/src/main/resources/tank/static/index.html b/gui/src/main/resources/tank/static/index.html deleted file mode 100644 index 4444408d..00000000 --- a/gui/src/main/resources/tank/static/index.html +++ /dev/null @@ -1,19 +0,0 @@ - - - - - Tank - - - - - - - - -
- - - - - \ No newline at end of file From d259e35727e8be0aa455613fa7753a34837ff014 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Sun, 7 Jul 2024 17:54:04 +0800 Subject: [PATCH 407/476] doc --- gui/src/doc/tank/main.png | Bin 0 -> 29578 bytes gui/src/doc/tank/phone.png | Bin 0 -> 64365 bytes .../java/com/github/kuangcp/tank/Readme.md | 11 +- .../github/kuangcp/tank/domain/EnemyTank.java | 32 ++-- gui/src/main/resources/tank/client/index.html | 7 +- gui/src/main/resources/tank/client/main.js | 138 ++++++++++++------ netty/src/main/java/netty/Readme.md | 4 + 7 files changed, 124 insertions(+), 68 deletions(-) create mode 100644 gui/src/doc/tank/main.png create mode 100644 gui/src/doc/tank/phone.png create mode 100644 netty/src/main/java/netty/Readme.md diff --git a/gui/src/doc/tank/main.png b/gui/src/doc/tank/main.png new file mode 100644 index 0000000000000000000000000000000000000000..cb584e0a6da11aa661372d0bcaf4b9dcac7ff3ce GIT binary patch literal 29578 zcmd42byU<{+b=$f3Wzj<0*VYR%}7ZprNDr6cMQ@cjeycE&5(-HB@NDy(y4SfLxbed z9p^jV_x(Q4Ip;mkI_tN7f1Fus)@F9^&y{;~U17>fF9>f_-v)s|gmN#XRY4$}91!Sw z72b8A=1t?BG@!tDe5vCC0uilazt^1l-z9=Tk3e$L&(%HBH&Akpv^s9gYc2z>G0o8T zYGjY%;fXGqW1Z9tuUri~FE|0G&F93vaBsT<0e!JbF7GtM zm#@4=0rE+%b-7EBmR}R=kfNOTM<*AbqgC)lzKyK z=CKkE0NW!>>rXaPxO}RKxd^$ zy}I3HD-2GgFwBfO+|P}IndiP5Pya5*;2w71hQ#?vnnS}ukCtJEggX~^xF!N+UHuj> z`hi;UMtl9XlauLDdy52K6B!8P`0IoFoTJ7=Ug~h9#ch~d9BvsD;!@c=UfNKFJRjN} z8meA`pVWUev9XEQ@ER9Ol=4S?aK8qcjeLd<`o1%uf;xW3tE16sV=l98&8%dPJY73! zZ(oJ4o&=pNkEjdwZvCoPKzH5obT58{xejW+{|-qIvhb;^7blB}FWb1VQ2f|F$pGQ| z7UssR7{3aKudc4TAuiHp&NW?Lg(F1_|Fsz5N~|HeM7J(Hlu@Z)w*)wp+kip+E?MQ9 zrznIw=CZfUH^;O2c&qL?`X(2?e+0Xd&L1rsEA4W_vq3ZbN;a z(>E9KF%n!A>4ai>0I0KX021uoyNzgQM9LSpwMKjFt)6z$`BokN{#n1bA}An`Sy~d* z%EGAaYzpOZaR}oFf&44|U({tf`jDb98;q1hB_XJVD%mef88&S&n01av48)~1I_#$( z)vR&At2ht6>mZ)T_hy1tzFMkC>WkIgsgb1~rz<^8;&sutx{h%L5mj4uJUC~RB?N&C zLAzX{ML3Tgg$MK@U@$Kqm`{L@dHLO>9ccTy^84!`N9itvW8FpEmhs!`@hZ?-O(j!y zZ$T1BIhkR}HA7l3H>g%)F3ii8(AV+1lUI|G7TMf59JUT=U<$?R2B|NQy&yr=ez|%DiUImxxiPv~(RiGM_JXTz-MzQQ)am4IG>aG%8ddcTe1v}J>{p#+h_+amz-8i^ zQ>7$M>_upAxY<>E79R{g+7!(`rXRR}t+|rM0xfn7r5zYnEd73l(bcHdYY0}u9GrQy z+B@{!YtrHon2%%MX)%_D*^bT@lkvvmxol@fNvHG$LbbyrGu`{I6HuXX38mZm!1Haw zq^72nCM@6%$v#-|VzKUWd*s4GR9H}?cR?Ko+nU5>imt^1Si}8m8aWh% zcU~V!UTv92LhpbAcp3>9YDA{y@8S+bim~7B3HH(C{|pl_apw3U!&*^{$Z(w&UzA-_ zR7~vaf=X*kYfsd-<;cZ-V|0~!%ym~T{X+!=4B6ydRaCz#cBiqw*sTy5eNZN}9HUr7 z1UvHG`t2d)=!E$i`{tT4$u1YDnb(Yufmu}p1_Lvbn_%8%R@VxS?;$temM-_cn>-E? zj^3+qk&DZf`_5z)Cot5Zo~l$|)om&(3FGKsY|CCsP%$g&a13KU;lRUC^_M##?ZV<`E=NJA-N!1%FoPHRSZNcv1kDD|0(3b7#o!` z@gz|B7>U}f^7+Yho5MU8JocKgUv@1W0#<{lK_Jy1Td2eb2`~W#_wOs{|6)q9?m{l3 ze%OVGrVW31vZ9@tkt9i#CxC%7s%B zS#wGAT$xzcsgmsJnh%hmiA)YflzJt-WLt-3G(H4Ez`|%XpBgoS%Nd@{>VpT%)hKqc z{=_;mmHXUsIbS#)55JyoR>}UDCVVAe>5hh5E!ya;#u)^9hJN4@qLiCid@XI~h@*1c zDee2EdhgP}EkC&wIq!9r6zf>`>5~2r?p@a_c?(=du*VC?ryYw5!h=fpf#d1|)re$e zVi!GC7SU(3{3$B>eXV^|o~+K?rEr0jmt?C~9;GkH7JW^)h(~vwC60(j&#!|>=mBd* zn?X2`G!<6V?$-?+O`{aYETf{~uV^bnxhvX)SvaBtsL3PnAn}KmMK>$8yVmvjAn}2@ zl=XwV9NgjAcoqbBFjKY4k3H7r9bk3NW6j*|S16F%!@t8hD9hpA(!2oVsXD1iF_4Np&Z7+-JRooAx#O z2qgXH81jAl43{g}lZTm%8LUPq^K-STcCT2GGIaFG2W&l#8 ztl~jgV~JDR?dxDAP5u5BncQ#{UJzwV=g2fTcz^G(?`+2KYq~DVBCL9 zo1z^W_%h~XLWvqu|Ky-?`x9O+mn)d8FF`S&_G59VqZ% zqu`RUmO6-OtAfF<>ng^%ZS}ILxl!tEiAJ>X_GRPWquZTvsaQ)-?Kk6>*2s2?fBblz ztJV+Kagy2g1Vph#Jyb?ox{D_0a>_rlcI*XXeJl;RD8VpySU z8~D2*J}6!1FKPw9;s=va3TSXv6tC}q3(D6`Yu<=uV!Lv~ZWb(;;hP}I)%ii#y_{I6~;1;Uk%<Yy^nnLUKK)G)EzKX*nNQtPsG<{PLmduYefuNpNqg(s8TS9vdPeox{&^omry-; z8U4|s{w42`CT{dq{t5V~QJIjJ7SZu$DBVsVPfox7SCGcKVM0XYelpd=^%)Rz@;A-e zyBx^#M6H#9L52B>>^hIt`Cjgsx;H1zc_>4_Z)hv`uC6L%d0p$_af}rpPHh} z!@LCUGUJ;UcE~&`la`j-$ZRieWdtiR%8@yR)dk|-c@UxTqSfM}l<+E89h2cSNqCjek#dOev_XbGBFB$V(NUX~wl#i$;=9()^esg?ut)QD zN2>-W2j+bEY7Alj)pzg)Gp`wg+x*KgIr7{OwTR7rdm9Fd%|M@?r*?D5I^p?X9n&3~ zc`!M}xQrU%^MJxs9pwXh4?0O!337J{uB0M!CyU!gi)e3%t0ZiLdN^v?*2PQUkl!q6 zVwrI=H}KKex|mcU}yg z*xDNiUrt40T2hRX8tJO5JB~kuXaT{U{}fU;m}9On!Nez^K}tzVAkRx*pvo~ceej-K z~y&aw0pkZ%Lh`bOo}RX!5~^l)Q_k zr|>3g9Je_7?isx4-W{=x&mj+w4z96Qa8(OnA%JB@nm!09hzmL{CyfwFX0(c9F2F;y z)sJ|p&0Qim!MHZ$??3tG13+BErZ4at*>pp~JBU z$NSMH=MPQjVWY;Bq3@l4o0r(5`Fe)hhMw&uAR&;eEroHe{yv=_c_k%ho0}o1vG0d& z<5i!hcs1tV7e6AfrazpuAc8)R$WC)lAz#st3w4RBTULiS6}S*COGZJ%CP+zR<0c`Z zpCb2=0@1zrd2)s3>#xTA4qi3M8?1Gq>YU6T+bw%GmNz!;ylEJ<4kBq|!Wll64pr}e z^3jJ11YSffkoPV~r<35r@d@N?#m&J(MVae_OGM)AE)D32apt+r6nqEzz>hGcFK~ff zQ9-qXpahro5R6)pcxksCuRsbs<2c)xj9ya%Mnr#uaDeIEh`Nk)@7=yoo%ydw_$|7G z&``O+kdPVgoN^IdeD`3~I~R(_(uFr;wR)y?@>r)@zHRuB$d7U{tqO7*y!H0oah@`# z(`}=PT9!@m)=jqwF=ZGWdy48Wogp%rZWTD8R7vI#yq()&9xc~a6f7Fkxv}@5PQ8IT zXJbA-B0+-;_dHdx%b$ftgp%-iKP*)Zfa7v0%lBaaXT0Ey@A|L$J(<|D;^ND>J??KM zltp0??Iz6XHC93dF%=iGc1LMSAjc$n%){iz?baN+q2xswF1h88q6&Y0zD#sqM0I<} zJ#|S5)1FUD+J(S&HVQ}z?x*qgX0oW%Yds1Vaj&Y=FKCI4bu4a+8vV+__qEktIGxSH z&c_y&GkP}AK(Orj+o;08L@>B_;!1wvm;%h0fdQKyq`YoeXm@ka~MA`9AbtCD!- z66$e`PQEI&(@gv;J-2bG`>6CaPgS`wVa%(pUeiZ<0qM)0^tE%$*|8?}93vSY?W>+0LMT)zvmM4xxSWYaSGnOFpMR#Axs( z*L_@mOZqZ&A-oOB=sm(q`(#3-v5&)pTPa;(BDDWWpc3Mu8zV7L0o6$Bhaq9}>FNsZ zjt;6>u_QF*)Ey$%K%PevjwkOQYbt&CMDjkYO-8&v_1?p*46~iBAH5WrpF5TDKH&*# zNg)dj=LM{$UYh09-1!w-e)%aiAyl>1EFqT-ev`1LSp19CQxX2^hK41;p*`s%s3c#} z=1{VXctC8Ld_761peS{;<}6ad0R@>nh>!ACYwHRu{_VCmCSU8?a^mXeoVhJTjN(VGT+ohqup9Wh}6s@7%|Yn1~LAt3LAm9 z^t!B`78>)4r;<^vLNCI?)Nd2x z)^&OMliwqI8uRliu3Rn3ol{??EG(3=hG%zCadWv<*bDYjM+G0%6SovqID(Y|ncS?S zMkmUr=C;o)V!9NTSU<-GgV)_ere$g~KIBruy11C5$~g7CN`$@Nz4VdQymym)wDqFtG~$Z+Fk-!2nnHt5 zk@d!hl#!Arw?6u%P2SE zvvH@ox<2Pi+)I<^rs$}J#!+K1Ejj5*pcEeUdulmln6uK*6VLUM+5sc&jpfM+AZ!b-{ zI1FY^A71`EJZu;{L=7Bo^qGykw`W7Qtp}T~Tfm$x*zRY9;nEq3fQW*S-ujeORB6L%=rjAIn}jV-#32#nEMcic zTDZI0x6l)WSKp5|KX9!juv+H}tk=@LH_NBSJ(jL&%OtNiRX>~ka`Nnyg_h|>bJdg4c_P(*^hEr;d(-&*3yO)9@ZYC z2pyrtk8oLh*oYpT}I_~F)0OeC&h$m|CXT*&B3(q*qlX?=DfUKv`oGJR<*ELgU`b0(Ix8YuqUky z<+nZC%iVOoxDb<7T2igINunM$Tvt)0yMa%lzEG7vOdYqHm_8N^ho>r}e`Tog*h)(o z-Mz4(N3O@NchRcoGSOZ?$tPm3`5e(ieW8(C;%B4IBmmPL=!|5d^Y~0?Imx=W z)lBxn+vus&V9kqIi^(7Ys>Zzw%-vi%-{ZLjmRI4k87|Nc3S?r0A(6OVCn3XH$sPqw z-uL5B!Yq!%UluhVWmu6`e51!>M@_x0F zIjqr0Qw_iIge|ZA5?yM1RF-Z14o=40$=F5~$(CUA0D=o9Y*>h`bIvM|+c=PfIte_C zKQV@F(5At)_}RU;DiRb3I*kEQ5pa~!XzzK^H2SL5IHt}}OqP3H-F@;0D zv%y^a3}(T^8Vr?gfzA(Q^iiK;u6+>=kuDEK_4%--OyCnJ{2<{LXb04sJmTnI2oa z#w#{fWnMV~AL}+mtHPoa(Q6;Z;T%FJ9O$OeADmaMIddkes9K{{-R&lS9 zT2AB9;Rgyu3p1LWC|iJkY$hhAw2_V`_P*#8vu8XI%E^g|iC@2b_P3pSluj4S0%jW4 zmPmS~7FKtKMEcbyQq-%vzD;Kw!qDg^oGGr~=D4!bE(Rq|Ur8LF3UXyhWLfIas|#7H zu%?Wr$kB?hgneZ&uf zY1!y6@Gp?cN`gMJy?Tv7ri;AzIlmBMH``5jP4gIU+h4>>GW+Aj^x4)FY)Xud1MS3c z;l>^kl{m)obEyDkx%cDbLcJf{jeb4%OwLbpjk2Bl)q2!;)v&ZazDz~Q=!)V1-c>v^ zS>zs<%S`?_v)j5bIN%Xk{_Jl_C3TkuU%|&ETN*y7$Z92E=y%q?yiXx)sI4v3ROexD zV&Y}(WoK8?fVwU7p~8j?QhA86xOcNu2}Z60ZBP~WO5n(cd0sBArYabiSzGVS2TgDU z!SnL+=)}DCjSnsXZp}%*vy5_%^mrtg$a%A*P~Z^*0l0$_IapIG7_JoIqqG(V5RG8w z@=)+{@M=NAZ&Po^P~XtnP}^4joAJsrB8rhQsx_Z=gqJ4b@xh$8&Pwmta=d77SQJ@Y!53Z#WPA{zo{aQX#wUr{@r~(fMvWKg zv0~Kt3DBM8aTN?{lg43ts~fz{LeDR-zJ|ox)BOku-}34P&jt3ffvhwme^1ybWy)3iG5`C_i&!O5pD55u>fgD7>R}>ri(eH!`~|9yI;><_t%QR$46Ls zx#7n51be1%rzW|E2^!erwXskvz8r+%aM_GMu%so)IEFvVo*QZK`nTi>{t+$3S&x}e=D!ah=o#tIFHC>M9*l-IjFMGg( z2QwV>zwsUIvM0}YyRV|k0I}Iz8b=FV4QXU#pec2^zdfjklq1(9C1WNfGjTSzm8TEX z?k+N|KWRm#hQ~e~C)M3)&o~^5FHR%%c79AKmj_#3)@i$y>FRBvhVypZ_E(W!%<;W~ z@sDM2vilK?VGP+M?~l-Z+Cp-1vY+SD)d%CzLfWuYg?B4Ed$81&1 z$2NrfAlzY0$>z93vZYm-?u>e;oAs2^OlAd7dlO) z7T^=$hlLhe`A{h`a&WoYQqqyBy3uQvF@w!~-M`ZxdWb%{CK2MbXfW;Kl4V2d(XetH zi|Z3IVci$nM|DDFJ>0@ZFQ>$kFfX^bA(TRY70MmH3HQscJZrw;fTE=06-+-+22|MHG6ezwL z4%+LkW?w;+^*K0Lyc(;~!qjR#e4|PChVEBhHQ}`FQi)QmCl!Z={DLxP0cpY4@N&5G zos%AX@=7u`9pljo!J8U3xAin4^p$2I)OfAOV(GNJUvCj;3g{Xq3xQSXfY*wZuP#c` z9Ix?OfWV-zh$3ygXxa`BmbUHv-l2zLWOhf@#uDTi?!@8sAunbZ+GM{xm+E?Zxs-e) zetFSJ#ckw!5heJt1TIi&Xz;Dk$8NLb`&rtwWE-@+K2l@2x3>dveDfiM(p&XD|B&nt z5=r{`mpUnJ9_!u1#8(GKlOcPRd&%5J@Qf^$&)$B>v*Vv;q%Rn)pGPU)<6RKiKhw~` z#HuUllbe!tw#ne7QbkR&alYsFX@$1*y(dm$jPg}+6&?}cG3}Go)Z`=4(?5Uo8ogPy zXCL9y5k2{He`7?j@!$aPfknz1E>pSH^(STege_&!9Qm;%_-P7X=k6*pc|43xq*~)M?*|4{p_{{Ejq1l{aq z`X^(8gC!-FuQFjVnN@V6zeRmMi244Qn);znUZ#eo4symDS`2oS+~}cs)-74>Bf}r6 zr#ZMU>y6vt-g$Q9*wls2Qq{KG$P-Wz;(G7`7Usg`Lq>K?&;0s# zf3pyH)Z|%fmvoQ!t1j9~lC$E8-=!Aqj?X*3>L2a2+03ikYGG z-SUy(_D}j;GY_3P_&*hjx6GT#Pb7prsJzAL{Y%q`Y{ukqr)|E4;f6aC1k5YIr$z{t zEVlkMZAay)CDmeU{!~!#C~@5IV$8jwa3PXyluxb_ep7RCXuzLcC_pus&d zIxL)-*`eSQ5Jwe6t~A_MjEo>p4kRIy)5)(yq0tkJrcPEH-`JsHF7)|@aqzDrs}{#2 z?I+giu2ySJCdCi)C>=b*7b-LlC7H>cmO_(LV>@mkoXjUq1F|&O2{pm0x7lK?SS6Dz zN@gJfcVDz_0sgw*b^xYnx2FiOxP|Y!!+#+R<(RCL&G?xPJKo8>l>BAGz^|J9sCYgl zs#~=)9dA%6FPS^Nsqu~P8UNJ8$NrK7gu5Wdce3HC&ShopaJb|EY@?tOszxnp4p9@R z-1VrUIv??>B=B0t5K&he^<8wjZ%(RWgdX(a?Gw26kvYW)VH%D;q3p%pemk`EJIvE6 z=Mdq1K`PxS1Opbnu$)IGySM9G`uyw zuvu8H)5<(&n=HlJp{SU(x9Ik(-z&GkO62t5(Pz>eJ5}|hm89^FG1`WllESR&Z|SUO$Q}(m)%+X)M}LT3BqP(klVZ;$lH@l8I{7qplsTY2$!x? zI=$sL9pkmVF4P!m+~NVO-fCMTz%Ft5kgGjP+6wqA*O)VWN!CkvvnC7D2@%E@h_UoFe$hI}V8rK8UFOQ*;9rQvB zW{-Eb)-DoXU2RXcF71^yTy#{A+au35%X|-4^RCWguFkhxtbw?LI`tu;7t-CzZ6fx! z&~=+a-eN+@mReZ+-HG?XyF@f^uTTq5&MAD1Cs5qWL?KINWFboP=cLRq27*E$L}b+a zF%@*JiJc}QIT?Q_EyjC85rF{@T;(4@ASg*c5WfNyy$l_ zDj_pddInw>^RTaj)2IQ>q-x3EWZs{6n6?lz$5UBX-Na2-0d#!>1MTy81iE8$MOM`} zct~$T2&vz5XQlScX{!zYuqidB{49qQ!t-=OGs&b*jYRhKBkF_9=X|_QoV}JR`Ud@+ zF}(<4x+=*}(#!>Q%YA+6LRtbO(JwlRWi<%qTBbj`lau)hc4~6}I$Bmu;%1k2`ndr< zuA07QBlMc}(fSLu)_0UnMlCecD+q(4xC$))E?O%J%+% zpx68JfvtI!*VX0P+S=Hmo7rQ$we&cdfX zu3xHZbg#3XCBLgJ zgaZesP7-dcbEIZyD2FC<+W|U(^9CO|*bG)}{DpdVR_^6Qn1W*Ow_D)H-W(~hZOKAv z{1NQ0JY2!)p}Ab}jPhWDXx9~~PqdGg3ng%f-fnrcOTu@5yJwW0TJ%0Az)c4zcm6@l%*k$n(O!L7u+Dasu(+G}?_0g7LKh{emfDR= zG=lu>IaTb@BW&<`)}gp4!}J386scYBZ~#q{_^JUmU|fjQXKP)=n)dbAbQ8yVP05W- zcz!`@jg5`aMm7;8&|$6Li;$}5)rM$^;!COT%#Sf-Kha0V+#6di!a(aY#e@Tn@0EvE zWijtUnOQJfqsAsLaBc+}V5erd#o%z>R!Wba+l1Ju?rtgn+%lfnd}WLF1VWQR{!^3p zy=@1bo=JkIOq_SO&~8NQMCkR;mES6!);+k2JkJ}sXU_%nQ<=Q)=IDC{hw4gHWNdc0 zsq-ZqU8!JpM-xYLmU3DK_r)XY?S2ofoN6{K)}E!^32L>xVq*~8MLp~;r~OI{EM+NY zD?>$PY{s`KG<+VY}s9C@UEM?AB^WI=2COv!(w#Bbf;jn z_iu4vZ2Dcz~GbLUR z(up0PeA>K-e*Jsz#pEWiKQV_QRP971EjIRf!{X@oL&~pD+oU5gL?w|h1ox%}AMFfH z>b>PdS_{#SH|BqP&G-!RG_1Hq#snDg*X`kc>1NnEB#69WHVY7Awtk}ded`51`m+7P zb<8d1EK`R(*4>*(pQKLTVDrh$9IY7l)}`j)r5J9R=eAb6!i&3HN5#crvuSjxHnCnn zc>1A*tO(Uz=BWpEq%Q6$#mH&Fy3+00*)2Kh6RV@o6u4R0E8%&#&|rWy{4L9ELX+L1 z*IU1abtBHqCtF!@|anuBYKc7x}7KL&mQXsb+Ran0d?`}6wNC+7N06RnE&HfijH z^{8Ha^4&ptW+$KVXpEr@3kOgubgk26jsF&7=sxsi76Z_zp9;kVC@#so4OKuQ|GzLDn*i;386i=o8 ztxVy;_{^eDnlDCsv}EfDLCR*>AUI&Fmj9HW`w%c#jl+vb94>6?4M>Rj-pox$&NfFT%S^c9%W;*XMn7gR&GBh%fU6n z5g@L6)1+~VD5dNch}^E}#2WX=1PGfHGD#_OYXzd>LCStj=j{(77p^zMgHEw336u{3 z!t$_XrOFtDRzLGByB)w}oA#?YHK>^+5{T7>rT_;3fV;F_Vn@_2+`V)=i7>jHAHXj9 z`pm!VCMcix?>YXN>%YqVnFsImYVApXo?)7rkVw(OO*K(k&^^C^_#xS9fB;%p;K`Ri zHxbcY98i5m0SNPsK(PCHmVu2E{;Q1c0ESEK+F|R3o?`Bt@v zAPTw=TqWT>msRzx;#(E+NyUS*+=@;7y&+)8Kw~eMxC?`k^7Hxl-~U-nrN!V$o?r&;bC0KqEA3?yv5Rr3pRw z{T&#oj8kimYJE0(EQJ<(Q0p+$@f~YLW^Z7VazG$j^RG{Le#{lFeB(>M)2D@P@_+jM zc&l8h0MO*kU%X4TT#)-QG>a(GP)n|P;Rw{Ubk)krLV!tAnOKe#zybayf(~e;=iaiD z{(YreyIjq6LdQ|V5q4SgbHHA1t+}&2So@n(uoMOokMKq?N>3J1KkuFQKO7YRY9euf zny1Aq$nRa_x8mB5pFs5R2rM-x0@b-?JLB3N{Qu|QyQ=IBs=n)>=J z_IM9}R#F_0(Ea?GjMu*hzv7%inmr_eHT-v+_E*gJErm?}qLvUIaBLLo86>4WcKj{( zgSo*!(&|A8G^gCjzXi-8BlW5kSAu2XzW1-l3SYtKSw4&bpzF>I-uxDnz2Hi}xkI5X zdcyw~LxLuVu{8Zl@`!fzndw0l5Zy{lQ7?T zrKng3EN4NhR1O(L1b)A8QwQYr5b^tkpXY)@a+6SZ2Ea%{3qN8u0!@JEGvKp!2Z%rZ z_tG|Dy{LupbcuUwLkA_Y?Aibb0wA^AoZMN5(X@AoZU8)8M~}(0`or{~>X6GjA|EBN ztI`T$36uYq82xK!7h5{-RR4HAX*`X3bpO)4ZVsL7=bO7!fF{bo>X9FZ*Nm_*@vMGu z?~J}&Tse);YTB33G&B~Oa-abkX7Bu$ApRqe=#s=1Ijhf8SXfg4R^f?G@;w};nz;O3 z_8sGU=C@gY@zW?7xXNJu!teX3wMgX2sWmV)#{K8epzrByNH0r0R?}>oeg%xNlo<9X zpppAPTFn1cE*Fiqt<9W2*wjFYkp2smm^0spG7T~1=oh#kP&|=mEzvD+AaM<#)uVmO ziE9`4fj?*KK1qDXm%8|0W&~LGjy#Zr1p;Lf0MAbUc490tqQ`T)zqHnR}L|OlV%$9n|i@emS#{A!uaiqRw0n_V^ulN2K4S%YTo&VX2{FmlmL{U|Ft&gqs z^jKm3?3DKUe{tN=O->ZDz6$9x<&E7EpiBOXt~c+wvr+<(3f=yfN%40IF?9mC{`0?e z14jM36#VbD{{N+RAUlp90uh-VBE7^qJAfnk?*9ne{|V0jTwiG(I1>RkI#&HN{eu7h z&(r^ls{eT#|F8AK!qVFp8VWx`ylIL6uo)rAqar(yYo+~w?WQ>@6l^Gm7w!5F94 z)kO~P1UU#R8vfk@;&(uU_6eRxvxfxQ(0jm|>h?2@V$2J5XFA_J2xl7Fio z8f?CwPWe9~*}n6Xpc4-?NN|9)ZDxcy-#{+{0UZ1*O;?xur(Pvx{V+qiu8)=I)oN;L zVigkyH}_2ATVOxaQq#CToS^-`9GqtvpE9vR+>roi?uN~rtc(2?Vq$8fUn%={`1^m5 z#rlz;^u|9ryEFvbDW&v|C9aVlrW44_2&veW0P`{ML5jo+MuTK?ousC+8n$2Q8t544 z8d6V2V^#Bpa{}@cq>LOwPOeb>94ZMUp!G>Z`=pm{N^5Tu{ISqHrGUL_6G=B!4XU*&tFX`KvU$6KhQ&iTckzIS1lzBF4CGrx-;J4W0NmIpalRQ z$2Wg&2Rylpn5*f4rYnbwm?OFw)ItyF?jId>Pfd!c9#@Owqc7EAz$F$=;6m}oPx0RY zoNS?|fA`9t35xxr!TfoF>U;PjfBUz8S>%p!Wx1`EyWfCbVmk*%zCKfDj;!og3}7G? zwQ?<9*Wk4|wcwYFNrI~{pNlQAj>$I1U$=-;qfL}|3ax7fW(QN&_eOv3pICrzr+g@v ztZpb@Sj&f$8}npsQL&;3{fE!$@9B{0$lzb^CF)o*f9I+t{JTeX(8*~>CE@ENVei_` zH66y?^pEV>WTb8eVSyihJKv$lPu7Pq!)=U;iq_6mAQ|vA_<;E--{>etD>LLx<7jd! zymT4Tba_~tpqedhWkw|(#w;(LpeipNrV9Q{Bdv~qgp@DGdZ(g=&lE}z=Dc^=)eP?Q zM_*#~$?^Sl0JUaBN>jvqEa1xezsMMRX|Rkj#U^8cW>xY=;uNm9awV^@!g3yYfUPvx z$7Fl_)eQW_C_g!9JUsw^l>1A!wG-s%=d0BDCu+H|Z-wZZ-(Kr??YGxGj!^5CI(dV<*L3u#-uwi-L{;@44jRqZwD1iGd(k#wuu zpT6&XTTZm?1ZLi)h!-sWZK@(4MwSz0K*E&DN~$8yM#>}~7HLm1ccZrW&)&!XyJHPo zdgCfnrU^O>7#Jel?6kDB3QB=D0A&MhtlQ?FRo=L+XER>PIh+WrQ;4_c&SHECpv@|{ zjM2Qej<}S@V(ogE;IU$!6MRW?6Im*6jum-J3m+tk`k zYyR;*^BH$b9e!f{;Ya(JZ^zYswcDp$guouu$u1CgJ5Ea_>UEt7+&O+=yZB(KkxwEn zPhhP1!ToK-A6n95PBq;Be73S~p1R8{2H1=bn9Y6ZBXU4<_@0YvEsVE|4|4h5#Z~rP zH1K zjbUn3Ivl8@DT5fiuD+pVS5 zjb}`B4W>pS?URp{4vKMLMSjW28Mi6e58q2%;6C21&hI)C?H>1CGS$&Yy=Z%rz=xp} ze@o3m4tUqkmdYFTT!1Cq?dt7nj;=q8a;{#a-1?E@NA*k&`!aDi?rL?Sl`d)Na}`J) zt2FaJT<5IEa#|VG)dD2M)Mp879Lr%MHe27U z&tx9&ihK6&6IC0xFI6@WvslDjl9EnSmfKA~WY;yLu`B0Xd7m^PxbWhs&+lzysVCF4 z6R8Q25+Q|+`G#&g4D?B0=?IeW;VSwCF{PMLOmWw{u%(O_0-w*`X#Z@_P9S5Fr$IcD zf1(1;PEZBQCy?DoJ!T>x6F{9@(kv1JYLYlQC)A$C_VNozeI zWzYq{ub^$nm^*t*fSMU=QjHKB@s?$EALzRLKmcySRe1dFERG`GUYAb*QV+lMgkiuX zBikWdjWA35%>U&&8do~lpudRna_1E70Up+f=a>Pnz|LcO5v4Si7E`VgB zc5#ECPc6-d=q=h`F@AdalkV?*6>D=|;c^Ub@EAhGqW`a!Iv4Z^0=M^2T`Sv}HIXcr zkBFb=A6}4$yW34ulID!4>*Y-?vQ>)227+b$DE{t59brL@2zLIQ1&|H&myZAMm9S>d z$(>6c1i2im>i!GTZEE6&%oJ?drdytisgX&e;a{5OGkERuTb2)bmcGFC;!YM-3}m=@HbDRh!_yzmx3IBLuxU2pc|_q7lZR8N0MB)T%dsk7 zwEt^~+hy=X$v?OG|6~Xw|3_ut0Tor&Y~5pGj0j3Hp$&qFAQB{p5l}Kp&N*jnkQ@}r z0@D(dtRPWoBukSsh-9J3K{8FwnZMc@oj3E%|GxEH%f+&7&b{}Xs$F~6sXBM-l$EFq z@zQV@jKZi5*6ihbTr?n+av5`RrT&K8t)G|h#(j}^`(Kx(*7aBcGR4=?<}-{Ee*3$X z!ul$la>4~U8JuSz|9Oc@bEBkIa1#XLM}+=v8Tpn)$H26AkQjA!Yf2Tc05v8vGYEJ)HrTTt8u4IEJlW!eu z1R0#m{#qx9LEd#iO?H&3Nrgt&$n=vu*}>;v5%Q-ikePZ2EA1`+L6O zemek=#)^?*uLknE8acnfhb1AyR6!1!nr$|uWyTiv*kJ-!65EUL*_ z3>Ko@yPZxH!W!UyGohbw)3qfVgS?%EMURzrV9pK+jejRTKW+DK4DCZ$ymF4EIDFl(?K)u9UgU?0)@hjkp!G~(~jA{c39!q?dJdHOPdI?Kk$>|f5T67 zAsP3Lf5lIX>NU9bSlF?iu_4#?op)kwLqF!^U%(k+KzT03WSK!&cK%w6lJ+yE#*l_#z=b02uinkN~LYS#MDm ztvZ5SsYiT053}d@WxXi!3(aBA<-bF8Tg&i-cNb5<`S&HiI!dEkw0<}b+F^jNUF`u{ zPZ7d1!*UP)AJ<6I7|Q!;!RHVPS$Gdu+*)W@9R*V(DT(A*GyXnfRE9rk)@gLPCN(aG zVpQQK2Rr-MwziReR}+)etgN=eeKgI355d9xO%E1CIy8vG4yKxgi>~Z>xS5#Vu`bI- z9({u)1c021t+gTX5~MFN_5AGY3lXFQLu2->#&-JwRT00s(WgQ>a4cHkm>OwK@m}v5 z_XbrxqP%g+7~$EYqpsJ3{3T1&A~7kcM)gCUC2y>ZagHhpxAW>$;O$Zq%KNmWk#hq_ zJ#T_ogs-3R4<}c*kj&QO_J88&sKTJAtW0snH{Nx*A=6_IAAQ$yW?_K>%{k_D!r%eoa=%wa}GM{YinzxP%GieT7@QU z2^uukYrVx=i@ka6ogCImS4!%QGyg)9zebfaT2{GAd9I|VtEN5dT}DJigb6zjJ9{pq zX%<{3c_R!ppY#~=I?TJSbt;Ek=I!e0QtI6wP-AN{2Ice!cX|P9*6=F7^rwXoOhLib zsa9;GkM)6a0{f!`QGI(E!U=H``XT&XIGjTZ|AvO+h&P3%UEA=m4%z8~p5iT=`DJls zlMusS{FyOR zC8D#f4SgWQhPSaf_FLG(94^mXR@YdDaLa@Y<&6>_l@d~6|LdQ_9zG-;`JFC`uEFu9 zWiaK0Q27~&Pcm}teQf=$$#G8Wkxc6Oe0!@M+PmKHblI>1G5^Y_3)^)_)*$8sbV>b_Nrd9HoHfpdB^00-O@y? zjg6!IlI73dHZnc_QCRP{LG99ZD_-hLr*R4x~xt;>R^}buDtl` z5ea+ct*N1-!@m*ufOhcv@fGqP_-n3#G&Q@E3NxgOFsow1+p&pYWE*b}`f( zTD$cpJysdAUm1Q)3V47~8MQFaI2MRWlJ+wCAqy4t zR*3fAHg&=7D3x)HZ6i}bzvF0-u&{8N!cFo(?)M*qf(ZZe^76{a$VlofdECOWVswD{ zPDlxC5q3B{#Yh_2wnpxdo;DI?KYrIoUBustHuoWyx#2C|%fWY_Ka<8PQBbq!H?8Ni z?C(ab9gLP=B%X}isjH;&OkacfWM76MU^ps@+2W?2wAlGIW zuFQ6!0!Lhmj}hFhe;`15!uw@|0ScAV(eTE!cX83i*m!cV#ARn^Cst3KZ>jp6oJi^I z7eHjLhyaX`=Fsx8>JjOqJ;_)@pIY?)>f6$lmz$ca5~Sx(DTjOQYF}p^=f2&8YUajU zFO(K#WLWCxl(RQsvDl|ie<3lovXY?-g{o~VhMv@))cuHFmCxydVqUq(0_{3_HIknE zIT6D-HTa>hei3050O3Sb+#m#ppVJF`Cwk8mZHF9S;Y^^VNr=j;#a!#urk^(8GmT2d z5Z`5IH;G!udF&O)!c8Y!mz>{?_b6IF{ED9RxO9h9hZHSop}SXbaIoP1sG$2yT1x58 zz=D=tn`kqsHC?Ff1se|O4*Lyr$Gg$?0$mX@DVBX_e5+(02G@;dsmvA)K|9HAjdpiF z6Dh+GXLa;m5yFg+DhnVz)zuSmL!I`9-f93x`z7;G1Em+&7(4Wjkfvj_I&vo_j7XgJ z)^Mk*OsuUvHb;DiobefTS6pWK7o0-;j(AlFUDfH!U%A84m<{WFt5HrONiOZ}+qY;m z+Kwl~RE_IWb5i%NY@GMKw=jM5(?0jEC4H^07rS~E04e@pcaqw9>TN|ug}QZ#;+g$a zsiD)#k{ENhf<;K4NP!ys=~V%k${vfmspbjS*&6nQy{z+$v0p|GIAf-z)lEw<8Kt^b zER^R-8nelnH5|w4(S+^s)zui6`9dgL&hvZMVVV>s_gS?9q9Q}*;Gh*{ZPDNwvnI~h z*O#d=phj;rGqlNwKK9*vJk~U-8EH!wDxM_gqr1AcwzoHQP@+ruO3T^Q!C`}V{+9Bf zJt5(ul4|g$vwT-?KlmueeavVT-`3CV)&ZwYs22 z9g-5Db~(1AxK9Ejy((9|{36o9yILrDKlgf;N-T-l>CTM&!xH{e*iwt0?7f{0>z?fK zq*q#m@md6SW=l&6o-9U7`stp6x`hGtW=U*g-)xfdv_sfGJ;9{itA#<{QP{hb*U5Fo2YAOvN!mo7Gd8U zjf^lBaB#bO_E*0Qe=`3gyiqf!BT?)hZGxUtOr_spN;uT+v1(#$Y|KJgJe?fvz8o5V zOB0)$m{=8=4rf=u8g9(>;UK!UwUM0h?U=T^8y!@mCv&)Jrt`hS!e9yfbtoRaJW7pY zd9iz4%UH^q?~1j#`N<=P00Ik!+;?(X1p3%7L-L7vIZkxY&n40+BYi#yN!Jul?ndY_VFY+zsjaBdvuG~sjl;-RPI z<>j6^u6;OMf$eB*IHR(bO~uj4SckZd!PcQ6>cE;~$BsoXYi>6}KV15I40ffNx38~{ z5Z+Vtb=yNR+(DHxydu`t3n=((HmAj>YJ_+G)94Gf=6VBGq+V2V*xa0Q%lg8hR71iY z2I$gOIZKJq(vU{cv~Q|y3-*$htZnZ~DScSP|Hj?^bu|t=!ei~d_T1EzN}2m!{Qh+O znbW5^?%pk3?=??}cL%cvs~O1B_f0nSSbgB}&Q$Hj`NzqOF-{YaoC9_p&9S`Nu1ht* z8uD{;RtStFUGe<{d^&B~eyU20E>wnb5zhSG3bJ<9@5wfVUN=0}9)E^f03!2bQ}iU< zWa{PbbLmzdmMbPidxHw^?Kn|>Xs?})KY5z;lo)HFRHgOMr=@ua@!)p@4nuAfa2Bv% zwcee^Y#G5lI})e9zCk)9#4G}E2<|V1#6Rf;?7SY@AV&LAB581M-a&Bft33STMisGO zKhUKn4qUZf7A)^hlsTqwd**!+7Ary<-9#7KAawO?(0#9pH>Od<0-M87HR3SWn`hdQ z4BV|oFAM(njBifQhn!;?0+@*Y%F9@YRjG(&%n$ii(@WYaDsqcN`lUEcWcIV(OHA!Z z(=1EwE)SD~i)DSert*AO1c69cmu^jf^MusFr7(CJ8XB39r@EAAF@ycxEqJ_<7Weg> z6I$#{lzt$HnqRVsVd+P zoP?E-?nr1e;kxvMTtnM^Yn+Tx=}WxefzL%N$L;A9rbbq2w(}>be9#RB%>p|XBu)!1 zLpy{~093Yn;Cagr6VDEo>`b*Kf@$|FmoA6j_SkA90Ia%tAK=5*=4c2sVKBns1^bsB z`m-FD^hoZUq9G6T7rPlAAoAJqah|+cL&gvIO}ekmX8a4pGLZn)xGXOuQbHtDEVVO# zZXGMyqwp}3g3a98X>nhBcX_->0EMF$+-iVx)XLVn)RC+n`>u`I**zaH_-0C067H2R zYy>$$>^3Fr=H}KI!NhAbEQCaVyu}y^N!7zLNpeqx-sI)2Jv+&)l4I0lSun8vE%3IM zZ4DVeKR@y@xVKgJ$C1qanapEEM4VRrjx}V;u#o%!3dyL+!neg$RaJ&fMTp_wz!Sdn z^jPMX;hxOculN<3k^AUEXU$fb$b(6jp{<(rS4A=>HBn6*%{=C;k+U;10rwM^9Qn{< z?Ck7*VtiMKeXDMlthSRWYv@w?yogmplKPuOk^76pJilqiMYS?=m=T$8IVmu%R9c`NS$4AA9Ad@8 zON=*Ok{JfxcogLPNlOkJ%kI7#>H!_RTg$r`?hV)RIMG1zn$abpt2Vu+DLwhIGAXgA zX6#tmpuC>9(1RiwjUSzptI0HKVp2?D|YF#@B}H_x|JyRX(bll&mksYiUpH3g{N!J(l+mwS+AEqdHC1` zSL69n!y1$l%GbvSo)H!NO$7NUoi{#=bLZbMhXHJpnkG;Lxwp3m=b4v6U#wE5mRpB3 zCzB?26(9|INbCi??+&(j?5{wq;cTA1cH!bh7iZ^$wyFC1dXGJv2a|SbaaPuyf?)M` z!oFwna&y1p2!D7Pi$ddGXDIOXmEkL@AbFkEfcpZQiGR?HY^+t35iO~ynOQHCPL%ES zl|l3X)O@_PJbv^FRBO29TN<-cRj~keNYPs@{Z6q)l=tQ3L;ZFkDtWB7J)l9?6A5ls*l_W2alyG3T+>zD_QnG^c4=gQ zR47!}QWD+~K+@p$CgZtw*l2f_ezNHkJGBR+f4Q+JIQaUya@{sLAIHE zWtVQuW{s3mR>px!y@p(JELL!GFfbS^e|oqt-*N{8kunJ^*Ua1;8FvP)!x@C))4y`g zS;@erL2f`Xt{Yx`$PCWFhWU?eJCSV4gD>^b9owJ>jZy^W)unFDZ#VV3Uv6&h?h3dm zgaL69+xxGR47EJ-M#~zvw$8er z`zt?*K-9NfMo6O^3|DwjXxKbO_2z`m%+6}SroOOn739!ZBfl&I4Bys{lSzSmLX3Qx zzaQIrh3a8uBjOhB$(W#voRHb!*^7jqBUxeQIjZ-mFLL`O` zz=gRvNP3vZ{&t!^yusk!+t%x^G>}M{6|Wc4GOg@PXfZBSbEV@g13RE=YgCge!!Mo!;_P0RK(1$h%Zh8qiZWIl zcNNT+9qdao7aELz4sQQbI)I}p4#1lUv!#sDD75U$j~pOFC@^$Ew)W3` zjIQ&I*-z$ruOs1cE?>F?(Em9;KRUCdWB_E}885l?tSnmtgKG9B>fK=?2%VXRsv!c~ zz_rNH2CSx(_ivF$_q?nC2NZo*Sjc_;#Mea*KuXD;&dp%Q(^rTt0qp*7)MZi|72S z0Ym_4zw0c(crAt61>Xlw|7qJD?6D*#EG;crYkIt7Z(cgX8?WO6;g^?}mx5V?3m94l zyT`%Kkb}K_Ck{t^`ZSVS>WAl-NJ}et?#@`&uM~or&74au~Ia8ikcwvSnOb{fI*e_^i z8;FBr)QtkYZZKGGeJc!S@sg7mA9-%5nx^oXd*vfrD`#{Q2s2P0GAYEDxITIu>|?P! zB(^^2$mB>o)5M%IBF~GE0=grOGBGg$!VZRY`0(MTrY1J7rA#*Wioy{f2O(GZqr_-s zc;|9W*e{&%l~4ORJUk2{GWZhra<_g+WHJL1iy?$Iw~}c)p7STT13xuiPVO+Bd|nF- zAiAkH-!jVR>e*E2%g!}H?`~KVfPU`AdJI3};b@p#bE^hbUIRS0!euZ6xuzWu3kc$sE>P0qIvH#qiLN)wi7-@+$a7wsA$6NZJiI(*Z1XcADj&11Iw`EvJRWJgJpVv& zyA3AxVA5lM5_G7*?g(+FYK{>Oqgl4UmN^7MMu+7S3?qUZi80`cm3e>!|JtfPC+oV0 z*FLDEDE++&%pPzS#++!{em)&C_DcJ~GteJcA61_0)e-tnLOjkVw z)nMsTORa>ua18>0(`2l%YS-f8Vu<@{Vl1!2`r2AU_n0qa^Ek}Arm$B|c*nFABS9qs z4Sv;NpqKS}MV5Y+N1cL64a6X)jKYbQc9LHh^$xiLfN(w?G zA)2`tysj2EqZUg>!SgC#2szECJhzq1hBu=CWp z_`!8SX93n^SK{rg5I2kT2l1sRX6d28i=0EXd&y>h>Df$7%bV)0)!uGH4y7#qox={^ zqLcB3EHj27e9QHltr}!Irx2bVL2C=a?(9s~Czll-4PK^xjJiH9Wl6bHK-%XPt2cIU zX7l$~+IDhp-n+M(sOXsQUzXW^(=l{9g-gJIqO=h8)Z`?*iAb8ux-iPF;?*Lr7TNf~ zy~nDbo(uMg<2X{(W-obor_GzSU)PF6ZU&cC?eYqC4%m$2rZu1}?8>`k=HYkg*&fP; z`*9zWGz9n3o_#=Fqc~T08$E$xGxgi;el5P>v@0zW_1nn@lc7G)&so8ZJyG|OGY;D$llgB!ti{OqV%qzcPK5N#7{iw+OYJ#tv@~EW??n!(#*Cwb> z@pX}rY*4WF0@N~o{&+DWq^4hEP%)vF^KR<7rB`%BBxx_*kKt|^hL#t;pR4#frg~l%B%NDziDWmPQ-nr z-$%`6qxNj+^%%z)dRQ2C3?@tDFQtdS+)-7SaIxxp@|5T%6e62dZJz1xFML*a;v5fo zt@VY1Pk1XMO3CUEhlA6wE$_MGA6?|P{z#hj;5IL{x_rLh4VT>Xux|a9BlLJ4K|4aR ziT-b@3gWW&UWos0%9o}m7*NmTa5qq!hq|J zi$iw3dAwuu*aR0>Epkz1{ivB(jr(ziU7=m(BX9BxQzm6pIxY{en|5FJK*8(e5}cRk z{f`?o3!)^8KMQ0znoo*qk%Z$-C=0OGeaO#B=N2xfGLW5&@}~AHKKbU!xOA&MQ|rV_ zjF?t;lcCOrX(!$OQS?kh(ddea6^BDM#^*R%S%glqq}dXMFHV#$;k*5i=H{-1SnpBK zL&;p0B3kX?$48!|%lP;^+{>quXL8zj-k_vMpU>>aRZ{#oL3c!CCcM9?ZqhjJZ0a?A z*}W5@(nZ6E$p%j#@F6Sso;vQjg}L2+FQ+&@U^{RMbA8Yjt$d%t{6X}E z{xs!frGhDaA?ZkqJ8!-abFTgI+d|S#ZQ*t(4+drXmo=JNs`Tjz#dwLL(ED0h;Y_T$ zZrH>jwx&w98^Ay_;m73#JB^^Cs-%*#3Ena_K%HtvpDcO zR{JqBssc$r^IU%CkNV0Mn~%BHo+%P8D5?LA7Jx~POmr7}zRctKnkN~d{k zHco+IWq#^FBxS0WvBp2FKVC8Vtdn8oy#@dHy>}Iy4m(*Tj8=7?W^xVaf>T3BJ)b2z zQ^g=Bf~{hZvvIpt<_et|3lpOKyY-C7b26`=0HCa|@X)<59X5r!Oryr?ka)lm8&Son zAy;<|Lv}IYkT!D<7oULKB!jlq>s53CY9~OYuX-LtJ(R^>`T$N znUSXOTBaeHJGx(+7u3Ix$Y=EVNv40G1HIY2BxR<=3P#GW_?tut>sxDGS8L{`XVAy? zS!y7eWjv)H8rLT@sWwn~b=r28ym!KM4+*>hfGaj~{w3nIOF%l$!h{sLYuyQq`Pcw7 zIF|Rxz^$IP$yOHFZ}4v zdiM>(IA!=g%b%5@!2JJ9xf$r#zlv7FmtD`@DC}1yORnr8m%zUpvKes+e*EosBG1P< zP&ou&R7%7{J-TuYtGMFq*eR|+l^R!j!|mqH>5E#M;#y3;LK!2zhY;iC+n8v}z78k( zi)^(86rHJyjE6HXB8CaOHvgCV>73f4<9mkkJrrJ%bqtaBz2^e4sTzb?AXjNzYGw^_ zSbrzQl{9BDBBjIjaI^3;8x^AX!~GS^gRjj8G$|)k3Z3U%8E+b;Tbp`US&a;t-CXA- zmSsVt8va@Lr!pjZ{Ua9E!sz7RYs)~t{8i-nZ$)j;I+5qCS`QuD@}G;%0TAWGl{RlB uBa(qm>c_{3ns(re2ysyv5zIZEXa5f+n8@+~ literal 0 HcmV?d00001 diff --git a/gui/src/doc/tank/phone.png b/gui/src/doc/tank/phone.png new file mode 100644 index 0000000000000000000000000000000000000000..af9966cb4b553180a8b3bc3b9f8d9c7ec1b697d4 GIT binary patch literal 64365 zcmeFYhdZ0?A2yy&T3VwmscMy)HB&W$(i$~t)JTb0NzDi)_I&!-1hsdyRy9^*)~*$` zqO@X1)kslNV#R##KJV}SD}LYL$bC2*ao^YV8RzFb&nx<=zUHNiTo*wg&?Rjxbt4ey z%o`BsR0-p0;5Y9K%SC{<3oo_c7!c_4>ffJJ?t`yVL7>|pZS_YczB#{V=*c(7QhD|; zM61p6+IJsWZ$Er`Rls>U?fb2%nL+RVHS5Q7nU0Z)_R-N9C+>*gES1l5jDG&vAJyyp zQyCHE=W1KTrKv3r!JjfKt*<>~w|J%g^8nJp#{_c|k!7D%@LPv^(v||+4w_ElfqMag z$S?UiY<5xqUHhI3V;tuAd)fFfgAIKOxOmI7up0dL0tBi&pD+IR-Sh1K-v74_|BHkF z<-z~z!2hr7K-f{E4X^;8OKq|48jl7F3w6}hvsQ4rB5aAZ#DK~il^k3Z-Iqa1n$3y! z<`mhvWb!oVp=M#qeF19$M6M8mPe(*>WT}$?KG=^@B!DRoAU0n^IW>j?hB#?^AU6(m z3lPXRw0sE)S&H`UNkR%F;+%E#pr+f~T@UTO$riI$+)1BIkM~ICln2ot!K>Cs^Jkjf zS%JRwWkb5Si3I>e{$xkFI8fy%|;xRCsOusGiDUPlVnZ!iQiK0ZNYd2Qvrpt|no zh5`Z{^RHb`eMK>WKyDu`%S{AUxgl4sutA)R1b)^<^^*c%dJw@;uxiO_?7HsYwmIbS zb`2Bh{sqP6!}$PeG7iVye{bN%YLsw9$z-A|;vY)5Q z>hD!=w@Q>Yiec7vRHMPGW#9sJokHB2w&Xn$t~edqF33lOLeh*bdP${48Bp8JS50Kj zt6TU!;RROj^9_U4k`Lp~R%Q~HG2d#`PT z-Fz`vEFvS0%_#bv?3y8zk3>5HgWNd(uIevFSUCsH&Q}l8^_c(_G%WJK`=&Oe!Ob#g z&^p0qR}yQ}x(k;+n$y}G;>$!Jv3TY0i!|l;QCC~YI&o=ncu3dNF%mu9R~j_)>hEJ( zeJ;!zX)toM8tW3{TSRQ!V{ygeiQ5OE+kgPk}2qaRIoLdWReQ-_GR1$4N z9-rK5b(B@D;Z4efk4Hf&gwJ=DuZdD2cxT4#jp5<;fAa=o(U?D$I!+b&jo5}~-#wn(8bDTS5^apt-zU(|(=l|;K)PX?<5nVF8d$TscL z9Zvo%3!Uth8t^E8IuTo|YiJ;u1xSn)gS%=%qN}6E!KlO<|3c;;;$ix_*rw_DnnoMZ{vN}fILZEN&9I68j*`n`{cp-z7Yzd78ce?a>To1rx0t4wWJ zG(2o7*uq=Gtz~R#x^ZiUdh%m#rYSFYbM|EaQHOO3+o5g+%xVMs2Jau&w;6Bk^OfPWryeyxi1yz+3}b8^a%)NZ zZ@h>f7r;-{bMmZ`1qY1@KxQ}IrO;Rlb~QreJ&Cm<89CIJ1M*t`7@6MgcCsfKa%^+5 zBY86AqVOQ-0YRQhcc~BX`w`(gVFd%gfw}Xuc??78y+6A6Gk>@AR5RNEfw1IQ0jBM)6F|BQHR{41Mq@DADfDTSuCd^E{uh+9GLoYJ4eH1mefAmU=SUcd7q7F;O zB`pOZMT!b_a;hatH$B)nt9@hS{jqAX<%UvGd4olqfPJMe-e^yptZ(SO}ZR$Mzpx1 zv>Q>Otq~TkooXT{Td%zLM@?X6YT^U#UJ*YZRF~o)ODIg)$#a8QWf1;1B;w-F;l(J}jzL_(1D1bo{Pd_qcZrTBkv^ zU+)v_LA~iK?JL8}YG$a|4htj>cenrEP#4U|er1<|I@7uq`;$!Z!Bv^SAbJ!viRl=v z7)VhHVxY}>)#uWW8!Ue(F0w*s`;Xm|KeH!2DkrhSlZ{5okhH$496cdoR_%6ezHrIl z2X(0FJqYTX)}DVWL<<^Gsn* zSQ!FqGGu3Rdv(5cJ2#T0IQhy?B_h@XL45E@Q#9;b{_C1fp-!oWA&0AJpu2Ir-CzL6Cv*flBo-sMMB04fnVB*bHtD^X zBtr_wQ?(qRRQ%~k`nI-{DoQ>G$eYd7d*3C7NQ=9SV7{W^c?YC)>c6Gs7&x3#IquCn zsXRIGMdx~i={b3L>SonRSXGghmcmj^o(!ndZ;v?#>P1IqNsMFKu3ce@PP8NqMt(Md zO{F5Cxph-Fvm79Vy?jP5FAtBA)$Ppij2w(uv zxc@6lqETNMc!DXC#|bAGB(mBn=Y~VFNTRDN+VC6VJXe*`BBg^7q5(l(YLrmg>GJG&lK>b3;i7-tO?lNz}JxFSbx@$%jCrOb7L|6Tad zxW>r^(H{0>dKnQoBMGGR53f(2^i7_li6IHJ4FGClAo@aAMnBlU`F1NE$uxOR0>zdg_9)3iR*x}!^%&)E0f{Ur)b&@6@9w8ykTI~n=3fWu*R}d4Y&lZk=g5->a~@--Fp}RMOVz+ zizCc8;#P1(J=TKS4WHKC(BtQOjcutQH^u|6HXtegVS;eSwb6!m&le=#WpipUGehNI z))*~d&bz;Yp@WfqHI`yKK0Zq{KiV8P`d9A-%K=2o4d#gXZ7&LUb>O0OV7qY=dvpfS z2$mPRIK%JVppcu6ju%Bu-zO(if}Z|Ue+u_EFZ6)o;J3PS(4p$LWC{aNwjj~aB>KLS zj^3%=8bpA1o@&5rtjzN9`g-p$9(Rwa`eW3+UYn$~%H`@`-remfz|)R3 z$Pr2G_?L!T@doj*D~Yyq7WHm!E-p&yHRpU!{};!XY5Pk{+pE-jPu^Tp%+{;4X%a-3 zAQ60nR*cgruG-O2(Vp~ohr$Dov*;JZT-4RoWfFk_Vsg?~;;Pzr%%1a6YiJN2-e$x(YdZELWNS6P?C$RS=@;ub|3H6_R!_k@*grV?quc%4Dh3 zR`ks$YwH?zDNy#0jsum0R(2vOzM2GiR6lNud?gvb6w0D#ql=aC6*cB zcOR=)v|wiB(}jFe7xQPS_&=LIMHBH&168FB$>HN*He?q4d5TOkHHSUb8yC70{)x>LNJ4*+1asNXS)*p(d-T)t&}_Eeo>0sk#s#vLEnCOwG%Q1 z(mnm;vl!Wr!*BiQ#TpYa0lt@y>nn_o8vnABT6#ve{+w@zTnX>1%s2?~;3^WHR@$~0 zWSfH&gvEK~QrkX8RX@XC!*G3tdl{xd!p4qPY_g)vR?B2yu<+EeAu)~0^x1-96BsPm z2Z6=tW9L%+<{<=N1YUV(1^TC6Gein}S+k)?7655m70~YPLUin@XsN{BOqrtZ=e0S~ z>JT_wmQdJ6L*azdq;VIR@-4HjtukvrRGDV1vRf+NMg*(j-d!%1(tUxQn4X-Rn(@O& zp8JJ!(-}?6%*7A`5WVlv+dfDv{ny&Gt#EyWoMehyN)Ya)594t7@J}vIz zLtrm>5_v+ZrK+o+b@AKTBiN`%&Kok_C8T>0NVfC`Q#GsiDP^^~Ca`NBKoe!V)?z)p zx_F@ua^Bwc20B&CSSQI;{KbGeKVM;1)?M^H^{yNvz94g_v|cm6LtjZkO^)GfBV3<{ zLN-S$xV&7^0VYx;#T6geHmgK`^4y-Sf;NlQ9cZqrBMyApJX5w`tUd)UDC8emlC`G* zw|vJX#!!7T&jq#S90nV0toT5Gv@@8jrgS5@N9-P3>|VZE+H)jURzNo5?}Tl|_})#n z+pOi)t6QKSsr_+i$YY@Ey~)%T>E;*Xi-?X2gJZ%3;^_Qvi(os1Y=boK?(MHt4QYN@lJ${$VVzlzVMIF{nx;c_JPsomA`RAY-~tcbWxDI~3kboml37GeZ%%pbGfGQ;A@ zV|LnWH}Q);ga+xq*tP_Uf36V*&!G|F0Gp*3t+jan-NkC0v+_+6_MzFW%Xiq9)W3VT z=bj&yK02jNY}oKc%X63O)(Nn2Aj34mjDhI-4iI9))GKT~oKE#tYg8FNh};t?NHTTT zG`;i`D5#~S&VG^cs!Y8HM;0Hz2i#@5L5V$tiV(iFf7e|f%=7hd_4dI+OaxFE4VWGB z_a-`mRo@|s$>Kndp1IIn^8gJ9M-fr-lrA%bEJ>bf7N#FL?3{cuId`Uut_-iZ*=1KA zrfsZ)n-mjOoZZyvXJETSf>T>|!+|eyfLpG#9}X-n-3?~Byu(9olgl2A(!xsR1r0d3 z)LLa4)l~_YmE%7poz9`vY5;7FWDG`{?m_#tqS<9eja4%IOngg zK{ub5*&%Cg8fHIvudd$JSIC9bLC>C&o5KiH;BdtyB1^7vuKw_$vxYxYFIE~c+J2?7 zM21SYoe$0bGmxXo|1*E=2KKQR+5{OU+J7h*4VHpVqy1#@8a~ zQg@pY^P{rCaP8X0<`xNr$M^$j#VIq2kriw(Crs%UFCo@d>m!Ma64hmHGgj%c`A|aq zwV6N_83mUp9VS89pa=JV=Li*n)v&|hWa%7Uqm~Z#WS%=nUL+JDqbkw0?RuzOJEX zR{njTj^5C#BmyfILsXD85Lvpuy1D)vobN6>)YAA>rG*%PL`xq&?J_qVt9+ix3s7!w ztgI68r9QN4_o}H}WEW-LO)xNo>HWsD>^kf@w&{W@g+Edu{Y{ zYoQw(hxh)m!rEWuLG-i1KAy*ksaYN5e|Z4!rz3`hPZn8`FUit!p}eIy)dKO)(8}#% zDd_-erUue{P?qFpvsLERpIp)))wCZ@46u)ebTvwF#(LTcSlI7q3@P~R5884dujxx{vPkvm#wTwgSIvo?jfpXK6yg4^;Td*$`=I~SYr23r#pZY? zZI`TYBB+1!{6BK4F_5}lP`bcBN(-Xj-(BYYAYmHVawwyh$L<{M5eEx1i?Nm*dU2JF znhOzJ?BpL18yc4WXn8FW!R$52*Wi+4lr>0^q)UJrDcdkK?b&HIH3-QIonvS1opmqP z$)g-sd{s3HqCnNiG9A|q5a*oZt7fbArFMhSk)1DeUXG99ZYhTQ9&{oHtw z%NgRf1Ca;q*~jpL*<_2w`nv<)AZmz;0BKHol~g4xUOyY7g?a8nJ<>?W3l`o;N&%?3>d9o`70OWx2xit8&$UrtG1??EUf=MG}TUZTq* zbaEnZN~YTk2Ac|E(~=2My~tZ<(IXM%MQkj7d7Sdz)*nBKmVnC1xN)ooDp$O+w*dLd zJga!(cL-&J*+KJ$jEn?5NL;8=k3?ZB$Gnh=v87dqn;moQkI3&|vjnrzmN(#|TRE35 z0F;u<2U)d2ql>)UJUok!CD}R=YuPG_j6W8Y?prJVKQBN_yd#SC%LUrfgYE(&jTt?c zhU)Ga!_xYBUSNcRB^H(>ikJcuNeYx`yM|YZ(MHS;@p)Bd^KW(?+jhzFnrr9uQ?dB2 zl-c9utDHtUPPLKpVXUgRN5HCZ80U1TA$T=iNXjGe_V>l*>r8eCWiqQ%-19kB>wT}TXCl0`>-q0DMcU{GT^@W<9q>lex$ zskS|kZ|X$#WhUn=gVZ~Bs&ho^?j}Y>^A=u(Vx91p6f ze?+ZcB-m|h;BX|b?g7zQQtIaVwu627wgz>YvxGhIV^ltc17v*;DJJs4Ibc>>+X-)$ zlfxa}s4v2GN5k+p&&_THehMukGsEGk(on9jAfI!#7aUvLq}arJ7PgoQpMezP?rMA8 z=Yk{SGE(k$-{-z;>J5wn!bSGaCML9B7z9|yK#1>+DY-sxOpqmHSy$o*H>M1N-x!N-uzuQnEd6LN|{Jl z;fpd0t6R5_*g1HUl}49Beb!~#IiH4ML##FgZ0{TppE+)pO}xC*__bb=AiqkCaMOnr zgTn@$cf;=%nW+TKO`D@)fBu&9va?OV^Sy%_o&x=0PcbXAU`h!5eIO%_*O3wIcQhL&W<+@hvm&V2p}m`n#>JIG}lVp zos^wXDP>rTzTeHN`prv6y$j*OEF%)_6k9()pckh63_rmS%w>1Ku{PO^#kNO*ky_Ej ztJ28U;=si_H;~v`^a~edmF|ZFekB}+t#WP%MnqKh;*#F&3Q3Ri9Z5hde^kTODpK@X z_5Ncm;$i)jrIHk(N5S;Yd(~b5i{Dr>=@P^*NT z2VdI7bPIE`uMoF{lpbk2R6FUq%X35AjmmK=M29>fy#a1Dbgr8sGsKFG?9DlXLSZ*F zsFoFaknb=w7G55@q&NGnLb%(GoqcKSqoyggS72n6#pBy_-Y+Mg4_J3nL#3q$3J}LA zY12FG1-#!yit3`K0yF&l3d?dOh%Q*XBU;(OM$IWfwylAm zPGJBb?y=p+`M$C2SM*E=fYP!^cGp)Fx3y*&bJG9*oG#3qvP zb+go-tJ$mk)iOjg2QFI5cb813A0N&YObnm@wjQtU$A%qWX9&^x%Mn9n~x-~?IbT%F(i)bZk8!!A7+ zHCC~`ALJgabW4To=jq|01ZLi}TWOGdJdmAj(Smq3mKr4$`-aYD4Ku!#^m%{?pjnK> zhX~f>yNMnHPdqRJ4vWlO$+iVJZREFrsl&%KgV+fdgEmX0Xrg zuMg>(1UrxV7geT|*LvxQoYza#z~bNEsgB+hyn(#glffyPqx>lftkyjF<7*Ag0+p>1 zj}B0c5vg;Nj`%p;>ibS5kn)%f>7uLCg+Md6f3oM-a}C|rFoeOFy<&#JozU4zV=&Mt5jCPNxE@VCZlLJficR zq<@d5X10Ze#&AI$;ZJi!K_Vm~({s^$sPO$AkIZb57#u{#=T`~zcTviiUft-wi=EEL ztB;m@lRTKW0hze4u@&Dwz}rQ40aUD+2^UhJ6Gs1zvrfO!xz84>qncAsM~O7&#=($H zS_{?gUtLJp{y<_G_cB6fNKx%zVp0Fux8r7V#eNm9iEbC-)AeX!Qp3|R5rH^EV|F$U z%zp7EC)b@~k=dm>D~hGzRgxbP3+}DY-1O$Qf3_6(<~G61)D8xN74@H1D_OOaYW+0e zQ0CRZ@-FE7oe15FUW=CQjTiN?n2HKq3PLBb0F0apoJBxLH=@0Q6jH@}zV$5&#xJp# z<3t`q^pZo1!7CR#+kjc~>(wcxo`DomPCx3OC-2ao{c+i`J#kdlxPFNqFd zqaF6veW)ugmVRbH9V}|5V55bLx^zXuZ8;K^tRvFxtYHFnNp+VV8DlgPh92ZOXA$ig z7#KQ-eWk!jMle_;0|U$YOnKiqIjSl=EE>VQ6YYKPpZdYl1YjJmnDA>;Ds#q;2FB)f zD*$7?Lf88lOxv%2i+0c5=sntB3)eQj*sz)s%`7=}sDU*u_wWh9CVE^j&SDpo=G1VL z|GgWNKeoDf!+>Ot1onO?1pU}kniBAWevoLgii?3nkOGzS^xr4s~8*r^qDTZkU>9Z3f?D zuLb`LMwM%8ui~0B>KQ?91z=~S_N3<%U4oxL46hP-wv9I7R$@T#saKHg9;|D<*1C4nX}!>Rsc_Cr>sLCjFG&euQ{W?A+zkFa)@PVMirv ziyLtfBy+u9u-e*7^--l)a!xUQx`S6G;~=sEh^1*ZyuZFgt#JSnTRri6Vd2HS%mE6m z8?m9|A5Z&o8nxzhO@{LVLW`FR)vZ;gcCl7|%yq&JnKN0#&68}T154!xuc|Xcx*LP5 zgyf4%Qi~*G7OyGrjFo0zj*OiVVgR`bTz=ZJbwp-iMseUHpC?E&KfqcP=(= zwo$5uuUtXt&?bKW`SDzytJ2NINWW2&AunWSwV<;zwbZ{eD$E>t^X0qn`iXFL3z&HS zh_ob&38Z>kSw^weW;d^4+VTnihZ&0}^c@2%Ladr!0MVJx`#8JO5U2j43`2xcNrd@f z$NOOB{i9$--d9LwXet4`+IX3_(EGeEqaQJSVPd8sCA;z;K6cfQq$PH@R?8SIViO95 zx^d$MuF*`)zIwt_;eoR9n?ZXR?7@892FBxk7`SwDTHKf~KSfnn1WHXct6hf1L%6CH zxbbhRL%oc3KHM#u_(a-DpiRHlnwma!ld;ibvh~f5?QFRg41}=kx%q3ZbBG$qVf<6+oyX#}#d%TQw=+y+ zXU+3GoG4ValR)9l}eo;zRy+yl-xC z!_8(=;^KQ8Us7z99h~HqYh*6Q0sT$@_gCc%QRwB1xlHt1uyy}my9dOH-WMI}gpBF4 zgaM7I0V%Gy91J0Y``}IX^$|VRwaU%Faf~0ayD635=S- z)LnAE+3#CmF7ldOlg8&9+k|_q_rhM<&~2nhwF}AKqRVL}kT>T+5?a`IKpEmemi2wB zc;DOGTX{myb8YbnxPNO(Mv?oHq*cTI4>BbnAfP3DT^$CV zJ0#x0LdZC&cn){Kk<>fU@D@X`~+aV!qEx6dfHO-#+8mFKs*-#6Zs zTra%%`Njf*a}6+Sw5ZHH6ejrDJTQerObny~32bJ(b-!Zw-b)~%)G=kT^!$(WA%vf8PssdP!3H~Q~^`_3nBa zbi;_DtWyGZK0jO6e`@D+*+{yy7|?2+IuAy?8s8ga_lOr36m&+i+=}`#S~^x%{jLH~ zo`9lovP?Y*R>)mSpLynDRO_o3fySHB7fB_N=BUsO3RT267_ zyMtI#Y7`AoYKyRxL#sp{v;+sAY8ZQ_-n^@Bl4ciAgtAheg>@tFlkO;hLh+y9@I@k# zz$}KMVIFL+QCt~dsouhHHF7ujbVl8mz6 z8f~U&M1P4YE03$RY`AwTA(y7hg~-wq((LI0P*wHBK!7A)Lv~se7UDN)8r(Ro^dqOg z{eHR&&25o*7V_GoeQjPZChwMcH`+Mo}9qN&9a~-}`CVOaohW>j!cQy*L zy1MER%;=VT9-as;AP5tb(58!-v=E*#?n$oYM#qAa(R#%E`TY%;P@(ZZ62U;pw7_CLLQ8sFSPX+a3AW!0j0 z0$}P|d9A25zr}ZMjE@}pkq{)I!T+%9B!&|pktYg zN5FmS{_1*~)}-D1L+gUqld|x^J1y!pxkE*`!stzzK&UEjJ!vI5+pF#Ao5Hu+{T>^? z+FLYPc(x8g0f&=tpkeA_@7yr4Kof8P_IGxkVVm>bRPde!DJ>&zu&0^O!c;DL-IR3s zW+oBNg)|6peKt}^zg`4)Ez#vCl$DpaVAdvUOHBzVq>*{b;tK_+l2YK-pE)3KTx6e! zz)_|>pTtllgSwKVV59^K1$v=D?K$+}ZPTbssLdr>AmmOG4=>AOvv^G{kwN=vF!K0$ zur*t6Tr>1|=~`F6tu1dDcX7`=U~V)Iu3h&0!k6d_h=al%h_p-K!4ugBJtX|Nouh$B z>!iISOn`$e{$1;@qh1imhCgp>W_(&Krei7s4Jz7D2TwhKwRu$N0eA9^VVA1>DP(rnzuq+jfDEExfS~)SiT2Ah zWVd%VXy2&>^)t{v&fW9Wj}O+yHAPBtcL}iv1;F+WuFUX_J{|MAxryGjOW}?qjLrH{ zHW$~1Ul!^>2pVwx_uoQ$5)9(wH{tk0Q2*Nr&!^G%4IVAz^sh05wm;95p?wVv)DcrT zE$!y&8l%PNbxY>8L)c<&BlO2oR1T`ePuT2M!t2GaIa3&#iYYLQ0RB-6pmVDQPysDa zVv!vKPs3>&I|r8y2`#8UTmST-xKM{QnOctJQY$H4xwbSZR+M2wFAXvS^7CG%ZcM%$ zS3)j^+!P%0l_zegnm#ul+E}q^H&!MVW?J;E@0W+`TGd$EW~6D)Bs&}6_#1K`i;a$! zC&=dl_05NR+!8?MtS5(@jO^4oD?p*-sTn%Zn`yJhqc*v^`Xx?@tZbcH^&kV2Cwa`thO}bVS4K;DLJl{4mBN$=!)pUfI1Z1n##)+Ne-tS2;ePxI#z0@ z$_6NgRPrbHwMiICR|^vM(lfBRtt~IIn*gSnN`}0soVs=fdIe~OmM;c_PWL0kYg;a= z7vkuYVC2APsp-jH!pUBw%HiD3&JF_qw;pj8FLpkI$p8!V5I^CPmWQ9EDpC+wKUY`c z1ii(V$`JZUf5cDGTw;~v^&nF>$$35G*EN6_91t3LR_Tu$e6z6= zi3YU0VFDqdmIS9T0qumLz_vk{M=9V27mpy#Eom4=?jtx5OWkzxo1@N zLRI#1XX&{X^{(ZWl?jgv0Eb%?s(dh^d6M#@We7Hf!=-7=D$R+S#?u0NP4P;}+b46ej+~MhQ_@liA z0C>>MoU(GaY5$X>e$$FVu%M7olcFLp=N~L^w(a!*%{P#t>bkYlnDzlT4ujzxmX<)1wjl!0=G=^|-NUg>O7re?I|3d`8RA`l<<{zclD1g~9>`4&ZNH(v0pl4_#srGaB`( zxG$OaZ`7tp7}$DH>MYwn28*0{hqMebmwt;6qjN|8qOSm)it8*ecfVb!+(jSb}1C?`LuK#i+)xcFM{fSr=}G42cLzhedQ z1SBkXhyoz4fP(`9*dY$kHlsx-U#9KNuWAG&=37$BRu$flH?zr6`lrXTElzLpht0hN ztA05*c0{Y?_pUvypg+JsLd-Hm>by zsIj^L;l7MXa0?~?WsW;8ZRRtvK_P4!o@lD^F)C_n|7gS4@@n@B1v`6)V1yP9P&*mX z-_C(PPu-#J-|hEMxf%^W4^cuK5a|Qw&66sk$d10Ei^ubONO(Qd48NjZO}rww#pDy)+x!1`chk z6AJ)xN}0YWsZ8&dzpfV-bK9Ws4F2teQc{G4_;K#Xn6yEGkwZFN zDigA_G?@Lj;P1BXwydqKac9vUhBjx&$w9?9Vq02Ts>ITdn*1(HNbepTHAfBv06jqY zRzkL1e~yJ)y4%OUa^o%J04GUdS6u1_5-?W}QDAVGmjEA;KKD|dD{hIVflUloD}hu2 zer)@!k>R@sO}i|$Jp0W z@H-?hYGFXqDLDG$>tZm+BEWq+hPdq>&jhgWOsgm;@h_E{R?ajA9c-qGM%XE2-vWr< zzom^ze&2T3T^YPxio2V)|I^e>{4}67BkelRUGUPwk)R{eQ#MgwIJ=jYmI(X)%zAVxSoi(69G~<{=l#g4$778Z3GwkB0cy%Z z>9aPiXDLRaa^Eg#k4LbY6$)Q;Sj!hfVug~E4;|^@m(GWlnpP+&%gZY(BiOLmQ~%Uk zVmGRXltur#_GkRt?sC%N2Fn2m_wxP^}x8&ybB~sMAHdOgJ^&jwkAKmDk zn`>_ezC#0cm5;WP1M)(SH2~1u%01h9RE`>RJv^kGEUpF%NPdAj9Ea?XYsRWpKe1z@ zT6V9m|0(9S*3S`1MRg`AT80Ds$PUS8wA=$}@NZ|~WVeIhYKGY)G} z*HGI9R>i8g)%}ierftU;8WTVP5KDVvc!{?VtS8z3eg-t~@8^YbdoxrKAQHf=-w74l z!OiI$pn>zY&s%ny9%J}4wbtPA4Ye&T4Gj<8V1xdMd3gGI()PDIJCEoa%&h;iXuzJqtgdPR9FbMT zQ(vAOA1mfUE#g3q5mxV8onE!2MD}8pQyNr=t$f89gE}}|56_i$P-V;TgV=EM++3%w z-Mq!!OslRxSyee|V>mci&Hv4XI-K6?dTOCwlfOJO`Ssn(1s$Ee9ABS%+5EzI>Z`i%JK!~?FDjdfGE@1khA&^qp*1y8dI}PU z*MYu1(;5^Yu4RUd>$yw`GEDIqxb@*>Okxi09SD?|s$n4ixSy%Z-y~b6cCmWl@awnZ zoL%0td_n_|BG#C>)Tu($)cw1v!~m+o)R^MiyGfWn9M8jc6VK=vt&gD1zpI}3(|bMA2lk_ z`!4%uUjtiP~Khrho&%qcO@_UdC1i!82uB}Ns$ko@C*?zK=SkmkPh%QZr1E43V z1knahb_#d^Ckn%6S`Z`SFEy|}kNQ(vP)*@2e$ct6Y;620(+uq+@kVgiGZz2bm6QT& z-0nN!Q-<3q7ZfO4v8&PLZoPc=7pBQYR@U?BEP2e%Q$oKQ2bB0-X5uz{1Lrz`HlJdo zATOUW2(ahv3^2ptfe8=S@;A=-nzY|9M`mp~KavhIznibocqE z>}&3&ICF{gX~i3!Otw@J4#0OZ+2P=2qb}CRek!B;MHNSjee;C1^9oDs+NrHV^g%3jGAyLP*0 z?_*p40~s;UXY2Tv&ulqkf)gJh_3>zpLg^3gmzz%EjGwY44v3yREtO`ICPHA)M=NG) zVO-9Fh6m{DPcHDS=R7!=#thl`t$YBASKQ#=S1-ZdYD@#po`3x{56{h;JU7MDW5H87 zRfcA4_?HZ4)m1f#P)ak4hT-V8uTmzkxxfj6YwQGBS-t9vL12^Pfs%TerNSnJS}r&1 zf70T^^MhjcOeTA2kZ#QL>QXZ3HZG?4YB=<&095*Ld1)rgzQJ7A4>;~a4P`myU;iUH zh6RLM;-+)T3wGLLGch^~mgfCR%IEgZI-(=bTFE4o=Rdc%wYExisaX9zQOz4xheorD zN|!nkubaTlU^f#}#o2~A3WyfB1^95QSy2%&Wsolb$7U%pRVUNuxOoOya)zZ&Z_Rmd z^kNI>`C|re*hh*+7d=q9dC#_aAQ*u-qh8~1_8;D5QxjO+_w#IZHr#|s?DFJoa~KRt zNrA)ktd+P%^}b~&kK?`o7x_cPpKfQ{+F}hjX&1_-$Ud^f@yWru15mt?!hOyuM zA*nsikP*6J4&vYX0X!AUvnGdnH!^^08ojr3DK@?v9o%nhc=!n&Vf3lttW;3Lss;V*!vNiFZM+Aw|U7ype!n_zuNlu(5Dv$c&=cE@Xt10#GD{O+yRK2O zI_c7|(kQDIFu9kiJdTrFD=1~STG_is85C5~rFb@|4XJpi`X6gkAPnh*n`+)3^ z{stC#JU>?J5z?19*^~s5e&obulQ+^zagDyqo8A5CL%E77nfd_QDXkL2RaFcyjzo4q zZO&41-%EUbci?3RGvt66S`v$!(Ec-Pz(Y%L#7@b%weGGrQ4fEkjawhQOxb2+gN0$d z$(01$nbR>fXbf&q>uSshv`=Jaqd|!|BY-92ety5NF^rX{u5S>elQp??OuX2|7dEQL znej~hQ;@u7K%CG;1OHJDwo}>$S1e!}E3K>@mSeS}s5pwi>I6J773xq|jCG%QrRSXI z%>*ij)m|(a47hZU)L4<;F8~^^yy0{Io^v36{+mc<<5(c;skb;BFHgG(gMI3A4ad@L zu2rj|u^mT(V;pSCQI+^CH$25yXSjlOWGETFG4AKq?o`(Uc<@>q_+YS!6@Z&a>t6)6 z#8*k8f5f>Sn8HvqYe4b{I_j`7&S+a*%EkN)(iaiJ=TFX0hM4zYzbwai$^XMx&8W!3 zIQ*0OCU3`6j&0tDI>R+ra9PHx0}^LdpNyyyMP>w)e8D$guKIk~8UBgOe{NkD^(#jx zU$^Vo;d(67K;YVnBG3nOTwUHg2=I1ra8SyPUonJ@JrlT~0lyk9viSSrzuyH~Vn2Rd z$T22D2v-4-YG1{q?3~^7JBGBSBL9ji<9HAI>Am`BUQx zuL4DI9-yBqc3YjYik+I zdB6^ybx<}8&)#*OsuO!6-E{LOdhIeuDr`%&@?pv9EAfK&`Q=d=UVfsKTY9LSCN{$1uewf0l zaaqXwcAOxkd+*w$g#O8wHwVs|#69*0{d5Im8< zp+OArLSm}Lo>0UtUunXjd4f^35&Ey&p#=Ht!OJX{FSD@x?JF!Soc(q&EDtnXZoB^< zn$G&K39pahqf_bb(OnXw!HrVs(MSnM#|Y_3$N)hU1V%_nNOw2VFh#mSx}_VQeSdiN zA8dE`zCQPy_c<5*4I*A+`8krcDOcbnt^6I~)*XHfBg&uUj>*Ra_N;oeFGis&0P8X* zzLEiK@f2#1;5mp&K z@AO^g=l17xup~WuJHvA-UL(};Aw1zYA9-Q>2k%;F09Ieat9j6p)IJp^8Q^FAPxQkw+4n^FS`Qq z03+R?I#J2kUKdGOh5tTI$;0)owwnL`;nm<88=CqVx6;_J?g#1AW#^SQ)6HM#PD!ZX zZ)fmfmL{0>W{`v2?=g(&%$tOHL1bT{IPcUZX}n@k*FJ+6~2L#IZ-&c>mfsrKEf~@U<7rYiFLIp-{EUStc+Gl zI;1Ps??-QVHJJ-!z!b2p<3)=RIP#@3D`_4o;Ns%y&())&uh=im%*?pR+SgW=?>$Sa z`ship>;tHVyb>(gnt+tL^LWrk2tegb9j6I1)2|=|VjD!rM_AgOt;B zBL1aSECVVuCC%G6>*9MmCAdxFDt)hj=XLRpMjOfzh}C$Etxy&tqaRRKkG@7pn3$ozvucGa}9}gh=hTGAP=b*KWCI1<==G|ZF0`6?edHeTRZNz!W(>~F8+%rQw>8y*{mBTC## z4fv2&zBmiUzew$&Kn`?;jH%EW^n$eSRi|Q_gM+a##sb}k)=sy6IUoH4@CPoht=X;} z`Uc_hMz&oY9Jt!Q-k3!Ng2nLqqb`sY=w)E?LczCVy{-skPT20kM)xo+Hty_s{7MF| zor1U~WFc*P^CcHz=GlRZsHFfkp9=qd zY%b61z=Jy27HXEAIPQvejlsPYy_NJdt5`6ZD9fK0j@5}dw^_Wou9D{%vw-5sK}1BZUIt)91k?+{@|09~C(0Yc0@@a6Ig zwc89mMU6l5pT5?;oXrp|`NtIeQHN3?LiP+-V8mW@LZef0Hfx9eGLk!Tt{Fwl2*n_H zj$i$Ugxvo8+sG#p543A9#%vbsS!FChO*Fr3H?HOHPElIXezQ9)+|hc&sm*)eqj=Zz zDdddZp7~Qh8sxFxlz3JM$mSVxdPe_Atym^&biN*a4Bn&R_5U6lM zqTWV8MU+lk@9@jJxOCHunIHuzh32ZNoLsbVPHk9Yxjl)q_+oUV=bZ@(RQLv0$?%QH zkr{wzxEg)_6^|r$ZT9^qe@ZO}ouX}&PYn$XM$3U8kI^2K;=}PL!D?%Ttob$L#xyFy z(zk6L9V{w3+X2tjm+*{h<|U@yJyQp7oW&%aIyl~64W1SSBkidC^AnIX{4k&s?I0sYOgB#r5>*T_07ljlzxE$ z!8C-}Iy$Gm2+@bX1bUZ5&^S>lM7TyVu$eS$_c!o`sHqsp7_@NvZtkz<)$`b}EP;&N z&EgUNa|+!_$~+Rbt-I4UOQXrWSnarP0qw14#BqhC39f(5l*Q(Kich0^UZ?ySW-qcu z?vpZ+eXT1|4Y}RFfC;;z;**V*(>hKKVfTFf$ugqP9jG0!r=zlWIUP0=nbaLzJ=1<~ z(MID(TR~)NsOLj8|9#gnJIEy6KYL~)g|I8Teu~0)t4oPqJb=|pw<0%FwUFQSz`tm5 z{rYqZFJSuF==`Z46y`~&Lg;^4Ct4UxLf1lJLa%Ij*CNL*ZAmIXPN$6W|5y<3axxGCwl$y2<^ zdyyja#J$7SO!j;IZ;~8x-h4Fbl=Nq|#$i-a*??v^zZ|~~A=lI|iK_0S&k`ysNG;;< z{>}%=57f3jL8R=B{&GJE3Gq7glK6qmR_$eIEizk*n0urI<3T9Ae^Qf)&Sv2Q$YWsY z_|cuR;Gm%tnue?p*XG;JlNH4yi`$*QRzeG+w4K(}f32C2pWbgKJy*tlv*-@>W>a6r z+J9rJ>WXpfy?T*CN=V3(aE8GZa|<#MQnwZ>6PB0vzo>aYbMbv965M3J1$ccUWIccGvOQn|uHt6nu+)zIb*JgM(G;|IfBh?@HRzJb1VuHm?RojFmS$%o zk>(eO`@2pC-@;9=H9Rpgzxh_QqbhW-tu5Wiz4a1xd&DGYAEU0m)G`8Jmv#U=VN3N&L4YSg z)Aa|7%i4)J=`X@zhqud*Xiu5sVTGg|P1h{+*ADpp>iS0fawOq2NP#6zb$#KD*dQ`J zu?rKbbcA(jPvnfAfu2Vef2Yh0P;f0jAyb0Ko}aytP3frgd}yuUSAJ_^l0*kq1{>#k ztuOFNWte>}b+NEz#m2@|0YuRzVA=qTfQCW_?f>H8bfglpTW_bY#m}35y9b-o_-@vl&?;?)Irw1GR(M$R2Sg8>$=Uo z4dyJTfA#HtpM{YzOT3~e)$#gJqEAuBU zU3KFj-`&-Pg|nQV9Pb46NU)Br$t6~O3>Xz<<($vyJ$+szK&UGr7$5g~_F1PDp&4al zxwJoq()hE`Q>oBXn$Tk!^!0WT7Hw*PEEY<)mtKUj74Yz>KP81sQY$&nI#BCq@Zz$d zPkx$?n<1JknNY_-89^RtfS$KN0@U^c1RGju`k{!G)n=pUg2OK&WX^K})8k(%p=( zBm?*;u^$zdFAz_D5avHo&y7#&^bh4~EIYnV>6cwrY`+eD9lAE+G2zWg{O4RK<`V#-i)j->((A|{vGSY^2s~i5^oI`;^`e?;EN{v@R%5PyS8tX^ zM?zZ$ZfBfo1eoH~@`~yGkKUUbQ67ES3dkv`M3Cov{Y`@w$UKvb^(AtvIXI%$2}oIW z$WWF`GO&=l5u(7y>qj@Ywxd=*A(2oIiXH>%9KiIgB5UqxS4rAi(r@rYSe)92epOdI zsqo1relp1?IyCF}N+^)7{-kv7L7AYvf{aXz>-@7gI(K9ino_A(H*v!}Yx$G(=RfbR z<8HX)*=y&x=z3Q!>*C^EJtL~vBuF&iv^Uo^uV|{yVJxFYSS2N#^=~xF?c4Pbf5**H z$e!7_(?@|+$h<%-u4x|!ZA}c&dHMkh=X*R6kS8~H^f&-w@X7Qh_rwnR4FubXJT(eA zdSQ7xce`iO0JLF# zr?$1=*KuoM+fdI^c}s%(R5g#R%GFkv($0hm@*dy=WoBf&QYiWz@M7tjqoXJWRnM}s z_0=D(n$csCdbAX^`>gg>9LYh6-rBq}5yujTxuueLwWQ3`^&5&|z60Z}W8e?woR<5w z?)GgcLlXJetBT>w8yBo~M_au!Df8tE7n1|GE3GlP#kPx29~v6U&4pA${m(6zq}xX? zd5J$ zJelmzfQ>&J-tc-xAAI0!nXXkBdg&K>Sr~d#NbLX*{#h<6w}l@qR>MaM0_@8*QziTk zG@`^H9VSJ&9Rxk}ri2^~cU(txoF7z#T%1BxrJcM(p9a2=F<$YEfDjFTcb$^_%o7vF z!}peY`-7yCI2KX0(nu;cdyiPRXUVDIreG}r4$8$P^m@b6{~Ea!MucW5HNMo1XzjSU z&Pi=e;snodY)7*TBL>jc1DbCU|0O3*9~bg3oW;B?z^CZS%d(TzYu#KI8>;iWm}lfc z00I*@;)X&^4$lHwEoYKHWIS@xK7Wlimtx^(Lz~y3p&MT-+{}qT=e$ZYw$aIB&=;qRYX2Nf|2d4FF%&ar7{Rvau$M#1ey2 z=Q(SNWpKzLP%GU{Cd)Pv`Ed1yzpeMuk7H4UW(*)sRH56S4nF-?GY6l)x%9?_v*qH|U5b~kB+@K-;EPu>C2y~$e;yy&QyLnR<_B-`e1X)c zym^&t!tvrVDhWnS1usp2)bPd5gyLi~$MvsyV!%P08DKb+PKgps6+~+h#P_jpXB(d0 z+s_<_vReIV`o2~wz-&!FtY67B69zHC{8GHpogK^|$nxvFe2#l+6oJrze$}1NI7LHj zLe%xD22i{3^7?2-Ez6^OG{4e|{uhMaoGLz^qJdx?%FS1{{>yeI$#$z5L`$+qvAG=R z_%wGlBI!41ZgZuZ|5xkP!Sw6LgI}Q=rzu(UNnuYJ7A(8F$m3Hg)&Fp~*4P_TTPXwV z$PUe$bugf=L_Y+wHR1S0K;O_&Oi|krN}OZ@YC*eMq)YI{kFD1s7m;OyeGkX0V>Y<* z!D;}tH{CX|cM{QGi$9Q-R?qu?Vo$as033CM+Pb?x{wOTJTftMjS|tg^54QTmM94w@ zH3NngEC!IaE`o>37%v@MT+&X3)2^e@#uXU|YMkkAVH+oLNW=W^9QNz(Au{C~f5D7> z9G%|gNGxSV!>Qj|P?j0>6S%|};tt;eQVw)~6dX)h$JApzQD+|Hm6pgUBLd^-y)S2_ zL12Et1A=ia)D`|VuB=mrhgHLQVzh#eyU#}KO)M=@n|DP_iv93SGU6<$7By=5QK?BiD3Ch@DD5mZNjgBu z&uR-OQ4UoXqkAAVUOAcKQYvAP;+(b1!O9gH(my>t3A!NDdY#Escslc|Vs=u;cp7($_28Mt&p7eY-w@_L;a$Qs^SQYdNXwTC_Iwq^O|KJ;|YGW2-A zS68J`e%ef2-h4NAQqs}h4wpKq{A(0os1z8FkKt-OgZ;WYZ0djl0}3?IwZkC8R#LL* zElo1s(oGrTbB}YX6ii32KM3x$mnjnT}Uw--bn7bLImh0P6qQ zK@CemuqJafvA&2+;G6vuw;;b}^L&X+51%a}n~=-ya7K_Al%%+|YxR&^^|=N zx4f!Pm`?Mg1GXBe*m^ZYE&VpJ51Y3?>GJPiQ`wf@VPd^(hMbbY>23ZK&L(mTjdqW6 zR+a?D+`Wg`r@z_!f!Zi`9Ivh>C7}fV0h)D8nL<(ozDW28Ckrgy>uC%#zJc-tyMn}$ zjK22LYsQh&v_%E`B)j|@7?eMnOItDYkYTy&02YUqzhb_REC^_h&aaKVX(m|zqLkib zry(%=wj>po#yANInS2l+B%B&HIQ&SNWKvx`^9x5TmhNvZ%*TWZA0)}kDVk@F!%4_s zHeUGcm)h;|_6oZYi(!~2s!Up(l}vu-%LTo*7&B9yMDtD?9u)Oao!=pIR*i(2jSmDv zslWL^kf&>51SRW6E2lDYFL}#Qy0bbHzw4wVTt6Pq)j5was#xjny^;nhRqFC>$w#sl z^TM>~03YA^A2EKe47$#@yk2fUWl;#-xUL?olnQSHF-uY6m>B%TAkT#gC{c~FRZD#| zAR|?t=XAR4>3TnR2*ng%gI2qaIxK;uT}IL7Z3m$;k%NZY!YN>>5^M0)-+9w`)1c@JO( z%BV%C125YQKai^MB=8hQAP}_$|Jl>0*4z^vfycc^8KG4}grdqLUry!v_dFlmGK3}Y zva1M4enZvY7qM=Q4V2W>YfsO1Jv`oa=(%$tlp?kh@CV8KzXR3*@;P|m$mH>l`gx7= z9E|^G0cNoRlrX{2sCVUQ=CrTe>reA0$~#s+kuBjVH7*-6$VdgRZX8(NrfEr{Wa7fgpyu;JP=rxkbMYom~EOEveO*Ra@2QE8anHe4K;y{vnD4$*Al!maxdl zf5dojskJfL;DG@LjBn6#tzbovt!)p8d&G5K@|AdmSgK!_(~K0^q|ZTHLl%QA`&KGv zUn&xUL#CGa1GZ%76Q-5+d>wx*Fx2{XKlE|W^0vY9x-ZhJxFLS^g7Fi>TbFI=^l~Lg zLL{J>SLgK1R`R;*pG`(ELv)6`2@MkuD<=Na!N9EqaI=#eH;y4IbVy+M?cYUy8lm=+ zzYhASX}5Movk@5TVv@-l;wyp4!0Un5c?^5F@ZiAN-PH?ikOMX3XHsr)0LxJI85<9P ztgn6)gs!!m^j4Cqg4E{i;2YV$R;12BGxJJ*WH_rIBZi!h$I*U-lw{ELXsmEZ@Yc>} z)}P-+(WOCaCyT!ASqwU~ISij!K9`J9sbgfSZIKNY*QleUOY5BC{NSLjxhH{Ae<)5~ zAh6dF8JN5>IGC(TI2MGzIHHxJEsQbQGRyi}^5PggJxNpcgs*LX-p6a_oQ~5dG@Mt2 zJeR6xNKOS+d$`0hO7*V=JR1VLZVoUaUx}3fV&3jgDLC&WisTTP0pzJdooSj51+V}R zQVNY1DdGjixiRs@x%d$aLWxCRumH;5=S54P!m2pDz<;ayb^{+ zusNWvNfPS1T^6p+gk;}cNRL}*RnhXik~G~mGD$|s+My?Xv0tXKFP~Zb=>4QCtjH9n zD%wYz=g&pSRx3(aW0GJzJ97{SGB5}kiJzCZ(z z&2KsFM0`s55GK3o)5A29mjdG)@R;IvM@h1ll;RariMM8Q6|4Y^99{}=OHVc-;Au3H zQ#?k-@NHtPqph6(>W)Ky-k5B|l6J zR|rfid_Jp85_Ov)UKHPNYp;PGn45vD0E(0EaKea6{kZz!vH9ViwS{i_Pgs`>=nb>x@LKLReL3ma zzUNOhSU37revo}&lnNO-Z1QO%)yKL!JvC@0;4mRgLdSK1w%H@P6jAWwCLSL1I#8|b!FZHgkB79KAIbb2@QM6EW$&y z_tC`~_*MAt5pzkTN(M?f@b}e*CS}0Tv&2qv)gm z^G;Y0@Uuyn_T%k~iizCc_Hjs9dnUD_^CyNub}!o#iKdv=S08*(xrX&(uZfBzC70|J zfX4M8z!_AEX_uJ)BQ{1{N6~p#LXpunc@CC3D@==6rrYY=D(YRqH$%2o&mjJ?(Mm;( zPN&QNMlObe`6sY9T0ru7`gab_W&;uet|;AS(Qg2{05GTCeg)B6O)Ol1lEH7PhnNcr zDz0gtrystH4R7NB4*Ea=EHeBTtTMT}(2Wh?k7Ae;XR6MW1FCOc>h$i4ed+6HmK?Ed zjKsG$a^_A9D*qtw`44AtUnD`m)(TbGSO9FGxja!xvDhdN8S)o;=Y}hmEIRz=n1N4_ zsuvWmxB2{r&IGz|=x$}0%DmEYolouXqwAgyJ%90d-0*lFDe{4#e`l&nh+%-(L*X804~JVgT@W_!B77`95@7&Q~7Qs^s^G4AFBBP2v(xn zL5z%f(CJw|e?vTp8_VI=66T0YtBNiuh@l<}Kt0GSUgJ?uWd4#@{CP(j0^!%rqZzvb z#y-kGd{=nve$xUVwKX8)zcm(U3|l|T(T&>-S3V4nOJw?EdQsa)fB7`(YIieIvGH!V zfb00I7Ou1QD3zB(UZ=C0gVAb0%wdBdI3v{oP0 z=ddt|Z#Qa6nEJe|?{yt)e%os0$^f_C>%1rRQ%hI*cX7w6__SkQ;*iO@x#@Ic`J}~& z9xDDkq2R}RDX9k9AJ&e@M0LaMIoC$L#RwZ<`)u3ylSU4;OACbdMbzanVTwXgG)h65`tuVsg(3gmaQg@Bb{RgquUB~i@ ziJ18n$_Dg`S(ovhFH4(wB_^|lzC*)={(}H;dAqZpp~KTjc|B_+#ro`T!0`iL?RHwX zGCpTuu4gFc@DXnQl+TnJc5r;O`X4RKXDw{^`V~b5I(3GuDwsoa|vHIiy&4 z$YV%u7GwP$zQy$v+Uf2Dh-(q*Bwnf`RtDwet3`i)NnK=Q+I=fd?a5rBV$-lC1wV`#1EUX`NJ^;T{Hu(7t zuo0m%Ow8Ko)FN-k!%4+M)biD)J-8{PsQ<-4>Fk@j=Nwj+zX4CWurM!A^nbSZ2<7sj zWR+Oe6yCKoi^LXR48|{lkq3}WZtwsvJDlCC{q%I#T>mFWW#(Ek?_Ycwt{m&aD~qB`Rg-U)P_uXRNy9wJuS z<`?Ep+Qutg1q~+O=&}?foiC+8pOmyt={<)5Sw7^u9wbzvH)?z+>>6e{+ZrM2Jd$yu zY~DVOGF>IR7LAMvn&l-e#d{e6Mm@Q}YeV@K&3#j+G&FJqdo7I<;^@MuT5c0%NnrL7 zGq1*hG90Hk&*7ptDk>4(w?v6$QuYlb<59^|Q}0@5-W|i{D;~A5osfxd<6q@pk05J^ zC+I7vM(1dZqTvF{4J{^xRqS=oGB~ihEM_^LNKq_y4~oq4o^d-$h5vbs$jFLUbp(AX zUTL*x>Z1`F9197&?aPzaa}HAZxAH@fJTh0#ju$Vx8^p~)48iF6o%+f`G*-kr@xgaX zXs4yPxVhZ+qw75Sv68_B<4jmj)++{P+X=0$;>|!i(QPz~6IKjmQi_<0Sjghziv-pEkz{{+{>e{-rU^Jz(*>v6|rgcV2gQSNY4c8uvCopHSb7rkeX| z*^){>1xmY=%)Q{v449-7At9RJcEtv$aTEibuA8~so#vB~u+3Q)h4r={P7jU#W-KZ^ zL;5~!Q1s4KAww+aY+LA~-ONTB*3OCp^XrBb1tX64sjSDkPUUqqs(%m}JEWLngFlVk zohpIV5{|AkOyPdN=U%^@hdSRb@46D(6-_M#GYmCU+B797Q-5L7dxc`dT#=?kMX|6f zYBn=(`2W@U*5Yy4(4HBq+rwz$HN;kDmagyFZs%tF1)qPwWA67(Vqq3j*XkX+`MtAL z)Xbw{VW=pc#?8v$p5eDN`_%m^C4(HMSWf~66Lz94f8Hk_yM65jmTIP)(A6I%ucq}H1rmq@ zOdS<3Axd6g)aJ>+>r}7Aze+xomI55NTj;hb2US@KS94n zXATN!>F}x3EBWFb^KScH?h|{qr)9{mQ9Ir=%=n*wBL&l<{qX$t8s5FVG$^(R{Y(e( ztM@}?E(h=3JJ;CHMt_3ZxIiaC+Up2)jIA15qNYmv+EbLj`#k*cgj0;R917iOwLQFJ z*{@5BI#i2IR>2RaQ?U%e6|a`iH>1U5F_@n4MJr4=DZ6n;3b(!D2F60q)BnWzdXBih ze`7-Y{`V!8tW~hEnsqEEjwlI@VLwhlTTX60IK+qGfu=xIJdbF!}(GvwTg+1+UR*nK$g9O z^Ii2{HUn3TOju$1mx=l=|l3Gb)*bu$0H< znAhUic*MyxI>Cc8<_62r#FFBmm5h|2D(?#_eTG^~x;WQo7i9ZOxX!4<|0t6^ar**n zgpricNsX=h@*{BG0hN?@^LRU(zF^dn3CrObRg`wn0@YPLSz=Scw{EWL5-`MXD17Go zB6zI-4vv;!*=?Ul0?N3aXk$cF$oL-TAZ*Be>JH(6v0dR_xE-5Y|$_Mf4j8ro|kir`=aY7CfZUj!R~>@&aGTF{=V+(O_> zJg+!zB9d=&#l=sAiDkvSlJti_=@%5F%AED1i~Al{AEVjl8HI@NKzYgY$WxsAw~uX$icyBaRb;VxiNx-uHRiCmmi0wEz>dz8PsY&YQc>q zYUrlI@O_Kn@nueh9^1ui;F(|0?8_;Kl#r+&+_Ca-KP_YFc|;{SW)XQVyBCTLo~qJ~ zO~Q(hesijlwH$jtgM{h@+EFOkM>V#zq%*dL)<(@}SPTZ@tSo$fM>>cc#4)odD_}n^rygnzj^;Z=7|-&NocEsD@0B{Qk--q2alLLgGTnaYbS?kKgMY; zUG#}*WFRX`@IgHVm?sQg%gWg#8W7m6Ya+<@xPEo9CY#}$f%^xQ(y@uMj4u{^q19xt zw6yS2$#rw;ovVAaRmzv~xPFqLk#PqbZVn}Y!D`MfEQMp!)-AwG1A^_bzFV#CnvPZ$ zwx93FjfUQ=eZu!NUKw^kTVIHg!jw_#cG)na0%-&WYNJ6J!k^6x>#*i7-_}icJS>!~TJfsE9KUcRXY_J4 zOyD-TJUjhK?+`h^)9H+%Wj}pW5o{#ZmE$#UVJHg%<-~dwiv)0%bWrf~EJ2dSXvBgvxBfsm%i~m6vMH+yu zrqxXU-ao-GK6gDrb?Q6(juzaMp9ofLq$U1MHh;KFpTYiFQuX{BA|p@7@cQC*XM1Ig zvF*Q$8sn)jl;7j&{?vAS-S817uvfKfqp**-!DM2<&IpXhO~`L6FHwSSJCIkdx!iK~ds|FOwdGHixW_InjS0-tHh&YxchJ*mIR1Yb2O z+tuCA?HzXOmnrdg^QNS6Cu(4R1ln5%UYb_$o3S611!~ECHhLlVe6qe}dpWB8;yL_& zbL9JmF?NbgI$0M;g&49*mU7gdw^zPW$V@UiZ3qPi2{$u+VLB51IU5scm|bUp381xU z$o@o&0Y{how4n=nRJ<`dqgD`NH6CtgLf^)^$qs7rAs6s7?sz@)G4Ihw7RWrpW~Wg) z8&iiV@Ox4);2YuZX;$qW=P0+^K?cbv;V2#KjCe;FMMGgFDAKcx4Umf&jxeDuA_Vfm zExbn5^aADgI-PHGWbL#-W`E~~@-++$!bl>D1k4**o z=EYH*BM#XlsOI&*B|l*TI0aVNH7_8{ew9|9Zv@Bv7rLMNI@JGo5-}rZIUf=PY%SiT{UPw45x~j{ zGD@|@m1nmYCHQZo2ELXL1Gvy-)FMHF0;0+l>4MJ3lhgR6P@0PPOqF% zJrI$DtYC9LE4H`sr#fb3d=Htio!6|{MG4SMb#h)ccEt{4s<<`zlRQVC9puLCk<;DP z-UNNe&v%FkYJl|C%k|cyGc3S*2J9iP5b>eh&w(9oOc{9@vi)T~4=K>~@(1|4BReLk zwPuxT@`W@W9^O(Qu-Z}l=)Pu4_^yOsxUmQ4nX4OnWrDYtDJr*(8GxI^lPgj~+feWx zbrEAGvdHfb-13ykT@=6BrwiI8b~8?s$w6@Wrqr*SmMIFZ-^N(4?=7WM9^kbeO26%`(agEJGghSswPwdV>_C)hQulDyhG= z(60dmlW9^(1ZF3Z1MN}%pI^SSI`@J-tq-l!?h1=18w-4_60)+2TtbuWyLNhT^&B3( zyWcKL=*(>o96;00@at29#t6RIC$E=4Dw$79Id-DbURr@F1WyqyhyOJEU5o0-R(EJU z=8sXLM_UD>CK!a@Put6nvRuzdX5>kLytxz?031hbq^Q)01b>wq)dno_fQ8ZAQte1V z^=hoK`OHAo3dbmY1~I4~7g8(&aRtB|VNQ7E09J zd#|nPnE}*AfpDVUY~UqZD)^!&q!%B#Ln>f54RW9|2cl;|Ol6r4zKoY#z%;#~k~UyPs> z-w@>vpCR#;UAKYA5n6ial<&HG%8II;49$PBE-QBVdwt6s#-Bp62K>q8fw&0eqL{ zm{P@RGn`!Xik9wQ-0IT)IcnP6Ot`)4mV43zId79X-6W@rWw~^x8#Vv((f?4xLId6~B-Nl#yXLpz?hz2?hstawy5)!GWprN-o^ zUtPZ%(!7AMqP#St{^Qx%aKGirJoZSVyu&jszpbxc{*No^4`OSPDfNIp&wV7XmOt~c zt@ixZ#c1EwK-CHd_*8;E22K6Ye#ki8+1VKlIC#sX_nA=w*|L?i7Ar+6RvO0a0V;VBLU`(LtVj|h4ZhuxY?mi&svu0gTdel|~A z4G(U6Mbnj0*{hWyg%GO(%v1(QC7mabuTBfIZa!+c=7AF^Lcn7>?NXQ4!hw+(pNHjL zBX5#!+rA$Y>@ksHx&5E#*8S)F2-bS(R1q@wK}K8^oA1~fJhRhM88T)}@-*UI7+AbIOp_xph1UXZ zeO-EnoiwqBlhaa1cmG(q0+Gl5N$B;n-oE)3T{2ZDbr2?(pl4cUXdO+ee`2y!8@h#V zp-4XIied?SJ9Zh{J0?P$pOir!J7F5l$xZhmXTsF!etGG^vb|ZZr{8FOv%oN>Wr>E_Io9J2oLp=)SZcc$yYu0ARj* zJ?=_};eKsBhWo9YLh{m^shCRe5htEH!K|%anhlnkehr~xT;i#xP|mV%(A9y>J0$_8M1cUPf zx4VVw7x&Qt5CAJc1FUQiPLV(nfziL2M$?vm*t8VZfJsAWaG6+BlRmsh+a7RuvN&C| z7Zk|jYWX@yE5EEI#gka3t8hvD3q6#JrqoX`frHXu-|a$fr`s-EBIiJO`e^X4M9pmA%X#Ce95sw{Of1lJZhJm zX<_#GJkjWJzF!dpZU|ya=oQOcj%)rs^2KcTb}WbN=j`ERxia9QE)tCGUY)1|O3#DU zl;EXRzo0Ry{5S5WzV z`yNI4h(Y8N^)0_QRn-TZHS_j(cN9Ha26LG20um-a7M16|l8#>!_(x(*0dMMay_8DK zU_wN^UL_4dPBFnVYLKeR=N%8R?rq5_S6AOY<$ZOk>l+cOH6Pu4BXoXIP6w-a_s4(^ zBYV@;PKOpC`YyNei!@G+cQT@>$KqA=0Nt*p>FStB{6ma(A$jB8UDQnAlA0t1-08!= ze_p^wuyz6EYxmI0NvY5SRLS!4fdTroIP4Qc_3e9*yfXHF4( z@a+m2vioHdj`b8@Wc$2VanPv5fUcoU;(1%7&1m^Ilx_49qa8RSR4iRy7& zC4vhW?(7F)gH{UKM2trk|EAR*ol?CX(C-X{!yPM-H)2vWlM5}xT%=Vy=nl?=lSz@o zBd~0p;P|zdkG*fzB8Uu}AVZICw=Zazd>^imV_X5H`s+M;;6*)H&Wo>+PV+zX8 zZo78TCMx0;P2~OY#juRZ#~UVIXr?juQ|Y%EH#R)TZBXZhvd3kz;D;Z?etPhitAlR! ze$<5>=Rv=j5_jIz{(2rma~Fq_%r>aPlG4HO8F}fV zq+r@?wn$98Ybd*3BbL zn(ad8Q?YI;o*g^^)yCdy(nas-K%8FWy0u?vKS#X0-H{Q&qf;gzq}4?^+D;Z30fMu% zw2x0|Di5Asafey{-rDH3rWPOVHC(>TnPTZD)2{(uHLgZ&B)ap%GxUO3Hs-wQ&@IQ{ ztD8Od#T=JCK_vQpN{&}0KeWOK{Kj=GM0Bw+DLR!qe8lw|wbg>3IoKAdyewZU+Ivzi z*}m9hA~qh%qvTNVqbnNVBJi*z?N5O-Sv|}BwgiM z4|9W|;2}M=jcLpw?(EZ!%gJTUjEUBxmJOTF`Y|n0M_|pt5gP_{wZZuK_@on+owLhb zW#yCI?gt05q1NXXft?t__(X$+j${_Rc!cH%a)NM2;2Z@J0z_#y0^9F?pdmu*Z@HVu zCxACE&G-p`99lZ6uP+?O)Ogd?vAtc_)@RRVj9w_&l4JZ;V6+s?icdK7)g}!mH%1V( zzI)N5p;aLi-#&A2_a|32l(chPJrdRN2NH4&SYLv&{Emw4z>a zlCO(@?D@s3)&%aCG#+&hPwEX=^F{Q|HZ}E^aj0}B8k{(ewP?A@(T`wTcYXJSZ3g*X z5-oJ&1+8BF@o~i%IheW91p^&Rk8Z6Q!<`w%5>shdN01lRu1oBe2E=%R7UB5!A;sFN9OqD7-b^K)vVTyL|eAhEd_7v;Q zR6{79Zx8;}*w`3)3ZI;WQdXa`ps*JbteKgZHZr8<%Zlf84hvpaxEC3C;SH2IP4WN< z+=>fGsbBu|f5uElzX#6^nDVvre3v)h@&lBCcuRYIk`{8pwhZcB)F;^z!>#%0lCRK6 z`?$(|zQr<8!e5^T_C8s_0dNxuG2(;mi|Z29D=|mBKY^qw@?N1IoTDH@Hg}Qxak%s} z3hbm?-j{uvHe!1fR}}d#Ew#a{%*Vc~FZrn1t;xJkjg+^)D$`*Z7aG@h5PN-x9t7;v zemU|og(GIIsFL+QsKpJyAhOFHPbx-$0jjySGWX4wUN1QPtUjW9F0eZ}%2gxkbiKOXE!L@e`9A~U(B|}&^rCoa{>UEB$`c=6y;Gk*a;3&0!xZ=K|xALC#Q$f_GQMG zuC&F}g^dnkh{mGtAi_knUa!Lj;rRGiRp*$oppDKVv$2#?ssqe?c69Xm;+Tq(nVVWw z1lP8W{u`TC)iBl5gvsa@K+IJllKqxq+Wx~FD_O^|(|Zq$yp}Ql^WTuK+@Zd6HYc;~ zU>{$MfB&QM?1G$`4>w|*-P+huYmG@x zF7=GyW8#J$45Sq{et*~eDDVxZ~%~4`qVKC zRgb0=6D68Mn;yH!yH6T{;7NJ-aP6HVZtG7a?6f8#CJCMFaliC!72!_O79#)wC#PN2 zn*E{p27)k8yxJ)+2K-Qqfs=-vi%pD8ih7&?Xn4$knd!J&Jg?Wv0iOw|>kaP2PQ-9# zt`V=G>a(_b+G*n2w=XPda6=;|&{Wqp88{x36+R9HZmOb2Km@bwaU=JxyM(aieDcFh z&1OtG6}RU0LA+Q!?`ApRR|#Lb($*)3gs4#C+&7l08W9XBcb!jE1WC!v#<4_wmUBKi zIZ?AP#5FfzuC?A>9-WY2EUL8?wT(v`oYtAWPVN>nc>=jvbZ}Wjh#em4u2?`4aU|u& z5g#Hm-|qaQ2R;mT|3Y57D<>-?MloOS>W2@?NB8aW%C|$yAv3oYoCbGtcWyNO)*=lZ zw43eQS-X;>T}|2`GWw}%2%2y-X-v83TdR{h$0zT8_&$K;a+!19Y&LUfLP{z6ud^*z z&N=7@bMZQJouhGEN(s8ifsJs$0WS!hq&{WR+xmbaz!n7e$Gz>F>J&}Ttfh?$^p?xiz84etFSZ=_YmJ!E5b>zXLhbjAxg+FWK(#JjlBl$9E~cE)sW zbSIChwso7^2^1jED)%YnQhyQCl>>eW5cE0gJZjOt}6Fcldx<+fP zcs!0iyM|{<`gm-?cDs|2u)E$DdsWRjzxvwQX0sWF!BihScn~JIeb)v1mYIM1^r-GP?U_KYPZX;BE9C)*}4==1sMx3;@DL#>uzI40I0SEkQKvGI6@pQ3o z*XfXj5dkzcWp6>mlZRldZq1#{ZY##=?hfqS;0Vueb#$qtIDeS;B$TrbHG_nhop+{3 zoCGaR^^pOuqrJF&j(*$~x5m0{#1!<~+txB5#7zi)zzfFYCK;?$kZ|x+(0-=Ls*Q;a zL2RLxvKtRT^kgeIqW1rJCv)67`g|&gsWBh+c`Xm~4}x3sXiVq)^kKM3^FvJgq41%y zeVOs4t5L$SYwkBNGqdEB)oi_917IQ+$vM|rqd|NB{)3!T*L9^7)EdJ7-EKFBKCGO$ zlsXP>3Nx6xxoY^)IstB0XY+6eFvT$t2-IpvF?PX}d^@>xRc}3&<|+U+Xf4BzM4SO4 ztLkcG4w!4Z;KcDfN4q%GG??%dqeX{>+iq1QCOC|U2opbnK`>{J=ci`OVzs(3$l*pv zr@Vaq?EHuKRsf=|O({iiMr@g`>qI0{cA2^By1ws&+7YXZ+3+jga{T!rEcbu|K6AwJ z71jr6a;zvEXgb1$LGVzMb4+OoL@mO+m6}l*gy+{IA-rHoV1X48ZFwSQP6TafL73!#Od&~myl?A7VWQmZ=fWa0><*sxvJiJA zRj_~sdlo=|N7&}x-O1T+_wK#7*9vQjmQQ%{67b{~5L@FwsCyakGk2{WDei6xnr@04rWp;nd#MF+3TTaHxYju7Tdi>_pG6%2nsIuyAP}Q*^Q(Fz z$pNrA3C1}*ZoMf}5r?juqOs?prV%0G>cj+Qm~pM=d7-tc*=*VswqJ8DL&RasNpI`* z+9$_}*qmM0scI=D+Tro%pWi-lzyU7-m|0S8-8Fc1jbk0Ks5(IN$tda)Z~ajK0I|TE zc;F$0*XXWU!u}B2%YXwbAl}viy#S%vV>t+vu?-FEg|Y zh?$z!*5#c#%5Z1qa49DyMp#c;w1mx!iDYWcs5)ED%$eY0e4Lz zM6J>djTwumRyaV4+62)Cbpj=s%>KxS@w8tsXs9*Z4~Aig=J)&_yRHk;?P|69%2&Sf zqaXb!Y|El+8|GhOn-I)|&1Q4BARTbPX9x+_JKDTo&FZxBZ2j7Z(rsPb`6Sh<2>@Wg zIqbUWp@Q}nPPndP50U8%=}Z^nM!ih;Kw6uKI2j0uIXiYxqfVkcb<7MdOf5aM<@dB` zpNf+BrJGTl$R^LPIJK$!uGYhO z%i!J~_vn+Pl*eIgA0=}wnxA@s2q_8eHdYF9hcc#{OYIkGKDS`^ar;=0KRewnqJK$FbUzXCg)uU+VTo4>6rG#$D9n5ZKeb+JZ-Me@G=#T#Bop;{( z-uJ#2f7^9k7*hq2wClQXy}h`&SlxMLMS^N>R!W5`RN-cG$QCgXZryW&aU{$vHJ;rf zg*OhdcGNOa1fbVCMy3IrtCm)jnl;N>;%Q*#d(%8>k~N}fghBvP%F!*Jyl|aBW?qJY znZrw^k!f}6AsfszoZAUZ#4}}3Sa<*th{P^_)1+0LqR&O{OnqT9Y2wcu{RT}AdL zx$$tHFbH;FAXC+aWF`q=*Rb0?&{A&QK#>PJN&SLT%y2rh>srRea%sVfHFFD=|3i-+ zCpL3`B$1Usps)gSA_5Bw5gU<`Z*+WInUrz{P)or9zceJJQc6m*S0j-~X2!5#)1@QAigu$c`zERWFjs3U0= zw@P?)zXAZTh_gSQ8VyVga3*4y*5>L=AT!%;_KD$r-&^B+AMjG(%Tn6LWSp4Y!=aYO zv1o0Xx*;qRk*@E*`s-i)*Z=xoee;{&jP7IbgKlnaFcC?k8lE4y7g?q3;|TX#rRQw| znWQj~4y1F4OYhmnqs}j?BO2|x8Un6?Gj{qF-&bSx&O|Dgviuw z8>;WW{`IfF`R1Eu_IH2xcadovf1;}M7LGvUVK^jY9&o@f3u4Y)H-~ZuMz1$@@P0)p zajimJ$(#^!AK&8B`3i zFAyty4y%So#?ryHZRUvcH*+V~Te}&FC3HU(c3F0hyhuqYcYS%R-*&(+21!XvskO35 z`yd>|tedq&x(B<9XenYwB(2}??Xv3HGLxTN)dBz<(A?)FYSt*Tsngw-7E4dvL;DGV z!#Q5nw;KTHdWgVtPOKuR>UKNB+<%rPOuVJ@?cdN?hFWV-ehGlPZm#1RDZ(jtA_CPi zj&jgIza01yeavRYpzybgE-6duyN;Q*+ijGsXXod?_j_-D>s#M?^UXKAuA7LtO@td9 zn#E$_Q%C_3CE^a@LTzyKr~=&xZ6P=jT^$6b=2B~Jjf(CzDSOGGW-*V8m+VB8+6%Ve z3AwttH^K5mZ6Ic5s#?$*9MF1Od{1JLW^~|iuSU|^y+KtlcQ!?vB(}>&OOtme;&2v> zZZWyQ957Q|vgDM?<|e4fIp-i7%_+P4B-%T7?tJyDUj>kJ{@(Y#7iu$1EuoxqM3)`7 zwg(*WnIL=&Q_Ayi2q;!>>Y%+lk+39|!KzbCm3_}e);*kJr)lw1gz3sYj5RYy=z{}H zpxL!+dg>rM5#ki=%-p4PJ?`$VmdTvruOF^1+^wzei2!JjB8Y5q+K(BWkrOzxM1fvm zm&4|fo~_oZa^OLJhM?4SbsQat6x{e^>-CkGh3HR-%2-4d!lCe+MuvOslc%;iNB8ry z^WpwIcL6a{wldJo6;98ww_`I)JdV_2>nl$kNjTtocXYgF2C4fVSC1a4Zn<&w+$LJ> z#9tt=*0ENToPy9`RY``RD>n}74Ix8MG?x8BM*Q%lF?T5FtGw~n=0tn5T|ErsWoQavJMU(;N=IX&B2tMw#dULuvDc5;)$@wV}1Ihbs(#z3(7=f01|VTd9EIUuRF?|1vK zlfG!qWSFSHGc~hSw*Vl_og^nxV061bY#x7|wUno8>koKN5Q~@^@9kjq>T;n<;UdZ6 zTA&pi#XwxV%`2U`Ds_?V3#e^bT?l4&DH{Ds9)eeBdHT$%)9l z7v|+k9U+y*G#H(EdV6mrer6Y+oaY(^!L^ze^s6Xr$!fj2WM)Z;_IMGJk~{1$7I+Ep z#gw*0lyYVkQ;jKnz|2amF*J8G774%Al+xF}_O(CwgFkrt?YG}}^UZ#-01*18a0&w; zBGIpn*sZ9Bi72Hk0Ng}aOPzYRa5tLhM5a3NU&*3tKoFi*uZ=0|OiU6-y}7wji!qzr zaLgMe&L2r`nHwvUzo1Wo(#SJ3)rrE(%#gjaNz}&^fD2yQ?GSfF7OTLN1(> zle<<`BB4y&11v3{fc9UdV?EC9x#sv>tx%x)M*-&_Z8dr~k0fZ3Djya)X1;R`5j zgGInB>XT-(wiy96|1OT!f{2ijtDl@6f9qS{di(9S-+b$>#bQB3raF$}+@M{pR&i96 zQl^SFwIqUCVKxo!5w?(S>e|dfxOIHpRGq<`qF}AHb#{|X-dkkk;3QDjQiFCNlZ8s_ zK+|orwpFx zA|yRsL3_XfFBjabj$`V&ust%jd*gNsJ!S!;V*v$0Y97`;j;B>2qhLD>C!B+d;6jPm zuXjF$)U@2k*#}p+>+%}bff$}e1}keXXXqyNY9rSY@qT@67CN)NG8gU+1Dvo-8BE4h zpg-SS4tn4od_Eu+Q8TSoQUXY~TfMpB@RP8`d?@tfr-T3`n6yp za~+XOR-=QG%z2mBMj=Uv(cg@8re+pvm761-LoLMpSQB*QQQ zJ`M^ZVvNK1!yo?e5C8BFyRHLZs&k7GleTHQ-Hu~x07n5A=W7s3tzzn9L9ISf!J>o? zu7FP*dsB~`9(S9wc8h_D;c9VgHM%WD;j3C3mmoa(K$8i=l>Rcw!F=|qg@?|J+@~o^ zOafVJRln`an1YCj+1*Sl5m&v99bytpRNc^$5gg2riFM~zCnJa``se0xDJ5!gnw)F* zZT3+s12+6~*%+%3?_=I9!KQ%mD|?Ff%PBR6ljxIuFeA|8vbZ)$&(BneT1>D8qT4_&0RRu*}#di{WZ()e7P^CHr9 zJuz!lpB!f#M4VC%yK)NCt|>r(-QCQGas2=O;UE6pzx($;{J{@uDRY`Y&N<4m%gf8T zuAK!9Knto3?~;%g2l3pkP8={ZQ*$#@cZHkQGQBp7QNLBKrKmc=Ioc53C<I8L5IY&n}o-h<+!TN};6zqcdF`Un?ude_6KmGY%{O7;8zP<)g$~hJl zu>~6WJ^%n907*naRA%@ETDHvG^$SV4l=7&`^?(Bo08~|L4a)!h-TJcZ1{g%Lxz-z zDbwnB92L7Y!xOVGa|=y)jxPtT)is)%aV$|NFoG zSJRNL0YK!LMREMR4hfMK5w3@3RBI8*bZFcjaKOU|j~7XKQ_DN+2VYyBuQ;V7ck=40 z>=bS^rRzTXgA$z`AMdW$PH@1JEK|qDF{L-|-`Bj^0;2k&PeYfqI)j|B=zE6XaV~7+ zi}@2?l{=hB$&LIt^@+0+M%eqis|QcgkR9;iFj+==Ermqt#XVnN^XgcbxnFE0^*2|% zI^o*zO^TS@8YC}klnzP_&a@0nVxxoWA1=!(Pj=4Ha?RN8i37c8HcOeB$7 zt3hvd!P9PMrsJsL!|INsqgAP@s_v$xe)qfI0sK$?9a1!ElUsOS0rg)t}IIE=< zwb-M*M`_E%a|MYA9>aWy#`=_!2moRt;wDdXcT*x0NoG)+TPK(Xfq+iGY|}r2dvzBA zh+%3@1ZJj99Ycy2_Ot-Pur}-|X4dRxvpGIFanLXffA(j8_7{KgpKq>js@C}IR8`Ho zt~1l&IoSw7dPe)8DWzP>7`bl0z>ajl0l(U)wSs6Q{B(2wTHiX6oJ5ZM<#OnXnW6=h z`Lp++9C!Pp`yd}icPy5Ro)TM){_5>fNYJJhXtb;BygEMGZMNoEERIgPC2b#ReYLX{ z@VJ^CQ&+$$jEIhN&qAYMWA)w5#noe`wgJ~&A8pz$*Wzz5IEJ*uCB_(eYm-Ks6?XnI9zdFD!lm8mZAbqQ0|@bQ{_xC}uLa5Es11}!XaYD7kT)l?O zLZ?>8%s>lvE<>CY0PXcbSrRd;s_GDeGiIr^QY>bfO%3D#k%Tq+99?E=l{vu~?&M&m zi`{lqjWRtJ1HrNlv0dBdW&q5*jN_mG`G5Qm|Ka}_h9T#az#tySG3N|m9LI;d(Vq?x z$vI>HBPNoRYZ(vbxCb2YL};x<^#11h=ez4WN6VgA5})S9N5eWOP4rxSTXSs7tEZ=W z&gPzrV>4j({o0e)P&*q>12py1#nI8pDGBFl^=j*?v?nlkza39K>Gik)>QMK@vdSHp zD*V3g-d$gmgNE#LgsYZuZ!KXqTwRopKFF`VPAp79Cuh5>2l?hoR!1VajUM+`KF7e=cDF9F=d^EAHWpN@Jap*jgdn_Bv$?AYUe~}XF|lLGz|6L zhqk^Upp>yW-&1Q0;Xq1wX&~q4caD!w-+lL;7xC!K-1YsVb#0WFaqze`E7xg%jojNg zEw<${O!Uy`(Z2uw```cm_iL>``N>am&M{t(AlWG3=2)^)N>1sUcVGKTUbt4Mk~_KS zSi}5lmK`$_H;z~&138oPY*1@vW-LK>HHS0PjKU`nx)S#`k_I5CGv%Cb*6Y9g%fD2uBJ5^e*VR3DFbRIH2)F3qr-6ua*QKt{V+=n)xj z2d3Qt2Rs?>uJF@t^~UmK!NLXx);bz2h1#~ANS^J>+)L$RQjCV$Y@KGm`#3SRz)~h6 z5~i@KRTXs^s%=IxYsTEoe90%T!+mb;?N?B2N3WyXoql!FEsO9K#viWl{ms3f-jsv4 z=nF^W8L8HNhs}Vgkw|{+4euA8vPrUWOuKdJI$tck<=#!^7oYmYU=|jca~1aX&ZZ8r z>C!a~Zyg?CM$#zTG_`5Wp7Ojzl$f$`X396$tx_q$v$GB{%D{^uJ63z+L^-5nz=*E zO9MFp>O>?G4Hq+YB@jUfu#Y!u2l~Ee=3y90DZAY+^m#-y3HS65MP%?0$6h;XgZi<#?Gg z=Pu==++U5`3Kudac>)RGDKPi3c%we?6IxhM z!V>dYzw*Q*+hrMkd~^TeK(zf_V5YYhwMTE$;iC^0AG{~46Um)Os;6he_C~HQ((*`i zuS5(y%L$qqc>0l}oSdC9!g6kgPm&R0z8&br9oscxDyg3YCSpP&N@cL?i_wA8Ght$_ zyxZ8vAC!9^MR6=Sqt-Hxq{O1ktg45W%1eS5RobSMbC+9Vpp61tYcETYx4 zmhgm{5#&^#QU+$ZSPaMezTa-Q!8*WR*1*Zh$uJDt?KW7X@dzoU8*(LN1{kO=LIlxb z=5aSyHJF1SNvc*;6^OM+gve~r)>}q6+)Q;cG294XVKuuwY>3n?8XD$cFx=G@ASR}1 z-r^1?b!!a~VL>Eyxtl5hYSCH_=d9#DrB@R36l^gU%x+F%AR#6&9Js0F~3PQnXdN~Ooi>dClbiqVNNW!4otU?;&H_U-Dcq-+~#dxi}DH21~itfR=T;YH#g<@ zj224)s1AC4QQrNL-dx8E10W)vQms{MJ(RRB6<$bbn{rOMn@@bvjMQ3Hwd?!V_-wZ! zm zuh;7}5&i7?a(nh#5Qx-GYe7}G3z50&eEfUk7Qj(!6^;f66EU2tR&OIYBajHIYSA&4 zq}6mHHnewYh@*(ez6!oIf{&#yT#&>^OKvFmbWBkpK_`^;D7@HyE^{h^24v6yn43i zGv_|#v%I_**TppwcR#&8eT-ljPP1m#C>N)vb74 zN8`D$D#LPmf08@g% zZKixOGZC~}V+RG@omj2HTutG(`3nxKdfP8;wD-yRi^z$%mSXN8^2vQ6F%z>{uovQZ ztIbOxvc5p2))?KZmWoz$GYb>9Ar6{{OS@h%0Gs`ZNmz0MvlE=?K`jr)!9l~Y%M+JF zL}Hd$)h@3t;b7+0CI?c^Oyq7%SR5T~H=C-OMKBSLiHKMb8oP(VAsX95xILIz*Yza( z`~)X_LUnw=0S5p89C&Yi`IDRbuXIO?B+De{i{;KrF-vYt^69qZjScnap*EeeFlkWn z$oJv2{m`G-Cs=$v>1C^XaU+uRew9)xf@>@94_AMG|Gn`bAp6q5cH7~nKVIE^Rr*Cr zN48wm(>vqG?{)X@rIfW_IZ<_g(#bjm8vMwrX_AFZk8dvgP|f-{8Ah8`?WcgEt|by6 z4LjWbcpQiN>KnXTCAbU&t}cdme_Yqs%$#%AxU~R?yRI9?@jyj*sqopCwv$ro7PS^L zO*uchPixhBdzx!`A$zu|IcH{$AcWA`&$|AZu>fGXT$WPedFQdas;Vk8UtM4G(moir zZya?XP%Go81@KyPKQOJ$!)s0j)5?%2=l~BfROF<%!Xy15a&@Ohbok7Av@IsC?7ThH zjEO)nGooPA!rYkI%q2Cp5dbjY%(J(yTQ!YK3b(MdSRas^6ES-;`3u$ubxDLr^6%eV zj9QtKhz!F3psDe4r>f&v%#?tfx}Zm-B*9UxrPQi7S6AWH0M99<*u1f+!9rj(K7`L7 z{VW}5zX?i1N)OewzLx-ZzySw*GH%N5?=Rl}_2auMPKlRY?#}zwz1_OFveTsYdUCOO zD_X@9yr^f&<70n@DaY|4LXl0Uv!}mccUSl7TFH*mVwD#rw8r>x+5C^o_kVVC|9Rg1 z0lysR(;Eo8pZ~ObAL({g7nScjrPMILhL>741{pQ)M-XQ z(;CU4W-R=On0SWiJOucZ*U2CW0a+dC;(oop+8v$Z^jw5f8F_tGKX`BVvv=Wcs>)Sp zufN8UaxQ!6^auRP!KddcwW_MHcvygy zGFkd+tS(Be!Xhax7K_;IL?9+%Y3qUb_(Iah`&M5tv)~O+dODbCE#u)VbHD-5g3Doh z+Aq%+M?L3`q-RmHomB$K947u48kwqDDbSw%(PQ^~_(w@o7}boK(fWYa0Ax=zLOS-sc_dXm_*4KYMEtg4E z);E0r-sT_vcKqLWg&pH)LQBuoq)})R&$HaqPjz4d>2PyvnLPIY^(xfPS+~qC^thM318ljk~pFSC$yllD5FJ^knp?PoA++6*254R z(A_uNZCs+Z+bw|bO&fGib zfO5{`IF92G*4b`B8b08F1D*-$_E+!!;LhT>J5D*#k;qBE7_5$_oKsv1KN0S`pZ~Ob^|fxXNJ)Ho;)_MO`Mf-_+7loRHD3j+p^5uR1=>6g-cV6YiBB`a_ny)U# zcYnP7**p85LS-C@XbNip0O6!u4}R(|30{b=ZInB*r;!g=IWzNSBk*a{hrP~UczZ>t zRdi?L_u&ZE_x(&oh~Gq8H}+tW6ow2pKwPve-iYQa1{!g;8HcqJ)Vw*A&6K#fPQ{O{ z1eTc>=k7tYAYslq^&NMeC;4v|AAf&+?QjB)j#iE4OYOnpT?mVa5DSZ#);2Vn#!Ssl zI3?_nsHQPy5c8k#IhtCui`qxe%tY;@Q9Ybl4mjX>F`5oq?kb&bKh;FWAw;^WgI!ZaDEyVQL z{($MGCw!slupPnA1({Fe+E3f5n2CvnDHC!Tv!EOAeX)~t+ zm!@3Y*$p>{wq4(c(?D@Q?HB*zj`_^IR>JBEBB+;q0 zkcJTvQAjK1mny<&2NlAB>*2TMW@@Ih^GQ&a%si^+$l9f8jDrYhp&8MWO(M)pEP`gO z&6GrPg0KsN<%0bGxw?1lnsZOUZrBBRqwD&!vr__Nc7d=y|_7U#v{%(#uC>^ZlET{_6c7 z+#Hg1KX*L)Dc8oqw%dGq#<}ON^W52DRY7!fnRXjumc>F+22Z?}$M&mF`ido$PqRn& zJ<-@x_bVa@QDS65t|LGAm_B;H*1CP|O}hJ9&MB8ct}gte_cnj~H|74vPkvu+k`fWh zIPMF34)~SF3rr&mHT^7m0uhTuarZ2Lo^uYOEv7@X>$+fwipX-gOey&kavOJuZ{|X+ z@B6OnS=d1-=MQ$9e|2^@xw}>^`v0@{W=)c0*R|fB&hZ#BYiM*gs!50hAcBNw5+FR# zjFMez8qN5mf1tOkC;le>0lmn`hf7eG5@jYM!3hu{5g>_y7X%tT*PKHP?q}Fr58K@{ zs=B(nx+`mF_W2BKR%Jy-ctmDKF5kZPTBh!fHLC^2@$%?>1p`y{w|_>`afQ=rtzMMi z>tB9rpPb^nL*`RW1ZK}YL1cFrn^pxdcfh$HgP8!q!2CCps(Ly6^t=GP`GOb`aV#VA zhc%lWa6YdUL1vVIA|~?V?b+YldautJz&Gcl?|Yy8rePRT@?U;xR%@L@ZJCIPa;fh0 zs8yHCrRUnl7`v|1ajEWOn9ocmo8S1mx1BUM8lMg|I~{iGuw2G25C`UtWkIqlxvG)7 zphi0sPM3GcWr7{-=K5y4uRRcrW?h>SVE|BT_01u_`A7fH0NsaBOp}&m^;%rLv{+ZB zb1k>Z_LsZ+fBo)H@AjML_|i2Vhq~XPSvWc(4!{9Iu=Q~U;LRCrH>gG-k_7M>$9!$? zF__ypt&U9}^R$(tvi>s z39OnK5CVe7;p&+1r>5?L9euH8Ml>pZ9Upysvl!D6#+S^SFNyqWj`}YVGt+r;>V`NZ zB4%Xf5Ksh!GvWX7{#)beNiTwH?q99l$bDIUR= zbM{yDap&%SVj~Rmax9JW?8pE9X*3!$S?kHLJBrI7B193nqYz03D;4a*k1#q6kY;-U zlE2j$*gVla7_3bdmK2Rh4;M4l(YmAM5pFCY@ z3m%x@t5G9JMEV$}EB|&ZrKFTnNn5A|em@azjErI5YD>BQwXvLL_vtQ`1sv z0{zYP@tP1lqyWK3m8BG`6-E_iriy0c8@(DD5F!&35+e)CoH7dtby_9^a9a@)(wy)& zW+>D-PQk{Buh-Rj90U;q5s*l5vzrhz@|Y~?ExvBgIJ`;-28b-eK_~QuW2S=iqML+4qs}`iMXX-m%9B!BqNQ z?%%=N@7wuVmY80B4X?i}%rfld`~>gc&AV0EX08%NFXF87G``vHjlqP#(lEUbc#Y8L;^{q5IG776Z7Ow zoi5dklVZDYw_fB>dZsRmkPr|VqWThC2z$amzy03ZIfW2$43{9aD#9XCa>=E5yIV@h zcT}|&B=X^RRrAX?L*IJ?ocCgQd)X)b>epI{*^T`ELVEMVC%vCWqv34xuq|m77sO1$ zk;8)HN@8+}X$FMT6<`|oPZ36!!Zcy?w>O%mYo0qeUJm{^Q42U#j#>3Q1Vp^nt&bPW zLU3xs&AfegIRB@6@BHNMyGfhD@w0^GTu$#x>_Y5_5Rgrxu2v;>S}SkQaeD?sLjN{H zG90HsFr6;Zyr&4$Z?Cxv{=;pxgg}Ub$Os_~e0EBAZsMI=nuc_IHNE;8Ui*R&$*||M z6S{vlzx~GMC;$Ax)v-pyc`8M_5231Q7(&;9nbn$0eoVXV2QMY~Xx3%cq}$Ma2($0j zTOMp&up~B6tsyWW7K5Mc&VT#(>KA<*n6VTC&zeG-kYb3Vu8u6F7P-4$e)9 z1CyKO5vB>X4=Gj0Rj~05V2oKX&t3rYH4OCB0`qw`dd%1KTgb)&GB?J#Y9A$|Cs4Xc zR=9xumwxk>6(Y*H5F!%+LJ%pnSQ&HIce@>cSNj4YbX^By79hvSX&BV4x6Ec1V`Szz z1?-bjQo`5~(`a~T$zP4e6JP-EZSViTx6r-_$W+8$3d>`TudJ?|54)3or&<(9#%$Wd zZB8;9-`Q+>Hycmdr*Z8^Jm?${V_*tUO|#Ytx?s89t(j#H^`!Q9^6tINXLxcqz4PX-&1)MU$+M8Q08nee1W1%q{@CQN88_Ox*}y#sAp{W_hQSw`d}>>! z!UYcqQ`J-ofa^V0)mrlQd;kCr^0w`Ny4$>Rd=0>=+hA$bmg5Pg7c9 ze{ov^fOog}HAY{^L%0a*Ap*iP)BorG-P55*8rQZ=tJacB0W*)AomcMs#6Znz%JXwE zquJWww{ZjRr*;u|&as40N@_ezS-M~Pdv*Wr?&M$Y zz5Vl(+fCPg7V+z%w%_(Y|1VumLBAYI#!_gtF5RNM`r2@GO!x1E%^BUhgXd?wTGRR% z)<J`yA5nNxZheD6e(Z5uGe3N z)lm?kl4QHV^HV&#pWgVl{hPn2+qRh4_$Z!{v;}Vj_=L<3TE8H^SS-xUb#1NH=ZLf9 zavfG`WswDQE=5ZzIjLDrgC_tO*hwz`_xo@E_gAi6kHLq)sAQ9eK&Of_CNeWUaEbW{ zcrrg6I2Cx@LAx-)BI-090hlh{)v@`(0{~~Yv4UNUoG+fb2a35tV$2!4(7ga=>YL9Y zu!$HGFjljF%Y0k(bzg}6pUAcFM^ytfE~QMs;L@O&|2$Yl?boK)jD*@oS>f7;a=X~FZ*}) z_y50Jzr49Usm)LS8N^qIiAVwJe!Kt0&+2a5edRYKXD*4BN3>X!tJmvtRnJf5u-^^)o$j`@+hMjeg>!acK`OT{kw0$N0(4E8UXm^FMsvMjbHuyzyHqjK9Ub* zzD_p3&w3++h=_oAp}7D#_{$4`PCbg3k=C}hEkwb zFsmuSnEz#SoeCa2v_F<(2Y-bzl}lBXiHT`G)KVrDt9AUu2gf9CM?Sp&c~m?7~^)^TuqI}b7p>XnCT>Z{pDZ%Z@%!Qm%{S8EU$5Q%!|$ditS5(KlkQ2 zN<<89kg{<y>MG?K;N}wS<&tyTR=lZZ~@G z_U>mtO}B48X1r44bI!Ay+I~6~iv<9rlyE9#a24xs@hK(GNdN#O3Nd0SIp@^(0N}f8 zK9%%8Y`5Ef7>M}qx4SPf|BqL%$G}8rgmqBM1rU%30kjr(DNJ@>n5MK&|Pr7K;}qwoQF)3uGp^92zl1Yu4& zABN8lCI9HoJ14pS&1*MaTV6ev#Z^gHd9h;ZXz?p?nY3(cN~IJi2mopb;GTV&)$IZP zzGL92-ERLkL=fU*S*$~cOv&n*rL#Jmmj1N#w}~Ta4q#CI@KF5 z6slTFO z-TLPmdKDSKg&D@c094V`Yv+~7B)t00&{WzQr}W4v78tf{jZUhwcTlTFbcd1qd+8%Bk^B=XnS` zY(nre+v*O4`3O8I(96S+00@Bzn1Gp>foVYdo0Gf$aQ|K|*~U-;O2aTutlf_6##ull z;*_##^)N1<><+`=rx1sq($WkXjpq&`h8UT}07QbB=|L|eAl}@ao~QmxN7ui4<<;wP zc`m~dbt{rJ$}+^4Vpp}6T8EN%He`Q?QGfH9PT6N$ttm`BGz=6acEnLch!wH7x~=`D zrt>pzv*I&l|h?Xl^u5_`i66(>B9UbrU%h2y(zr((#+*8R^3TlO-vzN{gfyqQv zL?xI+U94=mgvF9X0s@vo`wi`PwBO-=2ir}#e`k36&3xyUwf*D9C*ehxwg=G!RYcb7 z^=`K-rIb>7t=ifa)17X(MEf&Kt)LM}!c3sZJ z%$S*(4Irmfb~_e9#25nrl$@8#r5BO`fQXDC44*K?w$W%jCAzLd7C#+EBoWD~e&Q-2 z5|M#z^6-;;@4tP1?>DY}@mG(oUyrLL$qMACc1NLGutbzK^HE%wS+1oJWU^vo!#IF> zK@1ZHWHF42Zo5>o1a+fnuX$V2M$=C7c^=;1o&MX&&G$Da^})HUcXt~Q01%6q<&O;2 zZ#)-7B!pN>&Ii_2KWpMJ3IifWEUG%}cIBP7(!INj8^0P}`xQL8f{PU{mb6$<=xU58 z!B$6hbj1*AE>v?=#aew^TgL=W!@4wT( z`hxo!10N7M^0Fy(AiE34U53RNO&Y7B(T)>B_)1A1L7Ks_)KU{z5 ze|qJ{QWbKxQZ;9{-kK`9(45k~(4cAs1CQDrPikq-Re*7o%#4@;!NzD?w2P3xF+%6? z3f+M}0eN!h);dj8rW#BDKo%UUS_DNXMvBp5e5a)U?Y(z?vEOxF$0AHLu6?QIp+}^} zqVoy2kGgle-O%?)q*DySoEGf91t0D%7K^Uy_WQk`6+E>sX`|8jP?*^bSS&#gehRe~ z)5;<^w&D2u%IML5X>s)0(X|`PD_3N(VmXqE5?pTD=g|NiD=s1Gj(hY%z^eT73*UHA2+L23q& zlo~)pNf8i1atHxIX@(wT5Qgq8ft)acOP6dEDnwp)*f7@Kh4vP_C}c$ZuQ`5G=_ z>kjU-n`8fedqo5P+HbP)fEXl}yl#GiZHI#+DJbmEZrY(?vAIR_Y3CoxNIZ(N#i+Mx z+douZHsntC`D`u9NUfK$%(LbtGX*)wEXcoh{fbe65`%1r+v0|X%WEmk^UEVaHeKTd z>b%4v$elP>?8tWRjOG37D<%sQ>j%uwShT3)Q*dM%qy;z<-rGt$vz#^o;o-$Ucku?W zD=k@}qzVrwYzNf7GU?bK8EFOBlRX~gaEL3q0SyHj=5WL~=5*yI=FgPJ6xVxKs{wpG z;Tfd!y2CmN1TksF4-T?=Qc-|o?c_W|5(4p%y1g@KSRlS7;ZEWA*=+(V$D7U%So;{k zNgy!yvZHhbs%Lo!_tO!z|DzZ^qMOc=i08H{tdF(pj(@|09t+~J*SoLA?kgxFcdj>0 z?jih>b4KrOVD0v&DIKuK6x|R6LJ>IGX!*=z!`$4VX+!7P@b=hJi-LuF zfu363@1Af`a5YCB)6Gy>WT>GY6uG5m+6%!nMzRa_aTFRI$(Y&U9>2$rh4_!Vm$Z%p zUNUZW(C=g<{gBg$7kdTL)v#eCs6b9}{gDrow)2^#OCs|%FhD>U;2Hv9ub9wD6z=rA(703q6(NcS+dIb zPwfE8P*Z3|)dIP}7@Gc6XK>4L>{3@E`Bqdr*7-2%@2+Nd0SOsFTYO!Pw;q92a=rt{ z3H9)UFS$k2)0e09mx#CLPg!-J96oPegfb{alVK*Jndg)l=Q}*0orn{A^Y}a7_}$B7 z;sP5VqaO@9{A0C2h(@lMX2gBdzsj9D4arcd&eqs*q9C1dDoK(4 zLBIfec{?vR)dmdzN5pJ7cz_DJWd3m8PV41uNCDQgbF>)m#4oDOXUH8 z0+KKPue*tX-Y@e}F}G2#qLJeu0VW^!klNF@%U+G(%QH=aPI62U2Dni$@lOH+KUxc=l8iAy<#4%fQTY>lQ z$%)mO{?3~T+_sct{uAa|4rYsq`cQlQ}_OGu`TgSi3uz4#BsbGT_$$NyXolA3QyJv@J zem4lT2_K#x1@BFLDSV+2v`dfSBHGT`du-Di`h9x4Gxq*qkN3j-ZD1+uL$F*dis+Az_Ey2qYgbR(u=lX1 z%>7t&*oQD~Lc+hgO#s1AN@8e{uc@f`XY>M1ssJ-6;5OlE6%Xm$_7N#!?S{MHi{rs@ zu}fM|SY*=Y%<56bs3+lKM#Nlo(i_*urj@~U(mL4QtA*9ZXYL%--xhqwK+jMr#u;F@ zigB)d)W?*XQpyF}wt?o2LS_p~v&k&&f?dZ|#<+;p4RQ!rQz(NE(1y4R3&<)MXlm0b z^`7qJQ3)b@U_N})6FIG9@J5;w;ypcHbt*D;2>>X9Y2JqLW^98ARFq5w0>GcK&-hdc z+RBkMCQ3UVyqs*ifgBQrPQe>FFpMOXy+Zp8XAbP0m53RCgoP3*0%()% z*aQf}PxYwbs916+0}m+yBNaioev%J1_)MVNml_ruY&-%?h10}3 zblqosi&?UEk4CvQP8EZK%4# z_IBbVj}GXyl~8oTMct@S6bvbVub=qJX+!%nJxp&0aG;1017yVsL39sN2?ziPce4t; zOj5iueP;et-oX4>xNp{$RBmWr==YpQOtK6k7g-G>k5tXLOGlBTwQ}#-@%PWm%xhEK zd&g^kDd0~MmC-1WS$*}Bxn~QE!%~%5rph5U*cv(+<ZF-pH95IwXn@2(nth%f)tYMvM1Hj>v)0|0+hW&Y(|3yoqKM{Hgbu>om$;zrD>?uVp|L(tqP!RBU5(2VWLJH9Ma+}l$rStCk{V`#cU=H7bLJZkA z!Z${$iLUsib1xwz;>OG6eG1%Upyo=DNe9w zp?;PT%3`d*OlU*J<)(b%v-~Nuh^2kGS$U7>at!{Y05 zExwy`1zW=oe2}X@P0b3&10l-`mRZqM`Yn?zpm&X&AA)5geN*|m@Kq;#AT$QxTJ}6iU5r>e^nWb*o%`%!QtbzKB;G5} z#?j=*>yCxE@cw%4oIV{>{(r?yKABa=B0l zG1rox)#@)DgVA-YWt%0&u;I;;2|h7(S{*hh=v>4#L@Vtd@u6+kI z1DDxM5Y1U3dpKj2-*4PCwnR++46UlFVwB<2=LObMd zkGa~Hp}HJQjR^KKh1&s49kVcm$5*!FnDOrpm`7wOkUwD`;3<`Y(1XM@-E=eM}|QXr;3r)!b4ff5!AH zW@RQY1;yp6S!v&gUHD#0R7lVI>Ao7mC6SCGeLT34zhz;;MnSNUm?E@lb{mVhMy1IF zF0jq|Zem9pt}nN@j{}&nVyc`(;uQ(}=GQ$5rtaL55;nJK6nnIL`?eSNTv{y6g|FA! z|EP`%*?iY6F}!t21Ji2$2@rsv^;|zPSjpt9`zrsq&t&(FnDPsjL1u+W6^8d*xg%#C zOBbD4J*MYDE(%&!T-+%sN9ULDa!~_bnCEojw*Ut|;`H!f4*@H^5hZf5j!}z%O`qUp zpJ4nY+6KIxjr zhW6>#3x?_2>lb=aD5`R;Q9mnBu9qDC@>dZOhJ>Hza_X^k;`AXqCm@2Dl8HJYeGP&H zz{QMI*cdM%&52$I7VfYU+{Kqi&7e=Z9JLdneHVVDWUHgx1|r&HOPx72$#doTog5_E zl&uRgFX#rYL^Y}sr@abM!owoQ$ zb_e(DF5xR?4WYJgAh*}0=vL9Omkpt?drvd)?L-O&jFFhK`lE^MtF7&;BbVzVHkY~q zuN;)`etR~P_sOHaklVA|KxB6n@6gK!99{Fag=BITzZNcr9#`b`(8_27&o1v&zvq0m z$f?-13EwSr13%gIwWI9gYx(nKNi);LSz10%(}LMvrgZ8CAeuix6t%F_b4TH&*AME` zlM%bO>WfA*K9f$3v$bUhCnqQOEKg(CN4N3!`t0oN0MR%^-pezS>03!zx^OtjXSARa z&KNmMtN1bH+uN_^LbEJqjnhuowFL#U8#O#BBnrl!FpZw^IO2hGrlKJ;|DBkcr@1N@ zS8)!yn*=l6tIcd^x)1?$e%6m6zr{^-@N$XQs`xgR z?iLL8-B6sYNtI|MP=F?K&8uYv=bKLThfIlo=OF%7DQC>LSdT&SNJSSS3~}w1=14YbG&s7 zwxpTIGM8~%*fuaKskH%AO{5P$5ot4B4g=}&p8-eY^m3pi)$uFO;>AjzS`Xljn<%Dr z>s`=MMbx^7335(8jU1CV7;d9Fo8GQvVLt_eYcQ zEXjL0b;*4i)Mo!I*$K;^`;k0JZ(ct2)2&}=Ki8&#YZ@%~i_0T&m(Zkcw>$ajraE`Y z!-RDm!BKoVY;+DCZ|`N3_jGuhfZcYN@Aed;ul;frE$aObk(TGI0N0=6#_LP`#1R2s zOHD!6v)f(q>77aZ*1hBPUn5rrEu~05hdB(xqG69?o7szHDuQ~qy!oIHSM>Oy$2Y5d zB`O}J@y)FykpLkK@ZG8n-5U_G52lnuT0XUF;#OeBC#}GOsA*a^ALQt=J@x@4_%D9b z)X$#Ry9DoNl{K6>#1C+GTi$CN57dtaTjB0!9({UC)+mn;DVg-8|BfjRx1aJuSYf#_ z(}DGjMCh>%B}3xF$aIgrSA@2=;ZWXJU6D8B{JiW}noGqx4*i3wHu`WzWfqrbqm5gw zz1u9u$VL<8b7vua4~w0CWN@MTR#j*Dbyy6j{n z!^4+Pv+agazMh_*3mva#ukoH(OHz%e?IOVg9{pAtm9|UO)jf|XJhFr}L#6Mt1A+8! zTw#`~5bH31mJgwM{tMXu4Oex6$5-yR_pid(!j$NihZ+Lh9byx`KnyRjrTc;HeKsE3 zzpmy|g^NKjK$NOglxqF(?BL+wXmhfn!(bRWS{ih@ZYeJ^>5Zx~6ZYs8 z-g7W8nBG3#SJXh&PCHjlf(P)n#r9n7%GR6S!++hSvmlaLJ$3ITnO^mD7#_ErHTp9P zX@}XiiZ1G!HKgfo&D2LnLOtEx>ELEUX_un4cv;*v|S>(u2fH3~kG!Yy9Nm zt(mV*60Y&>E?QFT|EA69Y4G#&ThuP!v&G9V)U@y&%;*dZ27l3^Vm_()bQQyDna#;+ zxoEhDVCM~aIJ!keV&OZNjaT51XN^z4yo+J#E_`~}7}p+-8Y(fY|G9YID%EVS|Ja@x zHBQ}Dim{2mTJhljgv!p&zSx|-+LXT>lt1l}{WaQINbD`^|sCE`FQp^m-)zz-=_^&g}g9ARY?45zPhh9Ag#T z6iXt{IDT7PtHNo2<|+NKxIXXtVtHS_oq1YXl!{2ZjhTRJnecO>wPdq!T3m3W2o@&L znz&@c4b|Hzyk|$1mLoSnqsv}X>Y=y+9}@o|i-92S=Cpq(3?==S-xCaajv8n@c#0>v{ux-6XRiH#Fr>p-O!zMQ&)aasZG_^- z)df;?=mVpN_mk4L(#NBP#KB2Eo3Z3ihu7q&d!%b6yn8!XZSUl(?=%Xh;oGT%0Wc&v z7>9Gj!Z4}b%mwP7VNoPN{hw~yj_v-#{Nlbt|4nT(qrUyfVfs$b`1{NW85vA*QD`U0 zNp3!b1;!AkuX*^((&%kmJ0VyTqdYsf=l}q-(pl^L7pMLrt+#G1ahhQauiShTPX_*| z5b)4(gGZ97xgr7A{+^Xzxh&v=LG&c3;MmRA#1A9n^5NeI#J9ikQAoVgdH#6`-!jon zN(;3uNu9FoyV7ZEazhb@$Trw7HRcD)9|XYd%$=QgZA+*3eKco~B}gN!~LC2=)X8ue0N9JF;| zSAs(HUABY@9rzA7VgUrqOUpjJhFOU`t{tvzpKkPP>c{$|*UZM85b`RfVQMMW@|c6` zOf$~rHgWu^bp`iWquiDO7o3BgCr{xW1U~9T2jP181@S7bZ?^P?Ul?+Utn_M173pyQU6jX3d1R?JAZ5 zZP1e*g|~b!;)RKK3s47f+VHmFJ8+JxwEzp#Z^}|LYQcb2$qtFD&2Dg`PaH=6JEtqT zb*c3}J?ZSA9;RrG-cn(w?2UXBdQHiDMl|C2`?rx+zp$ya_hw{?!O9!zV*uz0{EooA zH`;6eZnxK(izZr5D!iW48^WP+&QTgnlv)6w_3+@b*=z(i)EOyV*a_C96{B@iCYQ5d zFQx{D1W_RRla|cPPS$vsXa{Uq>r;RNVz1A3sdQccgTo=8Xx`{q%J-28ubg!l?P4+r zW#UEk#$1pQm8f?Y8wmoegP-Yn)f;`9nnDk&Rr4+TDQzJB_%Q@kT5C8AaUN(e+nQ1|~{`^o}GKo&6WI z5Xb*tlAfP_pzd&gyLFch0K^sG4~AhYR@AeAF_g3Jecqt4&dw9gX(x&bwwpjrscG4$ zfkfd8Qn{ZmTHH9|7hB$-JT#!+VM3R`8qkDvgjIbv0n}GRV?m>qMW!=zmh(+>HM%6r z7BT?ho!4{#?SERYu8)PhkEVa06diRc)Y<+JtaOkdDlX2+$$5U488`_Es&%F*@e(hU zSKJ<8S7w_sI}c@+5Y@xr3f6%pl#v69phhNkW6!NI3$#y% z?HK(FU^JKU*2(=|xQoQn9K~eV{UNPeKofrL$r%bUaqHzN4fW=J<$w&N~Esg&;n z*Z08G%Cw>8qXhZ<7E#Q)zl6tLrA!IEM`LNG;ms}hBT|{%x_rgGmoFN1p^SxL*DDnxV;rjN8`WF+MU`e7F(3rXsD`hm;~ zGjB{BrXFsL%dB(y-+pt;kQK6zLrufoUJeoiqCL`N6dqe)m30tCUKU#>UGjnT&fE)8 z8gWZ-7mgS8H9@7~i~26 z7m1;babx8TD2sH#_ew48!*uE#=;XY^gpDZ4sy-pN!L&0>m(Jx*j0wF84%r>X+ba#n zLI~BRANOP3)X5b&@C=`!Cb!bZUDHw)C$|5cZf1{kR3^TulY>7U-Uen<&{zhzSaz)` zK@yagI%os*=^H68Lz3qcqmtzZ07A88^*6q~y7?&qm}>+kHOU_CWzOG%Hq@Zt_Ka)q z-?rbo378q%5GX(ca3)6)2&6feMT zkP^RW+iex|w(tzxdH4N-dmb4q&#lU|zHfKJJR{QwYjpm1d4B5rt7%%6rR3if`3$+c zKHcVi$5|iQMFD8^CiNTjaNOsWrO>;&m#O&W=1l?|7C{c-_SX<5JVEGpjU!v@h^w{( zfb|+08b<(_tNyWN;YHL`XX~|zM^B?=oNH1_u7emuuNV_#ebBkLhH{rlMg}>^z3WB? z4l&_%uaf>@DLP19_Ii}B)-I!ZDto>E!lP5oPEIRdg}9Lta^#Wle3P(BTewi2ndyp( z%5{T30B2;{qFlp>XTR>}vT;e+PyUl7s4lGTGJJF*r#%H}Xv2_Zf*cmNizSu^dcMFs zdc{im8D>c?6=iE5i(_Os zhFd@}|Ad7sPbl+3Wy2u`KbStwAu@5erhxp#ynOxjOn|*Q`exBeM8U#BimrA<=h_*M zx4BfMyzg;jLiA-RJYKA$VLPam3XBHYZbm0LsfU1uYrrm*B%mKs4 z7Xty*&S(m z%HO;Xutf)!-kG-lv=A#ZuYvcC_fwAuMp?e4b|=Jt#BU5 zK)?EmFK{r)tvh(v zoRG^oGWk1vaXawxneAiYl^BFFq{nT54gvXmt3KybgOQnp~TSIL2m#1*KE=X z&4&U1CGigz_dJ};PuWR%K;@Yu*;sU0*C>pmI?~gm6$Z8j_YG$9m|OoEBq8bp8JCTc z&spfsit{Y`)jc|v(^@tEZYI3UbMQl4M`=etdegfYt>w9Vw0Z0@b1^a6;Jwg+Tt6Ie z$C}|q7%D~%m%Tm>`^zC$c|Mb`?FP@@WU_h3ykvgWDmnoxPW#g`)ICSL+zUHCp&Rh8 z&z0%2I${j?$wZhAhKL1ZjHy%z6GAf4BR4|n2=`WAtKt=mn7k=^I06j`ELV61aF{NA zsV6VW)N`o$?=KF{ebJZH`cld?D0BPZib&*UZOTD}+8-qk$E2K0(hdp17D~$1J02f~ zbTzo)0Iw4GHUu+#Tv<0cvOtc{(h95ZHE+K~P4UkO*pxn0S;YS~ytQ?`+d`b>NedGS z1nTSUz1oQ79T9ODYGG(H)}>zFwq=Mi*(*S^%iCIjxDqT9n4C13Qux;Jx9&7 z#a3aVZZ5c?_##*|n3(thW+qb+f_l3lC1hhd`Kj6R)}Lw>zYRU_?#L1JS=BrD|KNTe zzVf1Kh_o$=OC*NEb=fB|xun6*plcm3V=n>&#GeY-W4Js1x|8UU8EZ#$ zcNP@zT)#i&l07iqV%*6V5#ipqFP4I)=x$Bd2CN@#&Yoe)I5{m>@j~5@bDir&ysgy6 zhGVJuP5_=DNj@qvW9pH+d6e}x+uWem;(+Vxc!ucpc+{b+*KH_KabZ*Yt!Y{gkm6G; zV^U}vYVrPnt6a29|^G_pS)S3WS8@`uGYKiic*Ypp{DG* z=%AnDwupq-_@H$8e_Po#*LlDhbJLwh%)*S1Kh^%BzX0w0uLKlTr!{Z-yX@|QWVfKi zF33uRL6%Lk@UK2Iftoi55eEVr>Ex1(eZ9`+f`z!XK_TK&A0$Sa4;r}eS_(=C`Itfg zXw}a+$|+SYpYhaWKmM+OisV)y!;E;(3bXv6jXYk>4U!qW;NIt=3o zV%=FxH(@=iG6p3Pu!g#fe+mAkRsJ8|?_B(%X$pKUWZ;cy4l3pQq%3gyWw%f)o0a9( z_QH+wEVl2C7mWhC2G}770~2Okpxt%Sv3%dl$E3|f_-anI8J-8EijbN;`z+D%u*{>n z8IKxDJqf75fsH+J+%={9Yyl(@^3O)Su1scHcKVxTJ$)B`kLg#E&fJnD*=qS{X=hm; zHx5Jg3PgrNb6XGf?LH$6b!o&1s}-vQf0s=C-&Zc1sG94mr~9M^w#mY00-q`fS`!Gw0N?*Gb|&hcP%7+p*0aj?xDfq z|8?5goJA+8Mjcu_jNU%)LF_+tejS%x_PmZmvVNW@MCPQbaxOJ{SuQRi=so|(k#G`~ zkE!mfKcfHp6lMJV010uaT#NkFyM=R}Dp!g(kv0ZRFZm8LHk%v4^piX?JaT_d)_>u+|jC-G6XdK zL9E$z2pT3bZ0>(j*I}~9Fz}a^smT00Y#nb`SBG~tuiv>d+N?Hr&y71WqKQmFG@?AT zXWKdD%GpE#s5s-6@z!})g!~tm$`a+~d=GL*t$g@UhQ@1jw{)3$bE!nTU#5!s+ z9r6=b&5)CcJ-|U;lB>>iCy;dtjmUPx*Wm^C&~5bB+kA2uHZE%P=_MHTNGW zg;2s9as|}Jxn`7rO^9%)2pX?0KYP!{9m&Ogt0T{E(GV{=|KB>2`=_k@16>}()#HGx z%x9Kilr9;j=Ke}{`{YyF{ew$!*%{5Hu&HlEiKv*Vz-$jOz4QyL>wBbTZ?#I-zZ}xZ z)K0#pDTa@lwU&hLgBs-EF&%|?95a`g+7dZ{K(E1jC1!axZ{!KpErhB7pq{|EH!A5F zC?HXJ4g_=%IL#jCqO&GH{s$qJ6PPq7=#nC>(9sng<=-ef9udNiZUE13f}yN7NN`35 zNKa$6!d}Q00T-r4yv?|q?q?<(H-ul(SVN{`@(0>pU=!jPca6Mur|SQ93V(F&F@YHu zqkYBtxHl?vuB%zR9PaxpnJdF z1aoGt5{9x*z5kEF-tSs3YrMi&BGO^5w7mD|;_&?5%EptOm3w~HOFe1OuIiEdazR7@ z130sUNiL1ut_BJf>1X^qsjZ7~GAwIrPvHsy!3@A6@z?fyCN=3MQ1~ z|1ZnJtCc_EYL3dT*2;1tzS}O1c`1~ThBDYJuce}D+FQI9BUs27;9U8-za9?h_q6Wl zv$3R0UtDkIXr@j+q*CDwPBAiw>qA45x%?GORAlh&uP~3Ea86}Nd{qx{kJ%?raatl6 z8Czd|ZXiMVr-@;7lNobc*I9~@U;rH?2uGx|_jdrGt<66j^=OAeVsa?@$kM<#=H;X$ z_>wZqnD_-*}1koK=`Y}MK^ic>1Ip;j6$34jMt$xsaR`$z|~>nyT8 z%2~aTp}bwrWJqILUZ%`f`xzw=<||kKzjr6sW$~9?{w~?9nO+s2MBVPz77-w24?io?KhseLa1;};_zd3O4PQc>;tn>!~!$4b4| zcJbAh4Al1#;lqgnv;%3stl6zTlM-3}h}o#jci&<%k2w}ZwrrFBd2<(MOM=C@m&xQR z7Ve!#ra3d2@66z4=vGh(JDv#G#t_mz#=HtJ8~?kOxLpAY)FCAX5R=ZFgEC-&LQuDu zhc(r4^!N?Cz6$1okqOhR*2quox!bq4M*V~;QZ`jJza33E0JwZZImSc@@p~k=HbKO6 zK-H;K6b|ZC{~-f@e(b>4d+x#X!NEcDUZ-cB^i)ul>G+#jUH~NC7{sn7P8{@52ns5< zYAYN*{@=g9D!XVwiu6sO9R%c^w)r4pfz(5!R`Q;*PFRaIEV}5&fbLgf(Wz)3u%GAC zZES7%vq;IQ2#TUGtt{)0TK)Jut)ie2&A>79m8!B;}NYZx*S* zfOkdC3SfE;H#hoIpCDx!cDL~JN~Q%0HA+Paj-!jItf|X@9^`X_<=tJ{vHn8jD;N?k zwDHDUlt0o%5Zmi0CjX%A^?cBD@DcXuKXbikeldb<3_3retq=PCrdjL#1b}~@sAxk= Im8`=5A1>1DyZ`_I literal 0 HcmV?d00001 diff --git a/gui/src/main/java/com/github/kuangcp/tank/Readme.md b/gui/src/main/java/com/github/kuangcp/tank/Readme.md index de37ffbe..8db122ff 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/Readme.md +++ b/gui/src/main/java/com/github/kuangcp/tank/Readme.md @@ -4,11 +4,18 @@ 1. build: `mvn clean package -P tank` --javaagent:/path/to/quasar-core-0.7.4-jdk8.jar +![](/gui/src/doc/tank/main.png) + +![](/gui/src/doc/tank/phone.png) + +## 2024-07-07 17:30:30 + +- 新增:网页Ws协议控制玩家坦克,模拟手柄交互 ## TODO 1. 碰撞监测 1. 对方运动算法 1. 网络联机 -1. 持久化存储 \ No newline at end of file +1. 持久化存储 + diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java b/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java index 437cfa96..48c29bb5 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java @@ -27,8 +27,8 @@ public class EnemyTank extends Tank implements Runnable, RobotRate { public final long id; public List bulletList = new CopyOnWriteArrayList<>();//子弹集合 - private long lastShotMs = 0; - private long shotCDMs = 168; + private long lastShotTick = 0; + private final long shotCDMs = 200; public int INIT_MAX_LIVE_BULLET = 4; public int maxLiveBullet = INIT_MAX_LIVE_BULLET + 1; // 活跃子弹 最大数 @@ -139,12 +139,12 @@ public void addLife(int delta) { } /** - * 发射子弹 函数 + * 发射子弹 */ public void finalShotAction() { //判断坦克方向来 初始化子弹的起始发射位置 final long nowMs = System.currentTimeMillis(); - if (lastShotMs != 0 && nowMs - lastShotMs < shotCDMs) { + if (lastShotTick != 0 && nowMs - lastShotTick < shotCDMs) { return; } if (this.bulletList.size() >= maxLiveBullet || !this.isAlive()) { @@ -152,27 +152,19 @@ public void finalShotAction() { } switch (this.getDirect()) { case DirectType.UP: { - Bullet s = new Bullet(this.getX() - 1, this.getY() - 15, 0); - bulletList.add(s); - LoopEventExecutor.addLoopEvent(s); + this.shot(this.getX() - 1, this.getY() - 15, DirectType.UP); break; } case DirectType.DOWN: { - Bullet s = new Bullet(this.getX() - 2, this.getY() + 15, 1); - bulletList.add(s); - LoopEventExecutor.addLoopEvent(s); + this.shot(this.getX() - 2, this.getY() + 15, DirectType.DOWN); break; } case DirectType.LEFT: { - Bullet s = new Bullet(this.getX() - 15 - 2, this.getY(), 2); - bulletList.add(s); - LoopEventExecutor.addLoopEvent(s); + this.shot(this.getX() - 15 - 2, this.getY(), DirectType.LEFT); break; } case DirectType.RIGHT: { - Bullet s = new Bullet(this.getX() + 15 - 2, this.getY() - 1, 3); - bulletList.add(s); - LoopEventExecutor.addLoopEvent(s); + this.shot(this.getX() + 15 - 2, this.getY() - 1, DirectType.RIGHT); break; } } @@ -184,7 +176,13 @@ public void finalShotAction() { // 协程池 // ExecutePool.shotScheduler.getExecutor().execute(s); - lastShotMs = nowMs; + lastShotTick = nowMs; + } + + private void shot(int x, int y, int direction) { + Bullet s = new Bullet(x, y, direction); + bulletList.add(s); + LoopEventExecutor.addLoopEvent(s); } @Override diff --git a/gui/src/main/resources/tank/client/index.html b/gui/src/main/resources/tank/client/index.html index 777ae55e..0016171d 100644 --- a/gui/src/main/resources/tank/client/index.html +++ b/gui/src/main/resources/tank/client/index.html @@ -7,20 +7,21 @@ - phaser example + Tank -
+
+
diff --git a/gui/src/main/resources/tank/client/main.js b/gui/src/main/resources/tank/client/main.js index 3dd8d7a1..4904ca6d 100644 --- a/gui/src/main/resources/tank/client/main.js +++ b/gui/src/main/resources/tank/client/main.js @@ -7,55 +7,97 @@ * For more details please see http://phaser.io/shop/plugins/virtualjoystick */ -var game = new Phaser.Game(1190, 590, Phaser.AUTO, 'phaser-example'); +let borderW, borderH; +let directX, directY; +let aX, aY; +let bX, bY; +let cX, cY; + +// console.log(screen.height); +// alert(screen.height + ' ' + screen.width) +let phone = screen.width < 900; +if (phone) { + borderW = screen.width; + borderH = screen.height; + + directX = 170; + directY = 250; + directScale = 0.7; + + skillScale = 0.8; + aX = 600; + aY = 270; + bX = 690; + bY = 170; + cX = 760; + cY = 270; +} else { + borderW = 1190; + borderH = 590; + + directX = 150; + directY = 430; + directScale = 0.85; + + skillScale = 0.9; + aX = 660; + aY = 420; + bX = 800; + bY = 320; + cX = 920; + cY = 420; +} + +var game = new Phaser.Game(borderW, borderH, Phaser.AUTO, 'phaser-example'); +var wsUsable =false; function connected() { - let ws = new WebSocket("ws://192.168.1.102:7094/ws?userId=120"); - ws.onopen = function () { - console.log('connected'); - }; - ws.onmessage = function (evt) { - console.log(evt.data) - }; - - ws.onclose = function (evt) { - console.log("error"); - }; - - ws.onerror = function (evt) { - console.log("error"); - }; - - return ws; + try { + wsUsable = false; + let ws = new WebSocket("ws://192.168.1.102:7094/ws?userId=120"); + ws.onopen = function () { + console.log('connected'); + // 等 open 函数回调后才可以调用 send,否则会报错 no longer object + wsUsable = true; + }; + ws.onmessage = function (evt) { + console.log(evt.data) + }; + ws.onclose = function (evt) { + console.log("error"); + }; + ws.onerror = function (evt) { + console.log("error"); + }; + + return ws; + } catch (e) { + console.log(e); + return null; + } } var ws = connected(); function toggleFullScreen() { var docElm = document.documentElement; if (docElm.requestFullscreen) { - docElm.requestFullscreen(); + docElm.requestFullscreen(); } else if (docElm.msRequestFullscreen) { - docElm.msRequestFullscreen(); + docElm.msRequestFullscreen(); } else if (docElm.mozRequestFullScreen) { - docElm.mozRequestFullScreen(); + docElm.mozRequestFullScreen(); } else if (docElm.webkitRequestFullScreen) { - docElm.webkitRequestFullScreen(); + docElm.webkitRequestFullScreen(); } - } - -// toggleFullScreen() +} var PhaserGame = function () { this.background = null; this.player = null; - this.speed = 300; - - this.weapon1; - this.weapon2; - this.weapon3; this.pad; + this.speed = 300; this.stick; @@ -74,7 +116,6 @@ PhaserGame.prototype = { }, preload: function () { - this.load.atlas('arcade', 'assets/arcade-joystick.png', 'assets/arcade-joystick.json'); this.load.image('background', 'assets/back.png'); this.load.image('player', 'assets/dark.png'); @@ -97,30 +138,32 @@ PhaserGame.prototype = { this.pad = this.game.plugins.add(Phaser.VirtualJoystick); - this.stick = this.pad.addStick(150, 430, 100, 'arcade'); + this.stick = this.pad.addStick(directX, directY, 100, 'arcade'); this.stick.deadZone = 0; - this.stick.scale = 0.85; + this.stick.scale = directScale; - this.buttonA = this.pad.addButton(650, 420, 'arcade', 'button1-up', 'button1-down'); + this.buttonA = this.pad.addButton(aX, aY, 'arcade', 'button1-up', 'button1-down'); this.buttonA.onDown.add(this.fireBullet, this); - this.buttonA.scale = 0.9; - this.buttonA.repeatRate = 100; + this.buttonA.scale = skillScale; + this.buttonA.repeatRate = 1000; this.buttonA.addKey(Phaser.Keyboard.CONTROL); - this.buttonB = this.pad.addButton(790, 320, 'arcade', 'button2-up', 'button2-down'); + this.buttonB = this.pad.addButton(bX, bY, 'arcade', 'button2-up', 'button2-down'); this.buttonB.onDown.add(this.fireRocket, this); - this.buttonB.scale = 0.9; - this.buttonB.repeatRate = 500; + this.buttonB.scale = skillScale; + this.buttonB.repeatRate = 300; this.buttonB.addKey(Phaser.Keyboard.Z); - this.buttonC = this.pad.addButton(920, 420, 'arcade', 'button3-up', 'button3-down'); + this.buttonC = this.pad.addButton(cX, cY, 'arcade', 'button3-up', 'button3-down'); this.buttonC.onDown.add(this.fireSpreadShot, this); - this.buttonC.scale = 0.9; + this.buttonC.scale = skillScale; + this.buttonB.repeatRate = 300; this.buttonC.addKey(Phaser.Keyboard.SPACEBAR); }, fireBullet: function () { - // toggleFullScreen() + toggleFullScreen(); + // ws = connected(); // this.weapon1.fire(this.player); @@ -135,10 +178,12 @@ PhaserGame.prototype = { fireSpreadShot: function () { // this.weapon3.fire(this.player); - let x = { - s: true - } + let x = { + s: true + } + if(wsUsable){ ws.send(JSON.stringify(x)) + } }, update: function () { @@ -168,8 +213,9 @@ PhaserGame.prototype = { else { // this.player.body.velocity.set(0); } - + if (wsUsable) { ws.send(JSON.stringify(key)) + } } }; diff --git a/netty/src/main/java/netty/Readme.md b/netty/src/main/java/netty/Readme.md new file mode 100644 index 00000000..138bdf54 --- /dev/null +++ b/netty/src/main/java/netty/Readme.md @@ -0,0 +1,4 @@ +https://www.baeldung.com/java-netty-http-server + +https://medium.com/@irunika/how-to-write-a-http-websocket-server-using-netty-f3c136adcba9 + From 7d08eaddac2e4f04a2b81a30744e4409974bd5f4 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Sun, 7 Jul 2024 17:58:50 +0800 Subject: [PATCH 408/476] readme --- gui/Readme.md | 14 +++++++++----- .../main/java/com/github/kuangcp/tank/Readme.md | 10 ---------- 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/gui/Readme.md b/gui/Readme.md index 17302eb8..f4c970f2 100644 --- a/gui/Readme.md +++ b/gui/Readme.md @@ -1,13 +1,17 @@ # GUI -gradle tank run -x test 直接运行 +## [Tank](/gui/src/main/java/com/github/kuangcp/tank) -gradle tank -x test 打包坦克游戏 -gradle note -x test 打包记事本 -gradle virus -x test 打包病毒模拟 -gradle calc -x test 打包计算器 +> like `Tank 1990` + +mvn clean package -P tank + +![](/gui/src/doc/tank/main.png) + +![](/gui/src/doc/tank/phone.png) ## GTK + > [java-gnome](http://java-gnome.sourceforge.net/README.html) [idea ui designer](https://www.youtube.com/watch?v=whF_Qm1epQ8)`javafx ui-designer` diff --git a/gui/src/main/java/com/github/kuangcp/tank/Readme.md b/gui/src/main/java/com/github/kuangcp/tank/Readme.md index 8db122ff..d9d55089 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/Readme.md +++ b/gui/src/main/java/com/github/kuangcp/tank/Readme.md @@ -1,13 +1,3 @@ -# Tank - -> like `Tank 1990` - -1. build: `mvn clean package -P tank` - -![](/gui/src/doc/tank/main.png) - -![](/gui/src/doc/tank/phone.png) - ## 2024-07-07 17:30:30 - 新增:网页Ws协议控制玩家坦克,模拟手柄交互 From fd34dc913dbbe7df7f1326d7ca83673055dafde8 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Mon, 8 Jul 2024 00:48:38 +0800 Subject: [PATCH 409/476] todo --- .../github/kuangcp/tank/domain/Hinder.java | 32 +++---------------- .../com/github/kuangcp/tank/domain/Tank.java | 13 ++++---- .../kuangcp/tank/panel/StageActionPanel.java | 26 ++++----------- .../kuangcp/tank/panel/TankGroundPanel.java | 1 + .../com/github/kuangcp/tank/util/Audio.java | 20 +++++------- 5 files changed, 27 insertions(+), 65 deletions(-) diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/Hinder.java b/gui/src/main/java/com/github/kuangcp/tank/domain/Hinder.java index ba543195..07629051 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/domain/Hinder.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/Hinder.java @@ -1,11 +1,14 @@ package com.github.kuangcp.tank.domain; +import lombok.Data; + /** * 障碍物的最基本类 */ +@Data public abstract class Hinder { - int hx, hy;//障碍物绘图坐标 左上角顶点 的坐标 + int hx, hy; // 障碍物绘图坐标 左上角顶点 的坐标 boolean alive;//存活状态 int width = 20; int height = 10; @@ -16,9 +19,6 @@ public Hinder(int hx, int hy) { this.hy = hy; } - public Hinder() { - } - public Boolean getAlive() { return alive; } @@ -26,28 +26,4 @@ public Boolean getAlive() { public void setAlive(Boolean alive) { this.alive = alive; } - - public int getHx() { - return hx; - } - - public void setHx(int hx) { - this.hx = hx; - } - - public int getHy() { - return hy; - } - - public void setHy(int hy) { - this.hy = hy; - } - - public int getWidth() { - return width; - } - - public int getHeight() { - return height; - } } diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/Tank.java b/gui/src/main/java/com/github/kuangcp/tank/domain/Tank.java index 14691eb0..00ab0448 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/domain/Tank.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/Tank.java @@ -27,6 +27,7 @@ public abstract class Tank extends AbstractLoopEvent implements VisualItem { int halfWidth = 10; int halfHeight = 15; + private final int wheelNum = 7; public void addLife(int delta) { this.life += delta; @@ -115,15 +116,15 @@ private void drawUp(Graphics g) { */ private void drawHorBorder(Graphics g, int topX, int topY) { final int quarterWidth = this.halfWidth / 2; - final int tap = this.halfHeight * 2 / 7; + final int tap = this.halfHeight * 2 / wheelNum; - for (int i = 0; i < 7; i++) { + for (int i = 0; i < wheelNum; i++) { g.fill3DRect(topX + tap * i + 1, topY, tap - 1, quarterWidth, false); } g.fill3DRect(topX + quarterWidth * 2, topY + quarterWidth, this.halfHeight * 2 - 4 * quarterWidth, this.halfWidth, false); - for (int i = 0; i < 7; i++) { + for (int i = 0; i < wheelNum; i++) { g.fill3DRect(topX + tap * i + 1, topY + this.halfHeight, tap - 1, quarterWidth, false); } } @@ -134,13 +135,13 @@ private void drawHorBorder(Graphics g, int topX, int topY) { private void drawVerBorder(Graphics g, int topX, int topY) { final int quarterWidth = this.halfWidth / 2; //1.左边的矩形 - final int tap = this.halfHeight * 2 / 7; - for (int i = 0; i < 7; i++) { + final int tap = this.halfHeight * 2 / wheelNum; + for (int i = 0; i < wheelNum; i++) { g.fill3DRect(topX, topY + tap * i + 1, quarterWidth, tap - 1, false); } //2.画出右边矩形 - for (int i = 0; i < 7; i++) { + for (int i = 0; i < wheelNum; i++) { g.fill3DRect(topX + quarterWidth * 3, topY + tap * i + 1, quarterWidth, tap - 1, false); } diff --git a/gui/src/main/java/com/github/kuangcp/tank/panel/StageActionPanel.java b/gui/src/main/java/com/github/kuangcp/tank/panel/StageActionPanel.java index 2a696471..e4836346 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/panel/StageActionPanel.java +++ b/gui/src/main/java/com/github/kuangcp/tank/panel/StageActionPanel.java @@ -19,7 +19,6 @@ * 用来放按钮的面板 * 并且监控按钮事件 */ -@SuppressWarnings("serial") @Slf4j public class StageActionPanel extends JPanel implements ActionListener { @@ -29,42 +28,31 @@ public class StageActionPanel extends JPanel implements ActionListener { @Override public void actionPerformed(ActionEvent ae) { + // TODO 删除无用代码 if (ae.getActionCommand().equals(ButtonCommand.START)) { this.startNewStage(); - } - - if (ae.getActionCommand().equals(ButtonCommand.EXIT)) { + } else if (ae.getActionCommand().equals(ButtonCommand.EXIT)) { System.out.println("结束"); System.exit(0); - } - - if (ae.getActionCommand().equals(ButtonCommand.PAUSE)) { + } else if (ae.getActionCommand().equals(ButtonCommand.PAUSE)) { System.out.println("暂停"); PlayStageMgr.pause = true; frame.requestFocus(); - } - - if (ae.getActionCommand().equals(ButtonCommand.RESUME)) { + } else if (ae.getActionCommand().equals(ButtonCommand.RESUME)) { System.out.println("继续"); PlayStageMgr.pause = false; frame.requestFocus(); - } - - if (ae.getActionCommand().equals(ButtonCommand.ABORT)) { + } else if (ae.getActionCommand().equals(ButtonCommand.ABORT)) { //不能把下面的saveExit口令放到这里来或,会出现先后顺序的一些错误 //口令与口令之间应该独立,不要有或,与这样的复杂逻辑结构 System.out.println("退出游戏"); System.exit(0); - } - - if (ae.getActionCommand().equals("saveExit")) { + } else if (ae.getActionCommand().equals("saveExit")) { System.out.println("按下了 保存并退出 按钮"); //退出 System.out.println("已经退出游戏"); System.exit(0); - } - - if (ae.getActionCommand().equals("Continue")) { + } else if (ae.getActionCommand().equals("Continue")) { //重新开启一个画板 System.out.println(ButtonCommand.START); frame.firstStart = false; diff --git a/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java b/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java index 85a80b2c..3a36a875 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java +++ b/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java @@ -182,6 +182,7 @@ public void paint(Graphics g) { } /*画出头像*/ + // fixme 像素偏移 g.drawImage(AvatarImgMgr.instance.curImg, 380, 480, AvatarImgMgr.instance.width, AvatarImgMgr.instance.height, this); diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/Audio.java b/gui/src/main/java/com/github/kuangcp/tank/util/Audio.java index e00e7ece..f7ae6e1c 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/util/Audio.java +++ b/gui/src/main/java/com/github/kuangcp/tank/util/Audio.java @@ -1,5 +1,7 @@ package com.github.kuangcp.tank.util; +import lombok.Getter; +import lombok.Setter; import lombok.extern.slf4j.Slf4j; import javax.sound.sampled.AudioFormat; @@ -13,25 +15,19 @@ /** * 类初始化时 把路径传入 播放声音的类 * 只能播放无损(无压缩的音乐文件)即WAV不能播放MP3 + * TODO fix */ @Slf4j public class Audio extends Thread { - private String filename; + private final String filename; + @Getter + @Setter private boolean live = true; - public boolean isLive() { - return live; - } - - public void setLive(boolean live) { - this.live = live; - } - //构造器 - public Audio(String wavfile) { - filename = wavfile; - + public Audio(String wavFile) { + filename = wavFile; } public void run() { From 55a6af300158122f55bc551d5d80acf49efdb0a6 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Mon, 8 Jul 2024 00:51:22 +0800 Subject: [PATCH 410/476] revert --- .../kuangcp/tank/backup/v1/MainPanelV1.java | 137 +++++++++++++++ .../github/kuangcp/tank/backup/v1/Readme.md | 1 + .../kuangcp/tank/backup/v1/TankGameV1.java | 29 ++++ .../kuangcp/tank/backup/v2/MainPanelV2.java | 157 ++++++++++++++++++ .../github/kuangcp/tank/backup/v2/Readme.md | 1 + .../kuangcp/tank/backup/v2/TankGameV2.java | 46 +++++ 6 files changed, 371 insertions(+) create mode 100644 gui/src/main/java/com/github/kuangcp/tank/backup/v1/MainPanelV1.java create mode 100644 gui/src/main/java/com/github/kuangcp/tank/backup/v1/Readme.md create mode 100644 gui/src/main/java/com/github/kuangcp/tank/backup/v1/TankGameV1.java create mode 100644 gui/src/main/java/com/github/kuangcp/tank/backup/v2/MainPanelV2.java create mode 100644 gui/src/main/java/com/github/kuangcp/tank/backup/v2/Readme.md create mode 100644 gui/src/main/java/com/github/kuangcp/tank/backup/v2/TankGameV2.java diff --git a/gui/src/main/java/com/github/kuangcp/tank/backup/v1/MainPanelV1.java b/gui/src/main/java/com/github/kuangcp/tank/backup/v1/MainPanelV1.java new file mode 100644 index 00000000..6690de3d --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/backup/v1/MainPanelV1.java @@ -0,0 +1,137 @@ +package com.github.kuangcp.tank.backup.v1; + +import com.github.kuangcp.tank.constant.DirectType; +import com.github.kuangcp.tank.domain.EnemyTank; +import com.github.kuangcp.tank.domain.Hero; +import com.github.kuangcp.tank.domain.Tank; +import lombok.extern.slf4j.Slf4j; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; +import java.util.Objects; +import java.util.Vector; + +/** + * @author Kuangcp on 2021-09-11 23:15 + */ +@Slf4j +@SuppressWarnings("serial") +class MainPanelV1 extends JPanel implements KeyListener { + + //定义我的坦克 + Hero hero = null; + //定义泛型的集合类 + Vector ets = new Vector<>(); + + //画板的构造函数 放图形的对象 + public MainPanelV1() { + } + + //重写paint + public void paint(Graphics g) { + super.paint(g); + + if (Objects.isNull(hero)) { + hero = new Hero(20, 20, 4); + } + g.fillRect(0, 0, 500, 400); + + //调用函数绘画出主坦克 + hero.drawSelf(g); + + new Tank(50, 20, 0) { + @Override + public void drawSelf(Graphics g) { + g.setColor(Color.WHITE); + super.drawSelf(g); + } + }.drawSelf(g); + new Tank(80, 20, 0) { + @Override + public void drawSelf(Graphics g) { + g.setColor(new Color(93, 217, 41)); + super.drawSelf(g); + } + }.drawSelf(g); + new Tank(110, 20, 0) { + @Override + public void drawSelf(Graphics g) { + g.setColor(new Color(34, 155, 234)); + super.drawSelf(g); + } + }.drawSelf(g); + new Tank(140, 20, 0) { + @Override + public void drawSelf(Graphics g) { + g.setColor(new Color(155, 62, 202)); + super.drawSelf(g); + } + }.drawSelf(g); + new Tank(170, 20, 0) { + @Override + public void drawSelf(Graphics g) { + g.setColor(new Color(240, 57, 23)); + super.drawSelf(g); + } + }.drawSelf(g); + + //画出子弹 + if (hero.bullet != null) { + g.draw3DRect(hero.bullet.sx, hero.bullet.sy, 1, 1, false); + } + + //画出敌人坦克 + for (EnemyTank s : ets) { + s.drawSelf(g); + } + + //画出砖块 + g.setColor(new Color(188, 112, 50)); + for (int i = 0; i < 9; i++) { + g.fill3DRect(60, 60 + 10 * i, 20, 10, false); + } + + g.setColor(Color.yellow); + g.drawRect(0, 0, 400, 300); + + } + + //当按下键盘上的键时监听者的处理函数 + public void keyPressed(KeyEvent e) { + //加了if条件后 实现了墙的限制(如果是游戏中的道具,该怎么办) + if (e.getKeyCode() == KeyEvent.VK_A) { + hero.setDirect(DirectType.LEFT); + if ((hero.getX() - 10) > 0) + hero.move(); + + } + if (e.getKeyCode() == KeyEvent.VK_D) { + hero.setDirect(DirectType.RIGHT); + if ((hero.getX() + 15) < 405) + hero.move(); + } + + if (e.getKeyCode() == KeyEvent.VK_W) { + hero.setDirect(DirectType.UP); + if ((hero.getY() - 13) > 0) + hero.move(); + + } + if (e.getKeyCode() == KeyEvent.VK_S) { + hero.setDirect(DirectType.DOWN); + if ((hero.getY() - 15) < 275) + hero.move(); + + } + //必须重新绘制窗口,不然上面的方法不能视觉上动起来 + this.repaint(); + } + + public void keyReleased(KeyEvent arg0) { + } + + public void keyTyped(KeyEvent arg0) { + } +} diff --git a/gui/src/main/java/com/github/kuangcp/tank/backup/v1/Readme.md b/gui/src/main/java/com/github/kuangcp/tank/backup/v1/Readme.md new file mode 100644 index 00000000..ab19a45b --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/backup/v1/Readme.md @@ -0,0 +1 @@ +画出基本图形 \ No newline at end of file diff --git a/gui/src/main/java/com/github/kuangcp/tank/backup/v1/TankGameV1.java b/gui/src/main/java/com/github/kuangcp/tank/backup/v1/TankGameV1.java new file mode 100644 index 00000000..30ab39e9 --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/backup/v1/TankGameV1.java @@ -0,0 +1,29 @@ +package com.github.kuangcp.tank.backup.v1; + +import javax.swing.*; + +/** + * 1 绘画出坦克 并且能做到对它的移动操作 + * 2 移动最好封装成函数(面向对象思想)这是坦克的行为属性 可扩展 + * 3 加上对坦克运动的边界限定 + */ +@SuppressWarnings("serial") +public class TankGameV1 extends JFrame { + + @SuppressWarnings("unused") + public static void main(String[] args) { + TankGameV1 Tank = new TankGameV1(); + } + + //最外层JFrame的构造函数 + public TankGameV1() { + MainPanelV1 panel = new MainPanelV1(); + this.add(panel); + this.addKeyListener(panel); + + this.setLocation(900, 200); + this.setSize(405, 333); + this.setVisible(true); + this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + } +} \ No newline at end of file diff --git a/gui/src/main/java/com/github/kuangcp/tank/backup/v2/MainPanelV2.java b/gui/src/main/java/com/github/kuangcp/tank/backup/v2/MainPanelV2.java new file mode 100644 index 00000000..0cbf983a --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/backup/v2/MainPanelV2.java @@ -0,0 +1,157 @@ +package com.github.kuangcp.tank.backup.v2; + +import com.github.kuangcp.tank.constant.DirectType; +import com.github.kuangcp.tank.domain.Bullet; +import com.github.kuangcp.tank.domain.EnemyTank; +import com.github.kuangcp.tank.domain.Hero; +import com.github.kuangcp.tank.domain.StageBorder; +import com.github.kuangcp.tank.mgr.PlayStageMgr; +import com.github.kuangcp.tank.mgr.RoundMapMgr; +import com.github.kuangcp.tank.util.TankTool; +import lombok.extern.slf4j.Slf4j; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; +import java.util.Iterator; +import java.util.Objects; +import java.util.Vector; +import java.util.concurrent.TimeUnit; + +/** + * @author Kuangcp on 2021-09-11 23:15 + */ +@Slf4j +@SuppressWarnings("serial") +class MainPanelV2 extends JPanel implements KeyListener, Runnable { + + //定义我的坦克 + Hero hero; + //定义泛型的集合类 + Vector ets = new Vector<>(); + + //画板的构造函数 放图形的对象 + public MainPanelV2() { +// ets.add(new EnemyTank(250, 60, 1, DirectType.UP)); +// ets.add(new EnemyTank(250, 60, 2, DirectType.UP)); +// ets.add(new EnemyTank(250, 60, 3, DirectType.UP)); +// ets.add(new EnemyTank(250, 260, 4, DirectType.UP)); +// ets.add(new EnemyTank(250, 260, 6, DirectType.UP)); + + ets.add(new EnemyTank(250, 60, DirectType.UP)); + ets.add(new EnemyTank(250, 70, DirectType.UP)); + ets.add(new EnemyTank(250, 80, DirectType.UP)); + ets.add(new EnemyTank(250, 90, DirectType.UP)); + ets.add(new EnemyTank(250, 100, DirectType.UP)); + +// for (int i = 0; i < 700; i++) { +// ets.add(new EnemyTank(20 + i * 40 % RoundMapMgr.instance.border.getMaxX(), 100 + i * 5 % RoundMapMgr.instance.border.getMaxY(), DirectType.UP)); +// } + +// for (EnemyTank et : ets) { +// LoopEventExecutor.addLoopEvent(et); +// } + hero = new Hero(160, 160, 4); + } + + //重写paint + public void paint(Graphics g) { + super.paint(g); + + final StageBorder border = RoundMapMgr.instance.border; + g.fillRect(0, 0, border.getMaxX() + border.getMinX(), border.getMaxY() + border.getMinY()); + + //调用函数绘画出主坦克 + hero.drawSelf(g); + + //画出子弹 + if (hero.bullet != null) { + g.draw3DRect(hero.bullet.sx, hero.bullet.sy, 1, 1, false); + } + + //画出敌人坦克 + for (EnemyTank s : ets) { + s.drawSelf(g); + final Iterator iterator = s.bulletList.iterator(); + while (iterator.hasNext()) { + final Bullet next = iterator.next(); + if (!next.alive) { + iterator.remove(); + } + g.setColor(Color.cyan); + next.drawSelf(g); + } + } + + //画出砖块 + g.setColor(new Color(188, 112, 50)); + for (int i = 0; i < 9; i++) { + g.fill3DRect(60, 60 + 10 * i, 20, 10, false); + } + + // border + g.setColor(Color.yellow); + g.drawRect(border.getMinX(), border.getMinY(), + border.getMaxX() - border.getMinX(), border.getMaxY() - border.getMinY()); + } + + //当按下键盘上的键时监听者的处理函数 + public void keyPressed(KeyEvent e) { + //加了if条件后 实现了墙的限制(如果是游戏中的道具,该怎么办) + if (e.getKeyCode() == KeyEvent.VK_A) { + hero.setDirect(2); + if (PlayStageMgr.instance.willInBorder(hero)) + hero.move(); + + } + if (e.getKeyCode() == KeyEvent.VK_D) { + hero.setDirect(3); + if (PlayStageMgr.instance.willInBorder(hero)) + hero.move(); + } + + if (e.getKeyCode() == KeyEvent.VK_W) { + hero.setDirect(0); + if (PlayStageMgr.instance.willInBorder(hero)) + hero.move(); + } + if (e.getKeyCode() == KeyEvent.VK_S) { + hero.setDirect(1); + if (PlayStageMgr.instance.willInBorder(hero)) + hero.move(); + } + //必须重新绘制窗口,不然上面的方法不能视觉上动起来 + this.repaint(); + } + + public void keyReleased(KeyEvent arg0) { + } + + public void keyTyped(KeyEvent arg0) { + } + + @Override + public void run() { + int maxFPS = 60; + long fpsTime = (long) ((1000.0 / maxFPS) * 1_000_000); + + long now; + //每隔100ms重绘 + while (true) { + now = System.nanoTime(); + + // 进行重绘 + this.repaint(); + final long waste = System.nanoTime() - now; + if (waste > fpsTime) { + continue; + } + + TankTool.yieldTime(fpsTime - waste, TimeUnit.NANOSECONDS); + if (Objects.nonNull(hero) && !hero.isAlive()) { + break; + } + } + } +} diff --git a/gui/src/main/java/com/github/kuangcp/tank/backup/v2/Readme.md b/gui/src/main/java/com/github/kuangcp/tank/backup/v2/Readme.md new file mode 100644 index 00000000..2430f16e --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/backup/v2/Readme.md @@ -0,0 +1 @@ +敌人算法实验 \ No newline at end of file diff --git a/gui/src/main/java/com/github/kuangcp/tank/backup/v2/TankGameV2.java b/gui/src/main/java/com/github/kuangcp/tank/backup/v2/TankGameV2.java new file mode 100644 index 00000000..d04fcc1a --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/backup/v2/TankGameV2.java @@ -0,0 +1,46 @@ +package com.github.kuangcp.tank.backup.v2; + +import com.github.kuangcp.tank.domain.Hero; +import com.github.kuangcp.tank.util.executor.MonitorExecutor; +import com.github.kuangcp.tank.mgr.PlayStageMgr; +import com.github.kuangcp.tank.mgr.RoundMapMgr; +import com.github.kuangcp.tank.domain.StageBorder; + +import javax.swing.*; +import java.util.Collections; + +/** + * 1 绘画出坦克 并且能做到对它的移动操作 + * 2 移动最好封装成函数(面向对象思想)这是坦克的行为属性 可扩展 + * 3 加上对坦克运动的边界限定 + */ +@SuppressWarnings("serial") +public class TankGameV2 extends JFrame { + + @SuppressWarnings("unused") + public static void main(String[] args) { + TankGameV2 Tank = new TankGameV2(); + } + + //最外层JFrame的构造函数 + public TankGameV2() { + MonitorExecutor.init(); + RoundMapMgr.init(); + PlayStageMgr.init(new Hero(50, 20, 5), Collections.emptyList(), Collections.emptyList(), Collections.emptyList()); + + MainPanelV2 panel = new MainPanelV2(); + this.add(panel); + this.addKeyListener(panel); + final Thread panelThread = new Thread(panel); + panelThread.start(); + + + final StageBorder border = RoundMapMgr.instance.border; + + this.setLocation(200, 50); + this.setSize(border.getMaxX() + border.getMinX(), border.getMaxY() + border.getMinY() * 2); + this.setVisible(true); + + this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + } +} \ No newline at end of file From 2414dc364868efbe673f8fd7992b4f09b18d22ac Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Mon, 8 Jul 2024 01:01:57 +0800 Subject: [PATCH 411/476] readme --- gui/src/main/java/com/github/kuangcp/tank/Readme.md | 4 +++- gui/src/main/java/com/github/kuangcp/tank/backup/v1/Readme.md | 1 - 2 files changed, 3 insertions(+), 2 deletions(-) delete mode 100644 gui/src/main/java/com/github/kuangcp/tank/backup/v1/Readme.md diff --git a/gui/src/main/java/com/github/kuangcp/tank/Readme.md b/gui/src/main/java/com/github/kuangcp/tank/Readme.md index d9d55089..728655cd 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/Readme.md +++ b/gui/src/main/java/com/github/kuangcp/tank/Readme.md @@ -8,4 +8,6 @@ 1. 对方运动算法 1. 网络联机 1. 持久化存储 - +1. 寻路算法 + - https://blog.csdn.net/h348592532/article/details/44421753 + - https://blog.csdn.net/bankQ1/article/details/121815065 diff --git a/gui/src/main/java/com/github/kuangcp/tank/backup/v1/Readme.md b/gui/src/main/java/com/github/kuangcp/tank/backup/v1/Readme.md deleted file mode 100644 index ab19a45b..00000000 --- a/gui/src/main/java/com/github/kuangcp/tank/backup/v1/Readme.md +++ /dev/null @@ -1 +0,0 @@ -画出基本图形 \ No newline at end of file From 379552d306209e7a5d8dbfc3b56552a7eaebf9a8 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Wed, 10 Jul 2024 00:37:52 +0800 Subject: [PATCH 412/476] bugs --- .../lambda/bug/MultipleExtendsTest.java | 80 ++++++++++--------- .../kuangcp/stream/bug/StreamGenericTest.java | 61 ++++++++++++++ 2 files changed, 102 insertions(+), 39 deletions(-) create mode 100644 java8/src/test/java/com/github/kuangcp/stream/bug/StreamGenericTest.java diff --git a/java8/src/test/java/com/github/kuangcp/lambda/bug/MultipleExtendsTest.java b/java8/src/test/java/com/github/kuangcp/lambda/bug/MultipleExtendsTest.java index dbf1e9b4..8b063832 100644 --- a/java8/src/test/java/com/github/kuangcp/lambda/bug/MultipleExtendsTest.java +++ b/java8/src/test/java/com/github/kuangcp/lambda/bug/MultipleExtendsTest.java @@ -1,45 +1,47 @@ package com.github.kuangcp.lambda.bug; +import org.junit.Test; + import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; -import org.junit.Test; /** * @author Kuangcp on 2019-11-28 19:43 */ public class MultipleExtendsTest { - @Test - public void testMap() { - List list = Arrays.asList(new CImpl(), new CImpl()); - List names = list.stream().map(A::getName).collect(Collectors.toList()); - System.out.println(names); - } - - private static void getNames(List list) { - list.stream().map(E::getName).count(); - list.stream().map(E::getNum).count(); - } - - // https://bugs.java.com/bugdatabase/view_bug.do?bug_id=8142476 - @Test - public void testMapWithGeneric() { - List list = Arrays.asList(new CImpl(), new CImpl()); - getNames(list); - getNames(Arrays.asList(new D(), new D())); - } + // 普通写法 + @Test + public void testMap() { + List list = Arrays.asList(new CImpl(), new CImpl()); + List names = list.stream().map(A::getName).collect(Collectors.toList()); + System.out.println(names); + } + + private static long sum(List list) { + return list.stream().map(E::getName).count() + list.stream().map(E::getNum).count(); + } + + // https://bugs.java.com/bugdatabase/view_bug.do?bug_id=8142476 + // 旧版本JDK 1.8 运行时会报错,但是 1.8.402 也没报错了 + @Test + public void testMapWithGeneric() { + List list = Arrays.asList(new CImpl(), new CImpl()); + System.out.println(sum(list)); + System.out.println(sum(Arrays.asList(new Combine(), new Combine()))); + } } interface A { - String getName(); + String getName(); } interface B { - Integer getNum(); + Integer getNum(); } interface C extends A, B { @@ -48,26 +50,26 @@ interface C extends A, B { class CImpl implements C { - @Override - public String getName() { - return null; - } + @Override + public String getName() { + return null; + } - @Override - public Integer getNum() { - return null; - } + @Override + public Integer getNum() { + return null; + } } -class D implements A, B { +class Combine implements A, B { - @Override - public String getName() { - return null; - } + @Override + public String getName() { + return null; + } - @Override - public Integer getNum() { - return null; - } + @Override + public Integer getNum() { + return null; + } } \ No newline at end of file diff --git a/java8/src/test/java/com/github/kuangcp/stream/bug/StreamGenericTest.java b/java8/src/test/java/com/github/kuangcp/stream/bug/StreamGenericTest.java new file mode 100644 index 00000000..a815c19e --- /dev/null +++ b/java8/src/test/java/com/github/kuangcp/stream/bug/StreamGenericTest.java @@ -0,0 +1,61 @@ +package com.github.kuangcp.stream.bug; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Objects; + +/** + * @author Kuangcp on 2024-07-10 00:06 + */ +public class StreamGenericTest { + + @Test + public void testList() throws Exception { + final ArrayList list = buildSpecialList(); + final Object result = list.stream() + .filter(v -> Objects.equals(((HashMap) v).get("name"), "target")) + .map(v -> ((HashMap) v).get("id")) + .findAny() + // 此处的类型转换没有起到作用,只推断为Object类型 + .map(v -> (Integer) v) + // 即使是硬编码转换类型 同样会只推断为Object类型 + .map(v -> Integer.parseInt(v.toString())) + // 甚至换成任意对象值,都不符合Optional.map方法的签名(泛型推断),永远推断为Object + .map(v -> new HashMap<>()) + .orElse(null); + + // 只能在lambda表达式结束后做强转 + Integer resultId = (Integer) result; + System.out.println(resultId); + + // 当发起Stream的集合在显式定义泛型后,Optional和Stream的 map方法的泛型就全部正常了。 + final ArrayList list2 = buildSpecialList(); + final Integer resultId2 = list2.stream() + .filter(v -> Objects.equals(((HashMap) v).get("name"), "target")) + .map(v -> ((HashMap) v).get("id")) + .findAny() + // 此处的类型转换没有起到作用,修改值也是不起作用,永远返回Object类型 + .map(v -> (Integer) v) + .orElse(null); + System.out.println(resultId2); + } + + private static ArrayList buildSpecialList() { + final ArrayList list = new ArrayList(); + + final HashMap item = new HashMap(); + item.put("name", "target"); + item.put("id", 12); + list.add(item); + + + final HashMap item2 = new HashMap(); + item2.put("name", "other"); + item2.put("id", 1); + list.add(item2); + return list; + } + +} From bd7dfeb92e864c97ae6d7b70fd9792ea845ebde1 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Thu, 11 Jul 2024 00:27:26 +0800 Subject: [PATCH 413/476] wip: state --- .../github/kuangcp/tank/domain/Bullet.java | 1 + .../com/github/kuangcp/tank/domain/Hero.java | 13 + .../com/github/kuangcp/tank/domain/Tank.java | 16 + .../com/github/kuangcp/tank/mgr/BombMgr.java | 34 +- .../github/kuangcp/tank/mgr/PlayStageMgr.java | 154 +++- .../kuangcp/tank/panel/StageActionPanel.java | 6 +- .../kuangcp/tank/panel/TankGroundPanel.java | 196 +---- .../com/github/kuangcp/tank/util/Saved.java | 742 +++++++++--------- 8 files changed, 567 insertions(+), 595 deletions(-) diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/Bullet.java b/gui/src/main/java/com/github/kuangcp/tank/domain/Bullet.java index 93c94026..d4a6c2a0 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/domain/Bullet.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/Bullet.java @@ -16,6 +16,7 @@ @Slf4j public class Bullet extends AbstractLoopEvent implements VisualItem { + public int tankId; public int sx; public int sy; public int direct; diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/Hero.java b/gui/src/main/java/com/github/kuangcp/tank/domain/Hero.java index 25904693..606091a9 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/domain/Hero.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/Hero.java @@ -95,7 +95,20 @@ public boolean isInvincible() { return false; } + @Override + public boolean hitByBullet() { + // 无敌 + if (this.isInvincible()) { + return false; + } + this.resurrect(); + return super.hitByBullet(); + } + public void resurrect() { + if (!this.alive) { + return; + } this.lastDieMs = System.currentTimeMillis(); // 100‰ 概率原地复活 diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/Tank.java b/gui/src/main/java/com/github/kuangcp/tank/domain/Tank.java index 00ab0448..30bbb660 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/domain/Tank.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/Tank.java @@ -7,6 +7,7 @@ import lombok.extern.slf4j.Slf4j; import java.awt.*; +import java.util.concurrent.atomic.AtomicInteger; /** * 最起初的坦克类 @@ -16,6 +17,9 @@ @Getter @Slf4j public abstract class Tank extends AbstractLoopEvent implements VisualItem { + private static final AtomicInteger idCnt = new AtomicInteger(0); + int id; + int x; // 坦克中心的横坐标 int y; // 坦克中心的纵坐标 int direct = DirectType.NONE; // 初始方向 @@ -31,9 +35,13 @@ public abstract class Tank extends AbstractLoopEvent implements VisualItem { public void addLife(int delta) { this.life += delta; + if (this.life <= 0) { + this.alive = false; + } } public Tank(int x, int y, int speed) { + this.id = idCnt.incrementAndGet(); this.x = x; this.y = y; this.speed = speed; @@ -44,6 +52,14 @@ public void run() { } + /** + * @return draw bomb + */ + public boolean hitByBullet() { + this.addLife(-1); + return true; + } + public void move() { switch (this.direct) { case DirectType.UP: diff --git a/gui/src/main/java/com/github/kuangcp/tank/mgr/BombMgr.java b/gui/src/main/java/com/github/kuangcp/tank/mgr/BombMgr.java index 8f2ed611..65abf31d 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/mgr/BombMgr.java +++ b/gui/src/main/java/com/github/kuangcp/tank/mgr/BombMgr.java @@ -3,19 +3,15 @@ import com.github.kuangcp.tank.constant.DirectType; import com.github.kuangcp.tank.domain.Bomb; import com.github.kuangcp.tank.domain.Bullet; -import com.github.kuangcp.tank.domain.Hero; import com.github.kuangcp.tank.domain.Tank; import com.github.kuangcp.tank.resource.AbstractImgListMgr; import com.github.kuangcp.tank.resource.PropertiesMgr; - import lombok.extern.slf4j.Slf4j; import javax.swing.*; import java.awt.*; -import java.util.ArrayList; -import java.util.Collections; +import java.util.LinkedList; import java.util.List; -import java.util.Objects; /** * @author Kuangcp on 2021-09-11 16:28 @@ -33,7 +29,7 @@ public String getConfigKey() { } //定义炸弹爆炸集合 - public List bombs = Collections.synchronizedList(new ArrayList<>()); + private final List bombs = new LinkedList<>(); /** * 画出炸弹 @@ -44,11 +40,6 @@ public void drawBomb(Graphics g, JPanel panel) { Bomb b = bombs.get(i); if (b.life > 10) { g.drawImage(BombMgr.instance.imgArr[0], b.bx, b.by, 30, 30, panel); -// if(rr){ -// Audio B = new Audio("./src/RE/GameBegin.wav"); -// B.start(); -// rr = false; -// } } else if (b.life > 5) { g.drawImage(BombMgr.instance.imgArr[1], b.bx, b.by, 30, 30, panel); } else { @@ -110,25 +101,8 @@ private void checkBong(Tank tank, Bullet bullet) { } private void handleBombAndTank(Tank tank, int bx, int by) { - Hero hero = null; - if (tank instanceof Hero) { - hero = (Hero) tank; - // 无敌 - if (hero.isInvincible()) { - return; - } - } - - tank.addLife(-1); - if (tank.getLife() <= 0) { - tank.setAlive(false); - } - - bombs.add(new Bomb(bx, by)); - - // 复活 - if (Objects.nonNull(hero)) { - hero.resurrect(); + if (tank.hitByBullet()) { + bombs.add(new Bomb(bx, by)); } } } diff --git a/gui/src/main/java/com/github/kuangcp/tank/mgr/PlayStageMgr.java b/gui/src/main/java/com/github/kuangcp/tank/mgr/PlayStageMgr.java index 9e7c4667..31c0a4b1 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/mgr/PlayStageMgr.java +++ b/gui/src/main/java/com/github/kuangcp/tank/mgr/PlayStageMgr.java @@ -9,13 +9,19 @@ import com.github.kuangcp.tank.domain.Tank; import com.github.kuangcp.tank.resource.DefeatImgMgr; import com.github.kuangcp.tank.resource.VictoryImgMgr; +import com.github.kuangcp.tank.util.ExecutePool; import com.github.kuangcp.tank.util.TankTool; +import com.github.kuangcp.tank.util.executor.LoopEventExecutor; +import lombok.Getter; import lombok.extern.slf4j.Slf4j; import java.awt.*; import java.awt.image.ImageObserver; import java.util.List; +import java.util.Map; import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArrayList; /** * @author Kuangcp on 2021-09-11 23:24 @@ -25,7 +31,7 @@ public class PlayStageMgr { public static PlayStageMgr instance = null; - public Hero hero; + public static Hero hero; public boolean startLogic = false; public boolean winCurRound = false; public int roundPrize = 0; @@ -40,16 +46,32 @@ public class PlayStageMgr { /** * 敌人的数量 */ + @Getter public static int enemySize = 10; /** * 无敌状态 时间 */ + @Getter static long invincibleMs = 5_000L; + public static volatile boolean newStage = true; + public static volatile boolean invokeNewStage = false; + // 场景 上下文 - public List enemyTanks; - public List bricks; // 砖 - public List irons; // 铁 +// public List enemyTanks; +// public List bricks; // 砖 +// public List irons; // 铁 + + public Map heroMap = new ConcurrentHashMap<>(); + + //TODO 渲染在一个线程里,不用考虑并发安全 + public static List enemyList; + public static List bricks; + public static List irons; + + //所有按下键的code集合 + public static int[][] enemyTankMap = new int[12][2]; + public static int[] myself = new int[6]; /** * 等待stage启动 @@ -77,13 +99,12 @@ public static void init(Hero hero, List enemyTanks, List brick private PlayStageMgr(Hero hero, List enemyTanks, List bricks, List irons) { this.hero = hero; - this.enemyTanks = enemyTanks; + this.enemyList = enemyTanks; this.bricks = bricks; this.irons = irons; } public boolean hasWinCurrentRound() { - final Hero hero = instance.hero; if (Objects.isNull(hero)) { winCurRound = false; } else { @@ -97,13 +118,119 @@ public void abortStage() { this.stopStage(); } + + public void refresh(){ + + } + + public static void startNewRound() { + enemyList = new CopyOnWriteArrayList<>(); + bricks = new CopyOnWriteArrayList<>(); + irons = new CopyOnWriteArrayList<>(); + + //创建英雄坦克 + if (newStage) { + hero = new Hero(480, 500, 3); + hero.setLife(10); + } else { + hero = new Hero(myself[0], myself[1], 3); + hero.setLife(myself[2]); + hero.setPrize(myself[3]); + } + + PlayStageMgr.init(hero, enemyList, bricks, irons); + + //多键监听实现 +// heroKeyListener = new HeroKeyListener(HoldingKeyEventMgr.instance, hero, this); +// ExecutePool.exclusiveLoopPool.execute(heroKeyListener); + ExecutePool.exclusiveLoopPool.execute(hero); + + // 创建 敌人的坦克 + EnemyTank ett = null; + if (newStage) {//正常启动并创建坦克线程 + for (int i = 0; i < PlayStageMgr.getEnemySize(); i++) { + //在四个随机区域产生坦克 + switch ((int) (Math.random() * 4)) { + case 0: + ett = new EnemyTank(20 + (int) (Math.random() * 30), 20 + (int) (Math.random() * 30), i % 4); + break; + case 1: + ett = new EnemyTank(700 - (int) (Math.random() * 30), 20 + (int) (Math.random() * 30), i % 4); + break; + case 2: + ett = new EnemyTank(20 + (int) (Math.random() * 30), 200 + (int) (Math.random() * 30), i % 4); + break; + case 3: + ett = new EnemyTank(700 - (int) (Math.random() * 30), 200 + (int) (Math.random() * 30), i % 4); + break; + } + + if (Objects.isNull(ett)) { + continue; + } + LoopEventExecutor.addLoopEvent(ett); + enemyList.add(ett); + } + } else { + /*进入读取文件步骤*/ + for (int i = 0; i < enemyTankMap.length; i++) { + if (enemyTankMap[i][0] == 0) { + break; + } + ett = new EnemyTank(enemyTankMap[i][0], enemyTankMap[i][1], i % 4); + LoopEventExecutor.addLoopEvent(ett); + enemyList.add(ett); + } + } + + //左右下角 + createB(bricks, 20, 310, 300, 540); + createB(bricks, 520, 310, 740, 540); + + //头像附近 + createB(bricks, 360, 460, 460, 480); + createB(bricks, 360, 480, 380, 540); + createB(bricks, 440, 460, 460, 540); + + createI(irons, 330, 410, 480, 430); + + PlayStageMgr.instance.markStartLogic(); + } + + /** + * 创建砖 + */ + public static void createB(List bricks, int startX, int startY, int endX, int endY) { + Brick template = new Brick(0, 0); + for (int i = startX; i < endX; i += template.getWidth()) { + for (int j = startY; j < endY; j += template.getHeight()) { + Brick bs = new Brick(i, j); + bricks.add(bs); + } + } + } + + /** + * 创建铁块 + */ + public static void createI(List irons, int startX, int startY, int endX, int endY) { + Iron template = new Iron(0, 0); + for (int i = startX; i < endX; i += template.getWidth()) { + for (int j = startY; j < endY; j += template.getHeight()) { + Iron bs = new Iron(i, j); + irons.add(bs); + } + } + } + + public void stopStage() { roundPrize = hero.getPrize(); instance.markStopLogic(); log.info("clean round:{}", round); instance.hero.setAlive(false); // TODO clean - for (EnemyTank et : enemyTanks) { + for (EnemyTank et : enemyList) { et.setAbort(true); et.setAlive(false); } @@ -136,7 +263,7 @@ public void drawStop(Graphics g, ImageObserver observer) { // } public boolean ableToMove(Hero hero) { - return enemyTanks.stream().allMatch(v -> TankTool.ablePass(hero, v)); + return enemyList.stream().allMatch(v -> TankTool.ablePass(hero, v)); } public boolean willInBorder(Tank tank) { @@ -164,10 +291,6 @@ public boolean willInBorder(Bullet bullet) { || bullet.sy <= RoundMapMgr.instance.border.getMinY() || bullet.sy >= RoundMapMgr.instance.border.getMaxY(); } - public static int getEnemySize() { - return enemySize; - } - public static void setEnemySize(int enemySize) { PlayStageMgr.enemySize = enemySize; } @@ -176,10 +299,6 @@ public static void addEnemySize(int delta) { PlayStageMgr.enemySize += delta; } - public static long getInvincibleMs() { - return invincibleMs; - } - public static boolean ablePassByHero(Tank t) { if (Objects.isNull(instance) || Objects.isNull(instance.hero) || !instance.hero.isAlive()) { return true; @@ -212,8 +331,7 @@ public static boolean ablePassByHinder(Tank t) { public int getLiveEnemy() { int result = 0; - for (int i = 0; i < this.enemyTanks.size(); i++) { - final EnemyTank enemyTank = enemyTanks.get(i); + for (final EnemyTank enemyTank : this.enemyList) { if (enemyTank.isAlive()) { result++; } diff --git a/gui/src/main/java/com/github/kuangcp/tank/panel/StageActionPanel.java b/gui/src/main/java/com/github/kuangcp/tank/panel/StageActionPanel.java index e4836346..2ee6b6be 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/panel/StageActionPanel.java +++ b/gui/src/main/java/com/github/kuangcp/tank/panel/StageActionPanel.java @@ -56,7 +56,7 @@ public void actionPerformed(ActionEvent ae) { //重新开启一个画板 System.out.println(ButtonCommand.START); frame.firstStart = false; - TankGroundPanel.newStage = false; + PlayStageMgr.newStage = false; Bullet.setSpeed(8); // frame.remove(frame.centerPanel); @@ -88,7 +88,7 @@ public void startNewStage() { } frame.firstStart = false; - TankGroundPanel.newStage = true; + PlayStageMgr.newStage = true; Bullet.setSpeed(8); frame.remove(frame.getContentPane()); @@ -100,7 +100,7 @@ public void startNewStage() { } beginAudio = new Audio("./src/RE/GameBegin.wav"); // beginAudio.start(); - frame.groundPanel.startNewRound(); + PlayStageMgr.startNewRound(); }); actionThread.setName("actionThread"); actionThread.start();//将画板线程开启 diff --git a/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java b/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java index 3a36a875..5c3b6461 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java +++ b/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java @@ -3,7 +3,6 @@ import com.github.kuangcp.tank.domain.Brick; import com.github.kuangcp.tank.domain.Bullet; import com.github.kuangcp.tank.domain.EnemyTank; -import com.github.kuangcp.tank.domain.Hero; import com.github.kuangcp.tank.domain.Iron; import com.github.kuangcp.tank.domain.StageBorder; import com.github.kuangcp.tank.frame.MainFrame; @@ -13,7 +12,6 @@ import com.github.kuangcp.tank.mgr.RoundMapMgr; import com.github.kuangcp.tank.resource.AvatarImgMgr; import com.github.kuangcp.tank.resource.ColorMgr; -import com.github.kuangcp.tank.util.ExecutePool; import com.github.kuangcp.tank.util.HoldingKeyStateMgr; import com.github.kuangcp.tank.util.TankTool; import com.github.kuangcp.tank.util.executor.AbstractDelayEvent; @@ -26,103 +24,11 @@ import java.awt.*; import java.awt.event.KeyEvent; import java.util.List; -import java.util.Objects; -import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.TimeUnit; @Slf4j public class TankGroundPanel extends JPanel implements java.awt.event.KeyListener, Runnable { - public volatile Hero hero; - public static boolean newStage = true; - private volatile boolean invokeNewStage = false; - - public List enemyList = new CopyOnWriteArrayList<>(); - public List bricks = new CopyOnWriteArrayList<>(); - public List irons = new CopyOnWriteArrayList<>(); - - //所有按下键的code集合 - public static int[][] enemyTankMap = new int[12][2]; - public static int[] myself = new int[6]; - - public TankGroundPanel() { - - } - - public void startNewRound() { - enemyList = new CopyOnWriteArrayList<>(); - bricks = new CopyOnWriteArrayList<>(); - irons = new CopyOnWriteArrayList<>(); - - //创建英雄坦克 - if (newStage) { - hero = new Hero(480, 500, 3); - hero.setLife(10); - } else { - hero = new Hero(myself[0], myself[1], 3); - hero.setLife(myself[2]); - hero.setPrize(myself[3]); - } - - PlayStageMgr.init(hero, enemyList, bricks, irons); - - //多键监听实现 -// heroKeyListener = new HeroKeyListener(HoldingKeyEventMgr.instance, hero, this); -// ExecutePool.exclusiveLoopPool.execute(heroKeyListener); - ExecutePool.exclusiveLoopPool.execute(hero); - - // 创建 敌人的坦克 - EnemyTank ett = null; - if (newStage) {//正常启动并创建坦克线程 - for (int i = 0; i < PlayStageMgr.getEnemySize(); i++) { - //在四个随机区域产生坦克 - switch ((int) (Math.random() * 4)) { - case 0: - ett = new EnemyTank(20 + (int) (Math.random() * 30), 20 + (int) (Math.random() * 30), i % 4); - break; - case 1: - ett = new EnemyTank(700 - (int) (Math.random() * 30), 20 + (int) (Math.random() * 30), i % 4); - break; - case 2: - ett = new EnemyTank(20 + (int) (Math.random() * 30), 200 + (int) (Math.random() * 30), i % 4); - break; - case 3: - ett = new EnemyTank(700 - (int) (Math.random() * 30), 200 + (int) (Math.random() * 30), i % 4); - break; - } - - if (Objects.isNull(ett)) { - continue; - } - LoopEventExecutor.addLoopEvent(ett); - enemyList.add(ett); - } - } else { - /*进入读取文件步骤*/ - for (int i = 0; i < enemyTankMap.length; i++) { - if (enemyTankMap[i][0] == 0) { - break; - } - ett = new EnemyTank(enemyTankMap[i][0], enemyTankMap[i][1], i % 4); - LoopEventExecutor.addLoopEvent(ett); - enemyList.add(ett); - } - } - - //左右下角 - createB(bricks, 20, 310, 300, 540); - createB(bricks, 520, 310, 740, 540); - - //头像附近 - createB(bricks, 360, 460, 460, 480); - createB(bricks, 360, 480, 380, 540); - createB(bricks, 440, 460, 460, 540); - - createI(irons, 330, 410, 480, 430); - - PlayStageMgr.instance.markStartLogic(); - } - private void drawBg(Graphics g) { g.setColor(ColorMgr.instance.bgColor); final StageBorder border = RoundMapMgr.instance.border; @@ -134,9 +40,9 @@ private void drawBg(Graphics g) { private void drawHeroInfo(Graphics g) { g.setColor(Color.GREEN); - final String lifeInfo = "Life:" + hero.getLife() + final String lifeInfo = "Life:" + PlayStageMgr.hero.getLife() + " Enemy: " + PlayStageMgr.instance.getLiveEnemy() - + " Prize: " + hero.getPrize(); + + " Prize: " + PlayStageMgr.hero.getPrize(); g.drawString(lifeInfo, RoundMapMgr.instance.border.getMinX(), 15); } @@ -150,7 +56,7 @@ private void drawMonitorInfo(Graphics g) { public void paint(Graphics g) { super.paint(g); - if (PlayStageMgr.stageNoneStart() || Objects.isNull(hero)) { + if (PlayStageMgr.stageNoneStart()) { PlayStageMgr.drawStopIgnore(g, this); return; } @@ -162,6 +68,7 @@ public void paint(Graphics g) { /*画出障碍物__砖__ 铁__*/ g.setColor(ColorMgr.instance.ironColor); + final List irons = PlayStageMgr.irons; for (int i = 0; i < irons.size(); i++) { Iron ir = irons.get(i); if (ir.getAlive()) { @@ -172,6 +79,7 @@ public void paint(Graphics g) { } g.setColor(ColorMgr.instance.brickColor); + final List bricks = PlayStageMgr.bricks; for (int i = 0; i < bricks.size(); i++) { Brick bs = bricks.get(i); if (bs.getAlive()) { @@ -187,26 +95,17 @@ public void paint(Graphics g) { AvatarImgMgr.instance.width, AvatarImgMgr.instance.height, this); /*画出主坦克*/ - if (hero.isAlive()) { - for (int i = 0; i < enemyList.size(); i++) { - EnemyTank demon; - try { - demon = enemyList.get(i); - } catch (IndexOutOfBoundsException e) { - log.error("", e); - continue; - } - - BombMgr.instance.checkBong(hero, demon.bulletList); + final List enemyList = PlayStageMgr.enemyList; + if (PlayStageMgr.hero.isAlive()) { + for (EnemyTank enemyTank : enemyList) { + BombMgr.instance.checkBong(PlayStageMgr.hero, enemyTank.bulletList); } - hero.drawSelf(g); + PlayStageMgr.hero.drawSelf(g); } - // 画出自己的子弹画子弹是可以封装成一个方法的 - // 从ss 这个子弹集合中 取出每颗子弹,并画出来 - for (int i = 0; i < hero.bulletList.size(); i++) { - Bullet myBullet = hero.bulletList.get(i); + for (int i = 0; i < PlayStageMgr.hero.bulletList.size(); i++) { + Bullet myBullet = PlayStageMgr.hero.bulletList.get(i); for (Brick brick : bricks) { TankTool.judgeHint(myBullet, brick); } @@ -215,30 +114,22 @@ public void paint(Graphics g) { } if (myBullet.sx < 440 && myBullet.sx > 380 && myBullet.sy < 540 && myBullet.sy > 480) { myBullet.alive = false; - hero.setAlive(false); + PlayStageMgr.hero.setAlive(false); } - if (hero.bulletList.get(i) != null && hero.bulletList.get(i).alive) { + if (PlayStageMgr.hero.bulletList.get(i) != null && PlayStageMgr.hero.bulletList.get(i).alive) { g.setColor(Color.YELLOW); g.draw3DRect(myBullet.sx, myBullet.sy, 3, 3, false); } //子弹线程死了 就要把它从集合中删除 if (!myBullet.alive) { - hero.bulletList.remove(myBullet); + PlayStageMgr.hero.bulletList.remove(myBullet); } } /*敌人子弹*/ // FIXME ConcurrentModificationException - for (int j = 0; j < enemyList.size(); j++) { - EnemyTank et; - try { - et = enemyList.get(j); - } catch (IndexOutOfBoundsException e) { - log.error("", e); - continue; - } - + for (EnemyTank et : enemyList) { for (int i = 0; i < et.bulletList.size(); i++) { Bullet myBullet = et.bulletList.get(i); for (Brick brick : bricks) { @@ -249,7 +140,7 @@ public void paint(Graphics g) { } if (myBullet.sx < 440 && myBullet.sx > 380 && myBullet.sy < 540 && myBullet.sy > 480) { myBullet.alive = false; - hero.setAlive(false); + PlayStageMgr.hero.setAlive(false); } if (et.bulletList.get(i) != null && et.bulletList.get(i).alive) { g.setColor(Color.cyan); @@ -270,25 +161,10 @@ public void paint(Graphics g) { for (int i = 0; i < 4; i++) { EnemyTank d = new EnemyTank(20 + (int) (Math.random() * 400), 20 + (int) (Math.random() * 300), i % 4); LoopEventExecutor.addLoopEvent(d); -// Thread fillThread = new Thread(d); -// fillThread.setName("fillEnemy" + d.id); -// fillThread.start(); enemyList.add(d); } } - -// for(int i=0;i bricks, int startX, int startY, int endX, int endY) { - Brick template = new Brick(0, 0); - for (int i = startX; i < endX; i += template.getWidth()) { - for (int j = startY; j < endY; j += template.getHeight()) { - Brick bs = new Brick(i, j); - bricks.add(bs); - } - } - } - - /** - * 创建铁块 - */ - public void createI(List irons, int startX, int startY, int endX, int endY) { - Iron template = new Iron(0, 0); - for (int i = startX; i < endX; i += template.getWidth()) { - for (int j = startY; j < endY; j += template.getHeight()) { - Iron bs = new Iron(i, j); - irons.add(bs); - } - } - } - //画板做成线程 画布进行刷新 @Override public void run() { diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/Saved.java b/gui/src/main/java/com/github/kuangcp/tank/util/Saved.java index ffad1380..13ca25b7 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/util/Saved.java +++ b/gui/src/main/java/com/github/kuangcp/tank/util/Saved.java @@ -1,371 +1,371 @@ -package com.github.kuangcp.tank.util; - -import com.github.kuangcp.tank.domain.Brick; -import com.github.kuangcp.tank.domain.EnemyTank; -import com.github.kuangcp.tank.domain.Hero; -import com.github.kuangcp.tank.domain.Iron; -import lombok.extern.slf4j.Slf4j; - -import java.io.BufferedReader; -import java.io.BufferedWriter; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.sql.Connection; -import java.sql.DriverManager; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.List; - -@Slf4j -public class Saved { - private Hero hero; - private List ets; - private List irons; - private List bricks; - int[][] ETS; - int[] myself; - - public Saved(List ets, Hero hero, List bricks, List irons, int[][] ETS, int[] myself) { - this.ets = ets; - this.hero = hero; - this.bricks = bricks; - this.irons = irons; - this.ETS = ETS; - this.myself = myself; - } - - //保存一切的函数 - public void savedAll() { - BufferedWriter bw = null; - OutputStream out = null; - OutputStreamWriter os = null; - - EnemyTank s; - Brick b; - Iron r; - - try { - out = new FileOutputStream("./src/RE/File.txt"); -// 同等级包下文件,用于存取,导出的时候才可以用上! - //尚未解决!!! -// URL j = (Saved.class.getResource("/RE/File.txt")); -// out = new FileOutputStream(j.getPath()); - os = new OutputStreamWriter(out); - bw = new BufferedWriter(os); - //<>是敌人坦克 - - bw.write("< \r\n"); -// bw.write("写进去了\r\n"); - for (int i = 0; i < ets.size(); i++) { - if ((s = ets.get(i)) != null && s.isAlive()) { - - String qi = "", qx = "", qy = "";//i-2,x-3,y-3 - - if (i < 10) qi = i + " "; - else qi += i; - if (s.getX() < 100) qx = s.getX() + " "; - else qx += s.getX(); - if (s.getY() < 100) qy = s.getY() + " "; - else qy += s.getY(); - - bw.write(qi + " " + qx + " " + qy + "\r\n"); - } - } - bw.write("> \r\n"); - //##是自己坦克 - bw.write("# \r\n"); -// bw.write("# \r\n"); - String Hx = "", Hy = "", Hl = "", Hp = "";//x-3,y-3,l-2,p-3 - - if (hero.getX() < 100) Hx = hero.getX() + " "; - else Hx += hero.getX(); - if (hero.getY() < 100) Hy = hero.getY() + " "; - else Hy += hero.getY(); - if (hero.getLife() < 10) Hl = hero.getLife() + " "; - else Hl += hero.getLife(); - if (hero.getPrize() < 10) Hp = hero.getPrize() + " "; - else if (hero.getPrize() < 100) Hp = hero.getPrize() + " "; - else Hp += hero.getPrize(); - - bw.write(Hx + " " + Hy + " " + Hl + " " + Hp + "\r\n"); - bw.write("# \r\n"); -// //..是砖块 -// bw.write("+\r\n"); -// for(int i=0;i ")) { - System.out.println("退出了敌人信息读取"); - break; - } -// L=br.readLine(); - String Di, Dx, Dy; - - Di = L.substring(0, 2);//(0,2] - Dx = L.substring(3, 6); - Dy = L.substring(7, 10); //是针对每一行的 只要有一行的长度小于这个10了就会抛越界异常 - System.out.println(Di + "X:" + Dx + "Y:" + Dy); - - ETS[StoInt(Di)][0] = StoInt(Dx); - ETS[StoInt(Di)][1] = StoInt(Dy); -// m++; -// System.out.println("X:"+StoInt(Dx)+"Y:"+StoInt(Dy)); -// Demons et = new Demons(StoInt(Dx),StoInt(Dy),2 ,1); -// et.SetInfo(hero, ets, bricks, irons); -// et.setLive(true); -// ets.add(et); -// System.out.println("坦克创建成功"); -// Thread t = new Thread(et); -// t.start(); -// System.out.println(t.isAlive()); - - //这里创建是没有用的,这里的函数栈内存退出后就清除了 - //虽然是传进来了一个集合的引用,但是那个引用是没有内存空间 - //所以集合只是一个指针,没有内存空间,要想创建并跑起来,只能在MyPanel3里创建并且开启线程 - //同样的道理,我的坦克也是如此,也只能用数组来做 - } - } -//************* 读取我的坦克信息 - if (L.equals("# ")) { - System.out.println("进入了我的数据读取"); - while ((L = br.readLine()) != null) { - if (L.equals("# ")) { - System.out.println("退出了我的读取"); - break; - } -// L=br.readLine(); - String hx, hy, hl, hp; - hx = L.substring(0, 3); - hy = L.substring(4, 7); - hl = L.substring(8, 11); - hp = L.substring(11, 14); - System.out.println(hx + hy + hl + hp); -// hero = new Hero(StoInt(hx), StoInt(hy), 3, ets, bricks, irons); -// hero.setLife(StoInt(hl)); -// hero.setPrize(StoInt(hp)); - myself[0] = StoInt(hx); - myself[1] = StoInt(hy); - myself[2] = StoInt(hl); - myself[3] = StoInt(hp); - } - } - //读取砖块 - } - } catch (Exception e) { - log.error("", e); - System.out.println("读取数据有异常"); - } finally { - try { - //后打开,先关闭 - input.close(); - ids.close(); - br.close(); - System.out.println("全部关闭了输入流"); - - } catch (Exception e2) { - e2.printStackTrace(); - System.out.println("有异常"); - } - } - } - - /** - * 将String转换成int - * 只读取字符串从右边第一个数字开始到左边第一个空格结束读取 - * 例如:" 123 4 5 "---> 5 - */ - public int StoInt(String s) { - int I = 0; - int bit; - bit = 1; - for (int i = 0; i < s.length(); i++) {//获取的length是实际长度要减一 - if (s.charAt(s.length() - i - 1) != ' ') { - I += (s.charAt(s.length() - i - 1) - 48) * bit; - bit *= 10; - } - - } - return I; - } - - // public static void main(String []e){ - public void readDataBase() { - PreparedStatement ps = null; - Connection cn = null; - ResultSet rs = null; - - try { - //初始化我们的对象 - //1 加载驱动 - Class.forName("com.mysql.jdbc.Driver"); - //2 得到连接 - cn = DriverManager.getConnection("jdbc:mysql://localhost:3306/Tank?user=myth&password=ad&userUnicode=true&characterEncoding=UTF8"); - //创建一个preparedStatement对象用于发送 - - -// ps = cn.prepareStatement("create table Demons(x int,y int)"); -// ps.execute(); - - //查询表 函数主要功能的实现 - ps = cn.prepareStatement("select * from demons"); - rs = ps.executeQuery(); - System.out.println("X Y"); - int nums = 0; - while (rs.next()) { - ETS[nums][0] = rs.getInt(1); - ETS[nums][1] = rs.getInt(2); - nums++; - System.out.println(rs.getInt(1) + " " + rs.getInt(2)); - } - rs.close(); - ps.close(); - //关闭上面资源,连接自己坦克的表 读入 - ps = cn.prepareStatement("select * from hero"); - rs = ps.executeQuery(); - rs.next(); - for (int i = 0; i < 4; i++) { - myself[i] = rs.getInt(i + 1); - } - - } catch (Exception e1) { - e1.printStackTrace(); - } finally { - //关闭资源 - try { - if (rs != null) - rs.close(); - if (ps != null) - ps.close(); - if (cn != null) - cn.close(); - } catch (SQLException e1) { - // TODO Auto-generated catch block - e1.printStackTrace(); - } - - } - - } - - public void saveDataBase() { - PreparedStatement ps = null; - Connection cn = null; - ResultSet rs = null; - - try { - //初始化我们的对象 - //1 加载驱动 - Class.forName("com.mysql.jdbc.Driver"); - //2 得到连接 - cn = DriverManager.getConnection("jdbc:mysql://localhost:3306/Tank?user=myth&password=ad&userUnicode=true&characterEncoding=UTF8"); - //创建一个preparedStatement对象用于发送 - EnemyTank s = null; - //在写入数据之前就要把表中数据全部删除,不然数据就溢出了 - ps = cn.prepareStatement("delete from demons where x>0"); - int deletes = ps.executeUpdate(); - if (deletes > 1) System.out.println("清除表Demons成功"); - else System.out.println("清除表Demons失败"); - if (ps != null) ps.close(); - ps = cn.prepareStatement("delete from hero where x>0"); - int deletess = ps.executeUpdate(); - if (deletess == 1) System.out.println("清除表Hero成功"); - else System.out.println("清除表Hero失败"); - if (ps != null) ps.close(); - - for (int i = 0; i < ets.size(); i++) { - if ((s = ets.get(i)) != null && s.isAlive()) { - ps = cn.prepareStatement("insert into demons values(?,?)"); - ps.setInt(1, s.getX()); - ps.setInt(2, s.getY()); - int cou = ps.executeUpdate(); - if (cou == 1) System.out.println("demons 保存成功"); - else System.out.println("demons 保存失败"); - } - } - - if (ps != null) ps.close(); - ps = cn.prepareStatement("insert into hero values(?,?,?,?)"); - ps.setInt(1, hero.getX()); - ps.setInt(2, hero.getY()); - ps.setInt(3, hero.getLife()); - ps.setInt(4, hero.getPrize()); - int hh = ps.executeUpdate(); - if (hh == 1) System.out.println("Hero 保存成功"); - else System.out.println("Hero 保存失败"); - // - } catch (Exception e) { - log.error("", e); - } finally { - //关闭资源 - try { - if (rs != null) - rs.close(); - if (ps != null) - ps.close(); - if (cn != null) - cn.close(); - } catch (SQLException e) { - // TODO Auto-generated catch block - log.error("", e); - } - - } - - } -} - +//package com.github.kuangcp.tank.util; +// +//import com.github.kuangcp.tank.domain.Brick; +//import com.github.kuangcp.tank.domain.EnemyTank; +//import com.github.kuangcp.tank.domain.Hero; +//import com.github.kuangcp.tank.domain.Iron; +//import lombok.extern.slf4j.Slf4j; +// +//import java.io.BufferedReader; +//import java.io.BufferedWriter; +//import java.io.FileInputStream; +//import java.io.FileOutputStream; +//import java.io.InputStream; +//import java.io.InputStreamReader; +//import java.io.OutputStream; +//import java.io.OutputStreamWriter; +//import java.sql.Connection; +//import java.sql.DriverManager; +//import java.sql.PreparedStatement; +//import java.sql.ResultSet; +//import java.sql.SQLException; +//import java.util.List; +// +//@Slf4j +//public class Saved { +// private Hero hero; +// private List ets; +// private List irons; +// private List bricks; +// int[][] ETS; +// int[] myself; +// +// public Saved(List ets, Hero hero, List bricks, List irons, int[][] ETS, int[] myself) { +// this.ets = ets; +// this.hero = hero; +// this.bricks = bricks; +// this.irons = irons; +// this.ETS = ETS; +// this.myself = myself; +// } +// +// //保存一切的函数 +// public void savedAll() { +// BufferedWriter bw = null; +// OutputStream out = null; +// OutputStreamWriter os = null; +// +// EnemyTank s; +// Brick b; +// Iron r; +// +// try { +// out = new FileOutputStream("./src/RE/File.txt"); +//// 同等级包下文件,用于存取,导出的时候才可以用上! +// //尚未解决!!! +//// URL j = (Saved.class.getResource("/RE/File.txt")); +//// out = new FileOutputStream(j.getPath()); +// os = new OutputStreamWriter(out); +// bw = new BufferedWriter(os); +// //<>是敌人坦克 +// +// bw.write("< \r\n"); +//// bw.write("写进去了\r\n"); +// for (int i = 0; i < ets.size(); i++) { +// if ((s = ets.get(i)) != null && s.isAlive()) { +// +// String qi = "", qx = "", qy = "";//i-2,x-3,y-3 +// +// if (i < 10) qi = i + " "; +// else qi += i; +// if (s.getX() < 100) qx = s.getX() + " "; +// else qx += s.getX(); +// if (s.getY() < 100) qy = s.getY() + " "; +// else qy += s.getY(); +// +// bw.write(qi + " " + qx + " " + qy + "\r\n"); +// } +// } +// bw.write("> \r\n"); +// //##是自己坦克 +// bw.write("# \r\n"); +//// bw.write("# \r\n"); +// String Hx = "", Hy = "", Hl = "", Hp = "";//x-3,y-3,l-2,p-3 +// +// if (hero.getX() < 100) Hx = hero.getX() + " "; +// else Hx += hero.getX(); +// if (hero.getY() < 100) Hy = hero.getY() + " "; +// else Hy += hero.getY(); +// if (hero.getLife() < 10) Hl = hero.getLife() + " "; +// else Hl += hero.getLife(); +// if (hero.getPrize() < 10) Hp = hero.getPrize() + " "; +// else if (hero.getPrize() < 100) Hp = hero.getPrize() + " "; +// else Hp += hero.getPrize(); +// +// bw.write(Hx + " " + Hy + " " + Hl + " " + Hp + "\r\n"); +// bw.write("# \r\n"); +//// //..是砖块 +//// bw.write("+\r\n"); +//// for(int i=0;i ")) { +// System.out.println("退出了敌人信息读取"); +// break; +// } +//// L=br.readLine(); +// String Di, Dx, Dy; +// +// Di = L.substring(0, 2);//(0,2] +// Dx = L.substring(3, 6); +// Dy = L.substring(7, 10); //是针对每一行的 只要有一行的长度小于这个10了就会抛越界异常 +// System.out.println(Di + "X:" + Dx + "Y:" + Dy); +// +// ETS[StoInt(Di)][0] = StoInt(Dx); +// ETS[StoInt(Di)][1] = StoInt(Dy); +//// m++; +//// System.out.println("X:"+StoInt(Dx)+"Y:"+StoInt(Dy)); +//// Demons et = new Demons(StoInt(Dx),StoInt(Dy),2 ,1); +//// et.SetInfo(hero, ets, bricks, irons); +//// et.setLive(true); +//// ets.add(et); +//// System.out.println("坦克创建成功"); +//// Thread t = new Thread(et); +//// t.start(); +//// System.out.println(t.isAlive()); +// +// //这里创建是没有用的,这里的函数栈内存退出后就清除了 +// //虽然是传进来了一个集合的引用,但是那个引用是没有内存空间 +// //所以集合只是一个指针,没有内存空间,要想创建并跑起来,只能在MyPanel3里创建并且开启线程 +// //同样的道理,我的坦克也是如此,也只能用数组来做 +// } +// } +////************* 读取我的坦克信息 +// if (L.equals("# ")) { +// System.out.println("进入了我的数据读取"); +// while ((L = br.readLine()) != null) { +// if (L.equals("# ")) { +// System.out.println("退出了我的读取"); +// break; +// } +//// L=br.readLine(); +// String hx, hy, hl, hp; +// hx = L.substring(0, 3); +// hy = L.substring(4, 7); +// hl = L.substring(8, 11); +// hp = L.substring(11, 14); +// System.out.println(hx + hy + hl + hp); +//// hero = new Hero(StoInt(hx), StoInt(hy), 3, ets, bricks, irons); +//// hero.setLife(StoInt(hl)); +//// hero.setPrize(StoInt(hp)); +// myself[0] = StoInt(hx); +// myself[1] = StoInt(hy); +// myself[2] = StoInt(hl); +// myself[3] = StoInt(hp); +// } +// } +// //读取砖块 +// } +// } catch (Exception e) { +// log.error("", e); +// System.out.println("读取数据有异常"); +// } finally { +// try { +// //后打开,先关闭 +// input.close(); +// ids.close(); +// br.close(); +// System.out.println("全部关闭了输入流"); +// +// } catch (Exception e2) { +// e2.printStackTrace(); +// System.out.println("有异常"); +// } +// } +// } +// +// /** +// * 将String转换成int +// * 只读取字符串从右边第一个数字开始到左边第一个空格结束读取 +// * 例如:" 123 4 5 "---> 5 +// */ +// public int StoInt(String s) { +// int I = 0; +// int bit; +// bit = 1; +// for (int i = 0; i < s.length(); i++) {//获取的length是实际长度要减一 +// if (s.charAt(s.length() - i - 1) != ' ') { +// I += (s.charAt(s.length() - i - 1) - 48) * bit; +// bit *= 10; +// } +// +// } +// return I; +// } +// +// // public static void main(String []e){ +// public void readDataBase() { +// PreparedStatement ps = null; +// Connection cn = null; +// ResultSet rs = null; +// +// try { +// //初始化我们的对象 +// //1 加载驱动 +// Class.forName("com.mysql.jdbc.Driver"); +// //2 得到连接 +// cn = DriverManager.getConnection("jdbc:mysql://localhost:3306/Tank?user=myth&password=ad&userUnicode=true&characterEncoding=UTF8"); +// //创建一个preparedStatement对象用于发送 +// +// +//// ps = cn.prepareStatement("create table Demons(x int,y int)"); +//// ps.execute(); +// +// //查询表 函数主要功能的实现 +// ps = cn.prepareStatement("select * from demons"); +// rs = ps.executeQuery(); +// System.out.println("X Y"); +// int nums = 0; +// while (rs.next()) { +// ETS[nums][0] = rs.getInt(1); +// ETS[nums][1] = rs.getInt(2); +// nums++; +// System.out.println(rs.getInt(1) + " " + rs.getInt(2)); +// } +// rs.close(); +// ps.close(); +// //关闭上面资源,连接自己坦克的表 读入 +// ps = cn.prepareStatement("select * from hero"); +// rs = ps.executeQuery(); +// rs.next(); +// for (int i = 0; i < 4; i++) { +// myself[i] = rs.getInt(i + 1); +// } +// +// } catch (Exception e1) { +// e1.printStackTrace(); +// } finally { +// //关闭资源 +// try { +// if (rs != null) +// rs.close(); +// if (ps != null) +// ps.close(); +// if (cn != null) +// cn.close(); +// } catch (SQLException e1) { +// // TODO Auto-generated catch block +// e1.printStackTrace(); +// } +// +// } +// +// } +// +// public void saveDataBase() { +// PreparedStatement ps = null; +// Connection cn = null; +// ResultSet rs = null; +// +// try { +// //初始化我们的对象 +// //1 加载驱动 +// Class.forName("com.mysql.jdbc.Driver"); +// //2 得到连接 +// cn = DriverManager.getConnection("jdbc:mysql://localhost:3306/Tank?user=myth&password=ad&userUnicode=true&characterEncoding=UTF8"); +// //创建一个preparedStatement对象用于发送 +// EnemyTank s = null; +// //在写入数据之前就要把表中数据全部删除,不然数据就溢出了 +// ps = cn.prepareStatement("delete from demons where x>0"); +// int deletes = ps.executeUpdate(); +// if (deletes > 1) System.out.println("清除表Demons成功"); +// else System.out.println("清除表Demons失败"); +// if (ps != null) ps.close(); +// ps = cn.prepareStatement("delete from hero where x>0"); +// int deletess = ps.executeUpdate(); +// if (deletess == 1) System.out.println("清除表Hero成功"); +// else System.out.println("清除表Hero失败"); +// if (ps != null) ps.close(); +// +// for (int i = 0; i < ets.size(); i++) { +// if ((s = ets.get(i)) != null && s.isAlive()) { +// ps = cn.prepareStatement("insert into demons values(?,?)"); +// ps.setInt(1, s.getX()); +// ps.setInt(2, s.getY()); +// int cou = ps.executeUpdate(); +// if (cou == 1) System.out.println("demons 保存成功"); +// else System.out.println("demons 保存失败"); +// } +// } +// +// if (ps != null) ps.close(); +// ps = cn.prepareStatement("insert into hero values(?,?,?,?)"); +// ps.setInt(1, hero.getX()); +// ps.setInt(2, hero.getY()); +// ps.setInt(3, hero.getLife()); +// ps.setInt(4, hero.getPrize()); +// int hh = ps.executeUpdate(); +// if (hh == 1) System.out.println("Hero 保存成功"); +// else System.out.println("Hero 保存失败"); +// // +// } catch (Exception e) { +// log.error("", e); +// } finally { +// //关闭资源 +// try { +// if (rs != null) +// rs.close(); +// if (ps != null) +// ps.close(); +// if (cn != null) +// cn.close(); +// } catch (SQLException e) { +// // TODO Auto-generated catch block +// log.error("", e); +// } +// +// } +// +// } +//} +// From 80e02dcd781b7a10df74d7667ad3d1092fe660c6 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Thu, 11 Jul 2024 00:57:02 +0800 Subject: [PATCH 414/476] wip: bullet --- .../github/kuangcp/tank/domain/AnyLife.java | 15 +++ .../github/kuangcp/tank/domain/Bullet.java | 13 +- .../github/kuangcp/tank/domain/Hinder.java | 13 +- .../com/github/kuangcp/tank/domain/Tank.java | 4 +- .../github/kuangcp/tank/frame/MainFrame.java | 2 +- .../github/kuangcp/tank/mgr/PlayStageMgr.java | 77 ++++++++++- .../kuangcp/tank/panel/TankGroundPanel.java | 120 +++--------------- .../tank/util/executor/AbstractLoopEvent.java | 3 +- 8 files changed, 122 insertions(+), 125 deletions(-) create mode 100644 gui/src/main/java/com/github/kuangcp/tank/domain/AnyLife.java diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/AnyLife.java b/gui/src/main/java/com/github/kuangcp/tank/domain/AnyLife.java new file mode 100644 index 00000000..a891194c --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/AnyLife.java @@ -0,0 +1,15 @@ +package com.github.kuangcp.tank.domain; + +import lombok.Data; + +/** + * @author Kuangcp on 2024-07-11 00:39 + */ +@Data +public abstract class AnyLife { + protected boolean alive; + + public boolean isDead() { + return !alive; + } +} diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/Bullet.java b/gui/src/main/java/com/github/kuangcp/tank/domain/Bullet.java index d4a6c2a0..6fedc02d 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/domain/Bullet.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/Bullet.java @@ -3,6 +3,8 @@ import com.github.kuangcp.tank.util.TankTool; import com.github.kuangcp.tank.util.executor.AbstractLoopEvent; import com.github.kuangcp.tank.mgr.PlayStageMgr; +import lombok.Getter; +import lombok.Setter; import lombok.extern.slf4j.Slf4j; import java.awt.*; @@ -20,24 +22,19 @@ public class Bullet extends AbstractLoopEvent implements VisualItem { public int sx; public int sy; public int direct; + @Setter + @Getter public static int speed = 3;//如果改动要记得按钮事件里也要改 public boolean alive = true;//是否还活着 public static final long fixedDelayTime = 50; public static final long delayStartTime = 50; - public static int getSpeed() { - return speed; - } - - public static void setSpeed(int s) { - speed = s; - } - public Bullet(int sx, int sy, int direct) { this.sx = sx; this.sy = sy; this.direct = direct; + this.alive = true; this.setFixedDelayTime(fixedDelayTime); } diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/Hinder.java b/gui/src/main/java/com/github/kuangcp/tank/domain/Hinder.java index 07629051..6e231c71 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/domain/Hinder.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/Hinder.java @@ -1,15 +1,17 @@ package com.github.kuangcp.tank.domain; import lombok.Data; +import lombok.EqualsAndHashCode; /** * 障碍物的最基本类 */ + @Data -public abstract class Hinder { +@EqualsAndHashCode(callSuper = false) +public abstract class Hinder extends AnyLife { int hx, hy; // 障碍物绘图坐标 左上角顶点 的坐标 - boolean alive;//存活状态 int width = 20; int height = 10; @@ -19,11 +21,6 @@ public Hinder(int hx, int hy) { this.hy = hy; } - public Boolean getAlive() { - return alive; - } - public void setAlive(Boolean alive) { - this.alive = alive; - } + } diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/Tank.java b/gui/src/main/java/com/github/kuangcp/tank/domain/Tank.java index 30bbb660..40d0d0a8 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/domain/Tank.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/Tank.java @@ -25,7 +25,6 @@ public abstract class Tank extends AbstractLoopEvent implements VisualItem { int direct = DirectType.NONE; // 初始方向 int type = 0; // 坦克的种类 int speed; // 前进的步长 - boolean alive = true;// 是否存活 boolean abort = false; // 重开一局等外部终止因素 int life = 1; //生命值 @@ -36,7 +35,7 @@ public abstract class Tank extends AbstractLoopEvent implements VisualItem { public void addLife(int delta) { this.life += delta; if (this.life <= 0) { - this.alive = false; + alive = false; } } @@ -45,6 +44,7 @@ public Tank(int x, int y, int speed) { this.x = x; this.y = y; this.speed = speed; + this.alive = true; } @Override diff --git a/gui/src/main/java/com/github/kuangcp/tank/frame/MainFrame.java b/gui/src/main/java/com/github/kuangcp/tank/frame/MainFrame.java index f00fe367..2165b01c 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/frame/MainFrame.java +++ b/gui/src/main/java/com/github/kuangcp/tank/frame/MainFrame.java @@ -31,7 +31,7 @@ @Slf4j public class MainFrame extends JFrame implements Runnable { - public volatile TankGroundPanel groundPanel;//坦克的主画板 + public volatile TankGroundPanel groundPanel;//主界面 public static StageActionPanel actionPanel = null;//放按钮的画板 public StarterPanel starterPanel; diff --git a/gui/src/main/java/com/github/kuangcp/tank/mgr/PlayStageMgr.java b/gui/src/main/java/com/github/kuangcp/tank/mgr/PlayStageMgr.java index 31c0a4b1..5e8015ef 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/mgr/PlayStageMgr.java +++ b/gui/src/main/java/com/github/kuangcp/tank/mgr/PlayStageMgr.java @@ -5,12 +5,15 @@ import com.github.kuangcp.tank.domain.Bullet; import com.github.kuangcp.tank.domain.EnemyTank; import com.github.kuangcp.tank.domain.Hero; +import com.github.kuangcp.tank.domain.Hinder; import com.github.kuangcp.tank.domain.Iron; import com.github.kuangcp.tank.domain.Tank; import com.github.kuangcp.tank.resource.DefeatImgMgr; import com.github.kuangcp.tank.resource.VictoryImgMgr; import com.github.kuangcp.tank.util.ExecutePool; import com.github.kuangcp.tank.util.TankTool; +import com.github.kuangcp.tank.util.executor.AbstractDelayEvent; +import com.github.kuangcp.tank.util.executor.DelayExecutor; import com.github.kuangcp.tank.util.executor.LoopEventExecutor; import lombok.Getter; import lombok.extern.slf4j.Slf4j; @@ -119,8 +122,80 @@ public void abortStage() { } - public void refresh(){ + public void refresh() { + bricks.removeIf(Hinder::isDead); + irons.removeIf(Iron::isDead); + // hero bullet + for (int i = 0; i < PlayStageMgr.hero.bulletList.size(); i++) { + Bullet myBullet = PlayStageMgr.hero.bulletList.get(i); + for (Brick brick : bricks) { + TankTool.judgeHint(myBullet, brick); + } + for (Iron iron : irons) { + TankTool.judgeHint(myBullet, iron); + } + if (myBullet.sx < 440 && myBullet.sx > 380 && myBullet.sy < 540 && myBullet.sy > 480) { + myBullet.alive = false; + PlayStageMgr.hero.setAlive(false); + } + } + PlayStageMgr.hero.bulletList.removeIf(Bullet::isDead); + + /*敌人子弹*/ + // FIXME ConcurrentModificationException + for (EnemyTank et : enemyList) { + for (int i = 0; i < et.bulletList.size(); i++) { + Bullet myBullet = et.bulletList.get(i); + for (Brick brick : bricks) { + TankTool.judgeHint(myBullet, brick); + } + for (Iron iron : irons) { + TankTool.judgeHint(myBullet, iron); + } + if (myBullet.sx < 440 && myBullet.sx > 380 && myBullet.sy < 540 && myBullet.sy > 480) { + myBullet.alive = false; + PlayStageMgr.hero.setAlive(false); + } + } + et.bulletList.removeIf(Bullet::isDead); + } + + //坦克少于5个就自动添加4个 + if (enemyList.size() < 5) { + for (int i = 0; i < 4; i++) { + EnemyTank d = new EnemyTank(20 + (int) (Math.random() * 400), 20 + (int) (Math.random() * 300), i % 4); + LoopEventExecutor.addLoopEvent(d); + enemyList.add(d); + } + } + + for (int i = 0; i < enemyList.size(); i++) { + EnemyTank demon = enemyList.get(i); + if (demon.isAlive()) { + BombMgr.instance.checkBong(demon, PlayStageMgr.hero.bulletList); + } else { + // TODO 去掉 延迟删除逻辑 子弹统一管理 而不是tank子属性 +// enemyList.remove(demon); + + // 延迟删除 敌人和子弹 + if (demon.delayRemove) { + continue; + } + demon.delayRemove = true; + + DelayExecutor.addEvent(new AbstractDelayEvent(7_000) { + @Override + public void run() { + enemyList.remove(demon); + } + }); + } + } + + if (PlayStageMgr.instance.hasWinCurrentRound() || !PlayStageMgr.hero.isAlive()) { + PlayStageMgr.instance.stopStage(); + } } public static void startNewRound() { diff --git a/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java b/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java index 5c3b6461..ae8a81b4 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java +++ b/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java @@ -5,6 +5,7 @@ import com.github.kuangcp.tank.domain.EnemyTank; import com.github.kuangcp.tank.domain.Iron; import com.github.kuangcp.tank.domain.StageBorder; +import com.github.kuangcp.tank.domain.Tank; import com.github.kuangcp.tank.frame.MainFrame; import com.github.kuangcp.tank.frame.SettingFrame; import com.github.kuangcp.tank.mgr.BombMgr; @@ -14,9 +15,6 @@ import com.github.kuangcp.tank.resource.ColorMgr; import com.github.kuangcp.tank.util.HoldingKeyStateMgr; import com.github.kuangcp.tank.util.TankTool; -import com.github.kuangcp.tank.util.executor.AbstractDelayEvent; -import com.github.kuangcp.tank.util.executor.DelayExecutor; -import com.github.kuangcp.tank.util.executor.LoopEventExecutor; import com.github.kuangcp.tank.util.executor.MonitorExecutor; import lombok.extern.slf4j.Slf4j; @@ -65,28 +63,19 @@ public void paint(Graphics g) { this.drawHeroInfo(g); this.drawMonitorInfo(g); - /*画出障碍物__砖__ 铁__*/ + + PlayStageMgr.instance.refresh(); g.setColor(ColorMgr.instance.ironColor); final List irons = PlayStageMgr.irons; - for (int i = 0; i < irons.size(); i++) { - Iron ir = irons.get(i); - if (ir.getAlive()) { - g.fill3DRect(ir.getHx(), ir.getHy(), ir.getWidth(), ir.getHeight(), false); - } else { - irons.remove(ir); - } + for (Iron ir : irons) { + g.fill3DRect(ir.getHx(), ir.getHy(), ir.getWidth(), ir.getHeight(), false); } g.setColor(ColorMgr.instance.brickColor); final List bricks = PlayStageMgr.bricks; - for (int i = 0; i < bricks.size(); i++) { - Brick bs = bricks.get(i); - if (bs.getAlive()) { - g.fill3DRect(bs.getHx(), bs.getHy(), bs.getWidth(), bs.getHeight(), false); - } else { - bricks.remove(bs); - } + for (Brick bs : bricks) { + g.fill3DRect(bs.getHx(), bs.getHy(), bs.getWidth(), bs.getHeight(), false); } /*画出头像*/ @@ -104,100 +93,23 @@ public void paint(Graphics g) { PlayStageMgr.hero.drawSelf(g); } - for (int i = 0; i < PlayStageMgr.hero.bulletList.size(); i++) { - Bullet myBullet = PlayStageMgr.hero.bulletList.get(i); - for (Brick brick : bricks) { - TankTool.judgeHint(myBullet, brick); - } - for (Iron iron : irons) { - TankTool.judgeHint(myBullet, iron); - } - if (myBullet.sx < 440 && myBullet.sx > 380 && myBullet.sy < 540 && myBullet.sy > 480) { - myBullet.alive = false; - PlayStageMgr.hero.setAlive(false); - } - if (PlayStageMgr.hero.bulletList.get(i) != null && PlayStageMgr.hero.bulletList.get(i).alive) { - g.setColor(Color.YELLOW); - g.draw3DRect(myBullet.sx, myBullet.sy, 3, 3, false); - } - - //子弹线程死了 就要把它从集合中删除 - if (!myBullet.alive) { - PlayStageMgr.hero.bulletList.remove(myBullet); - } + // hero bullet + for (Bullet bullet : PlayStageMgr.hero.bulletList) { + g.setColor(Color.YELLOW); + g.draw3DRect(bullet.sx, bullet.sy, 3, 3, false); } - /*敌人子弹*/ + enemyList.stream().filter(Tank::isAlive).forEach(t -> t.drawSelf(g)); + // FIXME ConcurrentModificationException for (EnemyTank et : enemyList) { - for (int i = 0; i < et.bulletList.size(); i++) { - Bullet myBullet = et.bulletList.get(i); - for (Brick brick : bricks) { - TankTool.judgeHint(myBullet, brick); - } - for (Iron iron : irons) { - TankTool.judgeHint(myBullet, iron); - } - if (myBullet.sx < 440 && myBullet.sx > 380 && myBullet.sy < 540 && myBullet.sy > 480) { - myBullet.alive = false; - PlayStageMgr.hero.setAlive(false); - } - if (et.bulletList.get(i) != null && et.bulletList.get(i).alive) { - g.setColor(Color.cyan); - g.draw3DRect(myBullet.sx, myBullet.sy, 1, 1, false); - - } - if (!myBullet.alive) { - et.bulletList.remove(myBullet); - } + for (Bullet bullet : et.bulletList) { + g.setColor(Color.cyan); + g.draw3DRect(bullet.sx, bullet.sy, 1, 1, false); } } - BombMgr.instance.drawBomb(g, this); - /*画出敌人坦克*/ - //坦克少于5个就自动添加4个 - if (enemyList.size() < 5) { - for (int i = 0; i < 4; i++) { - EnemyTank d = new EnemyTank(20 + (int) (Math.random() * 400), 20 + (int) (Math.random() * 300), i % 4); - LoopEventExecutor.addLoopEvent(d); - enemyList.add(d); - } - } - - for (int i = 0; i < enemyList.size(); i++) { - EnemyTank demon; - try { - demon = enemyList.get(i); - } catch (IndexOutOfBoundsException e) { - log.error("", e); - continue; - } - - //存活再画出来 - if (demon.isAlive()) { - BombMgr.instance.checkBong(demon, PlayStageMgr.hero.bulletList); - - demon.drawSelf(g); - } else { - // TODO 去掉 延迟删除逻辑 -// enemyList.remove(demon); - - // 延迟删除 敌人和子弹 - if (demon.delayRemove) { - continue; - } - demon.delayRemove = true; - - DelayExecutor.addEvent(new AbstractDelayEvent(7_000) { - @Override - public void run() { - enemyList.remove(demon); - } - }); - } - } - if (PlayStageMgr.instance.hasWinCurrentRound() || !PlayStageMgr.hero.isAlive()) { PlayStageMgr.instance.stopStage(); } diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/executor/AbstractLoopEvent.java b/gui/src/main/java/com/github/kuangcp/tank/util/executor/AbstractLoopEvent.java index 407894c5..24e12cac 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/util/executor/AbstractLoopEvent.java +++ b/gui/src/main/java/com/github/kuangcp/tank/util/executor/AbstractLoopEvent.java @@ -1,5 +1,6 @@ package com.github.kuangcp.tank.util.executor; +import com.github.kuangcp.tank.domain.AnyLife; import lombok.extern.slf4j.Slf4j; import java.util.Objects; @@ -11,7 +12,7 @@ * @author Kuangcp on 2021-09-16 01:44 */ @Slf4j -public abstract class AbstractLoopEvent implements LoopEvent { +public abstract class AbstractLoopEvent extends AnyLife implements LoopEvent { private static final long defaultDelay = 20; /** From 5f90931e10fa9bab48f941875f81d3b337339c21 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Sat, 13 Jul 2024 01:20:37 +0800 Subject: [PATCH 415/476] feat: ws sdk --- gui/pom.xml | 12 ++ .../com/github/kuangcp/tank/MainTankGame.java | 15 +- .../java/com/github/kuangcp/tank/ws/Readme.md | 2 +- .../github/kuangcp/tank/ws/WsBizHandler.java | 43 ++++ .../kuangcp/tank/ws/WsChannelInitializer.java | 36 ---- .../kuangcp/tank/ws/WsChannelSupervise.java | 51 ----- .../com/github/kuangcp/tank/ws/WsConst.java | 10 - .../com/github/kuangcp/tank/ws/WsHandler.java | 190 ------------------ .../com/github/kuangcp/tank/ws/WsServer.java | 50 ----- gui/src/main/resources/tank/client/main.js | 2 +- pom.xml | 8 +- 11 files changed, 77 insertions(+), 342 deletions(-) create mode 100644 gui/src/main/java/com/github/kuangcp/tank/ws/WsBizHandler.java delete mode 100644 gui/src/main/java/com/github/kuangcp/tank/ws/WsChannelInitializer.java delete mode 100644 gui/src/main/java/com/github/kuangcp/tank/ws/WsChannelSupervise.java delete mode 100644 gui/src/main/java/com/github/kuangcp/tank/ws/WsConst.java delete mode 100644 gui/src/main/java/com/github/kuangcp/tank/ws/WsHandler.java delete mode 100644 gui/src/main/java/com/github/kuangcp/tank/ws/WsServer.java diff --git a/gui/pom.xml b/gui/pom.xml index f086950c..a05bd7ea 100644 --- a/gui/pom.xml +++ b/gui/pom.xml @@ -16,6 +16,7 @@ UTF-8 UTF-8 + 1.0.5-SNAPSHOT @@ -33,6 +34,17 @@ commons-lang3 + + + + + + + com.github.kuangcp + netty-ws-core + ${ws-starter.version} + + com.fasterxml.jackson.core jackson-core diff --git a/gui/src/main/java/com/github/kuangcp/tank/MainTankGame.java b/gui/src/main/java/com/github/kuangcp/tank/MainTankGame.java index 4bb3f8f4..60300ed4 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/MainTankGame.java +++ b/gui/src/main/java/com/github/kuangcp/tank/MainTankGame.java @@ -3,7 +3,9 @@ import com.github.kuangcp.tank.frame.MainFrame; import com.github.kuangcp.tank.resource.ResourceMgr; import com.github.kuangcp.tank.util.executor.MonitorExecutor; -import com.github.kuangcp.tank.ws.WsServer; +import com.github.kuangcp.tank.ws.WsBizHandler; +import com.github.kuangcp.websocket.WsServer; +import com.github.kuangcp.websocket.WsServerConfig; import lombok.extern.slf4j.Slf4j; import java.awt.*; @@ -22,11 +24,20 @@ public static void main(String[] args) { ResourceMgr.loadResource(); log.info("finish load resources"); - new Thread(() -> new WsServer().init()).start(); + startWsServer(); EventQueue.invokeLater(new MainFrame()); } catch (Exception e) { log.error("", e); } } + + private static void startWsServer() { + final WsServerConfig config = new WsServerConfig(); + final WsServer wsServer = new WsServer(config, new WsBizHandler(config)); + final Thread wsThread = new Thread(wsServer::start); + wsThread.setDaemon(true); + wsThread.setName("ws-server"); + wsThread.start(); + } } diff --git a/gui/src/main/java/com/github/kuangcp/tank/ws/Readme.md b/gui/src/main/java/com/github/kuangcp/tank/ws/Readme.md index 36b439b8..0fc4eff8 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/ws/Readme.md +++ b/gui/src/main/java/com/github/kuangcp/tank/ws/Readme.md @@ -1,7 +1,7 @@ # 远程通信 > [Source](https://github.com/Kuangcp/JavaBase/tree/master/netty/src/main/java/netty/websocket) - +> [改进组件 netty-ws](https://github.com/Kuangcp/netty-ws-starter) TODO diff --git a/gui/src/main/java/com/github/kuangcp/tank/ws/WsBizHandler.java b/gui/src/main/java/com/github/kuangcp/tank/ws/WsBizHandler.java new file mode 100644 index 00000000..68ad6188 --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/ws/WsBizHandler.java @@ -0,0 +1,43 @@ +package com.github.kuangcp.tank.ws; + +import com.github.kuangcp.tank.mgr.PlayStageMgr; +import com.github.kuangcp.tank.util.HoldingKeyStateMgr; +import com.github.kuangcp.tank.util.Json; +import com.github.kuangcp.tank.ws.msg.MsgPack; +import com.github.kuangcp.websocket.WsServerConfig; +import com.github.kuangcp.websocket.handler.AbstractBizHandler; +import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.BooleanUtils; + +/** + * @author Kuangcp on 2024-07-13 00:40 + */ +@Slf4j +@ChannelHandler.Sharable +public class WsBizHandler extends AbstractBizHandler { + + public WsBizHandler(WsServerConfig config) { + super(config); + } + + @Override + public void connectSuccess(Long userId) { + log.info("CONN userId={}", userId); + } + + @Override + protected void textWebSocketFrameHandler(ChannelHandlerContext ctx, TextWebSocketFrame frame) { + final String text = frame.text(); + final MsgPack pack = Json.fromJson(text, MsgPack.class); + + HoldingKeyStateMgr.instance.handleJoy(pack.getDirect()); + if (BooleanUtils.isTrue(pack.getShot())) { + PlayStageMgr.instance.hero.shotEnemy(); + } + + ctx.channel().writeAndFlush("OK"); + } +} diff --git a/gui/src/main/java/com/github/kuangcp/tank/ws/WsChannelInitializer.java b/gui/src/main/java/com/github/kuangcp/tank/ws/WsChannelInitializer.java deleted file mode 100644 index 3fc34da0..00000000 --- a/gui/src/main/java/com/github/kuangcp/tank/ws/WsChannelInitializer.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.github.kuangcp.tank.ws; - -import io.netty.channel.AdaptiveRecvByteBufAllocator; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.socket.SocketChannel; -import io.netty.handler.codec.http.HttpObjectAggregator; -import io.netty.handler.codec.http.HttpServerCodec; -import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler; -import io.netty.handler.logging.LoggingHandler; -import io.netty.handler.stream.ChunkedWriteHandler; - -import static io.netty.handler.codec.http.HttpHeaders.Names.WEBSOCKET_PROTOCOL; - -/** - * @author Kuangcp on 2021-05-18 08:33 - */ -public class WsChannelInitializer extends ChannelInitializer { - - /** - * @see AdaptiveRecvByteBufAllocator.HandleImpl#record(int) 实现扩缩容读写ByteBuf - */ - @Override - protected void initChannel(SocketChannel ch) { - ChannelPipeline pipeline = ch.pipeline(); - pipeline.addLast("logging", new LoggingHandler("DEBUG"));//设置log监听器,并且日志级别为debug,方便观察运行流程 - pipeline.addLast("http-codec", new HttpServerCodec());//设置解码器 - pipeline.addLast("aggregator", new HttpObjectAggregator(65536));//聚合器,使用websocket会用到 - pipeline.addLast("http-chunked", new ChunkedWriteHandler());//用于大数据的分区传输 - pipeline.addLast("handler", new WsHandler());//自定义的业务handler - - // checkStartsWith 为true 支持路径带参数 - // maxFrameSize 设置的是最大可申请的ByteBuf,实际上使用时是按需申请和回收内存 - pipeline.addLast("", new WebSocketServerProtocolHandler(WsConst.webSocketPath, WEBSOCKET_PROTOCOL, true, 65536, false, true)); - } -} diff --git a/gui/src/main/java/com/github/kuangcp/tank/ws/WsChannelSupervise.java b/gui/src/main/java/com/github/kuangcp/tank/ws/WsChannelSupervise.java deleted file mode 100644 index 39296cc4..00000000 --- a/gui/src/main/java/com/github/kuangcp/tank/ws/WsChannelSupervise.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.github.kuangcp.tank.ws; - -import io.netty.channel.Channel; -import io.netty.channel.ChannelId; -import io.netty.channel.group.ChannelGroup; -import io.netty.channel.group.DefaultChannelGroup; -import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; -import io.netty.util.concurrent.GlobalEventExecutor; -import lombok.extern.slf4j.Slf4j; - -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; - -/** - * @author Kuangcp on 2021-05-18 08:34 - */ -@Slf4j -public class WsChannelSupervise { - - private static final ChannelGroup GLOBAL_GROUP = new DefaultChannelGroup( - GlobalEventExecutor.INSTANCE); - private static final ConcurrentMap CHANNEL_MAP = new ConcurrentHashMap<>(); - - public static void addChannel(Channel channel) { - GLOBAL_GROUP.add(channel); - CHANNEL_MAP.put(channel.id().asShortText(), channel.id()); - printState(); - } - - public static void removeChannel(Channel channel) { - GLOBAL_GROUP.remove(channel); - CHANNEL_MAP.remove(channel.id().asShortText()); - printState(); - } - - public static Channel findChannel(String id) { - return GLOBAL_GROUP.find(CHANNEL_MAP.get(id)); - } - - public static void send2All(TextWebSocketFrame tws) { - GLOBAL_GROUP.writeAndFlush(tws); - } - - private static void printState() { - log.debug("channelSize={} global={}", CHANNEL_MAP.size(), GLOBAL_GROUP.size()); - } - - public static void watchState() { -// log.info("online {}", CHANNEL_MAP.size()); - } -} diff --git a/gui/src/main/java/com/github/kuangcp/tank/ws/WsConst.java b/gui/src/main/java/com/github/kuangcp/tank/ws/WsConst.java deleted file mode 100644 index d019bd25..00000000 --- a/gui/src/main/java/com/github/kuangcp/tank/ws/WsConst.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.github.kuangcp.tank.ws; - -/** - * - * @author Kuangcp - * 2024-02-28 20:12 - */ -public interface WsConst { - String webSocketPath = "/ws"; -} diff --git a/gui/src/main/java/com/github/kuangcp/tank/ws/WsHandler.java b/gui/src/main/java/com/github/kuangcp/tank/ws/WsHandler.java deleted file mode 100644 index 969ba94b..00000000 --- a/gui/src/main/java/com/github/kuangcp/tank/ws/WsHandler.java +++ /dev/null @@ -1,190 +0,0 @@ -package com.github.kuangcp.tank.ws; - - -import com.github.kuangcp.tank.mgr.PlayStageMgr; -import com.github.kuangcp.tank.util.HoldingKeyStateMgr; -import com.github.kuangcp.tank.util.Json; -import com.github.kuangcp.tank.ws.msg.MsgPack; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.Channel; -import io.netty.channel.ChannelFuture; -import io.netty.channel.ChannelFutureListener; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.SimpleChannelInboundHandler; -import io.netty.handler.codec.http.DefaultFullHttpResponse; -import io.netty.handler.codec.http.FullHttpRequest; -import io.netty.handler.codec.http.HttpUtil; -import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame; -import io.netty.handler.codec.http.websocketx.PingWebSocketFrame; -import io.netty.handler.codec.http.websocketx.PongWebSocketFrame; -import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; -import io.netty.handler.codec.http.websocketx.WebSocketFrame; -import io.netty.util.CharsetUtil; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.BooleanUtils; -import org.apache.commons.lang3.StringUtils; - -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -/** - * @author Kuangcp on 2021-05-18 08:33 - */ -@Slf4j -public class WsHandler extends SimpleChannelInboundHandler { - - private static final Map userMap = new ConcurrentHashMap<>(); - - @Override - public void channelActive(ChannelHandlerContext ctx) throws Exception { - log.info("客户端加入连接:{}", ctx.channel()); - WsChannelSupervise.addChannel(ctx.channel()); - } - - @Override - public void channelInactive(ChannelHandlerContext ctx) throws Exception { - log.debug("客户端断开连接:" + ctx.channel()); - WsChannelSupervise.removeChannel(ctx.channel()); - } - - @Override - public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { - ctx.flush(); - } - - /** - * 拒绝不合法的请求,并返回错误信息 - */ - private static void sendHttpResponse(ChannelHandlerContext ctx, FullHttpRequest req, DefaultFullHttpResponse res) { - // 返回应答给客户端 - if (res.status().code() != 200) { - ByteBuf buf = Unpooled.copiedBuffer(res.status().toString(), CharsetUtil.UTF_8); - res.content().writeBytes(buf); - buf.release(); - } - ChannelFuture f = ctx.channel().writeAndFlush(res); - // 如果是非Keep-Alive,关闭连接 - if (!HttpUtil.isKeepAlive(req) || res.status().code() != 200) { - f.addListener(ChannelFutureListener.CLOSE); - } - } - - - /** - * 将路径参数转换成Map对象,如果路径参数出现重复参数名,将以最后的参数值为准 - * - * @param uri 传入的携带参数的路径 - */ - public static Map getParams(String uri) { - Map params = new HashMap<>(10); - - int idx = uri.indexOf("?"); - if (idx != -1) { - String[] paramsArr = uri.substring(idx + 1).split("&"); - - for (String param : paramsArr) { - idx = param.indexOf("="); - params.put(param.substring(0, idx), param.substring(idx + 1)); - } - } - - return params; - } - - /** - * 获取URI中参数以外部分路径 - */ - public static String getBasePath(String uri) { - if (uri == null || uri.isEmpty()) - return null; - - int idx = uri.indexOf("?"); - if (idx == -1) - return uri; - - return uri.substring(0, idx); - } - - - /** - * 唯一的一次http请求,用于升级至websocket 需要正确响应 - */ - private void fullHttpRequestHandler(ChannelHandlerContext ctx, FullHttpRequest request) { - String uri = request.uri(); - Map params = getParams(uri); -// log.info("客户端请求参数:{}", params); - - String userIdStr = params.get("userId"); - if (StringUtils.isNotBlank(userIdStr)) { - long userId = Long.parseLong(userIdStr); - userMap.put(userId, ctx.channel()); - } - - // 判断请求路径是否跟配置中的一致 - if (WsConst.webSocketPath.equals(getBasePath(uri))) { - // 因为有可能携带了参数,导致客户端一直无法返回握手包,因此在校验通过后,重置请求路径 - request.setUri(WsConst.webSocketPath); - } else { - ctx.close(); - } - } - - @Override - protected void channelRead0(ChannelHandlerContext ctx, WebSocketFrame frame) { - if (frame instanceof PingWebSocketFrame) { - pingWebSocketFrameHandler(ctx, (PingWebSocketFrame) frame); - } else if (frame instanceof TextWebSocketFrame) { - textWebSocketFrameHandler(ctx, (TextWebSocketFrame) frame); - } else if (frame instanceof CloseWebSocketFrame) { - closeWebSocketFrameHandler(ctx, (CloseWebSocketFrame) frame); - } - } - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { -// log.info("客户端请求数据类型:{}", msg.getClass()); - if (msg instanceof FullHttpRequest) { - fullHttpRequestHandler(ctx, (FullHttpRequest) msg); - } - super.channelRead(ctx, msg); - } - - - /** - * 客户端发送断开请求处理 - * - * @param ctx - * @param frame - */ - private void closeWebSocketFrameHandler(ChannelHandlerContext ctx, CloseWebSocketFrame frame) { - ctx.close(); - } - - /** - * 创建连接之后,客户端发送的消息都会在这里处理 - * - * @param ctx - * @param frame - */ - private void textWebSocketFrameHandler(ChannelHandlerContext ctx, TextWebSocketFrame frame) { - final String text = frame.text(); - final MsgPack pack = Json.fromJson(text, MsgPack.class); - - HoldingKeyStateMgr.instance.handleJoy(pack.getDirect()); - if (BooleanUtils.isTrue(pack.getShot())) { - PlayStageMgr.instance.hero.shotEnemy(); - } - -// ctx.channel().writeAndFlush(frame.retain()); - ctx.channel().writeAndFlush("OK"); - } - - /** - * 处理客户端心跳包 - */ - private void pingWebSocketFrameHandler(ChannelHandlerContext ctx, PingWebSocketFrame frame) { - ctx.channel().writeAndFlush(new PongWebSocketFrame(frame.content().retain())); - } -} diff --git a/gui/src/main/java/com/github/kuangcp/tank/ws/WsServer.java b/gui/src/main/java/com/github/kuangcp/tank/ws/WsServer.java deleted file mode 100644 index 45c91857..00000000 --- a/gui/src/main/java/com/github/kuangcp/tank/ws/WsServer.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.github.kuangcp.tank.ws; - -import io.netty.bootstrap.ServerBootstrap; -import io.netty.channel.Channel; -import io.netty.channel.nio.NioEventLoopGroup; -import io.netty.channel.socket.nio.NioServerSocketChannel; -import lombok.extern.slf4j.Slf4j; - -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; - -/** - * 客户端使用 client.html - * 压测可使用 https://github.com/Kuangcp/GoBase/tree/master/toolbox/web-socket - * - * @author Kuangcp on 2021-05-18 08:32 - */ -@Slf4j -public class WsServer { - - public void init() { - ScheduledExecutorService pool = Executors.newScheduledThreadPool(1); - pool.scheduleAtFixedRate(WsChannelSupervise::watchState, 5, 3, TimeUnit.SECONDS); - - log.info("正在启动WebSocket服务器"); - NioEventLoopGroup boss = new NioEventLoopGroup(); - NioEventLoopGroup work = new NioEventLoopGroup(); - try { - ServerBootstrap bootstrap = new ServerBootstrap(); - bootstrap.group(boss, work); - bootstrap.channel(NioServerSocketChannel.class); - bootstrap.childHandler(new WsChannelInitializer()); - Channel channel = bootstrap.bind(7094).sync().channel(); - log.info("WebSocket服务器启动成功:{}", channel); - channel.closeFuture().sync(); - } catch (InterruptedException e) { - log.info("", e); - } finally { - boss.shutdownGracefully(); - work.shutdownGracefully(); - log.info("WebSocket服务器已关闭"); - } - } - - public static void main(String[] args) { - new WsServer().init(); - } -} - diff --git a/gui/src/main/resources/tank/client/main.js b/gui/src/main/resources/tank/client/main.js index 4904ca6d..006b2adc 100644 --- a/gui/src/main/resources/tank/client/main.js +++ b/gui/src/main/resources/tank/client/main.js @@ -54,7 +54,7 @@ var wsUsable =false; function connected() { try { wsUsable = false; - let ws = new WebSocket("ws://192.168.1.102:7094/ws?userId=120"); + let ws = new WebSocket("ws://192.168.1.103:7094/ws?uid=120"); ws.onopen = function () { console.log('connected'); // 等 open 函数回调后才可以调用 send,否则会报错 no longer object diff --git a/pom.xml b/pom.xml index fd04fef5..c9664645 100644 --- a/pom.xml +++ b/pom.xml @@ -488,6 +488,13 @@ true + + github + https://maven.pkg.github.com/Kuangcp/netty-ws-starter + + true + + gitee kuangcp gitee @@ -498,6 +505,5 @@ Public online Restlet repository https://maven.restlet.talend.com - From b93d8f6ce8f2cc89b558fd0a065ca61c5b2c4b2f Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Sat, 13 Jul 2024 11:02:11 +0800 Subject: [PATCH 416/476] fix: repeat code --- .../kuangcp/tank/backup/v1/MainPanelV1.java | 6 +- .../kuangcp/tank/backup/v2/MainPanelV2.java | 6 +- .../github/kuangcp/tank/domain/AnyLife.java | 2 +- .../github/kuangcp/tank/domain/Bullet.java | 69 ++------ .../github/kuangcp/tank/domain/EnemyTank.java | 25 +-- .../com/github/kuangcp/tank/domain/Hero.java | 34 +--- .../kuangcp/tank/domain/MoveLoopEvent.java | 37 +++++ .../com/github/kuangcp/tank/domain/Tank.java | 67 ++++---- .../kuangcp/tank/frame/SettingFrame.java | 18 +-- .../com/github/kuangcp/tank/mgr/BombMgr.java | 16 +- .../github/kuangcp/tank/mgr/PlayStageMgr.java | 20 +-- .../kuangcp/tank/panel/StageActionPanel.java | 6 +- .../kuangcp/tank/panel/TankGroundPanel.java | 4 +- .../com/github/kuangcp/tank/util/StrUtil.java | 147 ++++++++++++++++++ .../github/kuangcp/tank/util/TankTool.java | 4 +- .../tank/util/executor/AbstractLoopEvent.java | 21 +-- .../tank/util/LoopEventExecutorTest.java | 5 +- 17 files changed, 291 insertions(+), 196 deletions(-) create mode 100644 gui/src/main/java/com/github/kuangcp/tank/domain/MoveLoopEvent.java create mode 100644 gui/src/main/java/com/github/kuangcp/tank/util/StrUtil.java diff --git a/gui/src/main/java/com/github/kuangcp/tank/backup/v1/MainPanelV1.java b/gui/src/main/java/com/github/kuangcp/tank/backup/v1/MainPanelV1.java index 6690de3d..3b64c659 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/backup/v1/MainPanelV1.java +++ b/gui/src/main/java/com/github/kuangcp/tank/backup/v1/MainPanelV1.java @@ -78,9 +78,9 @@ public void drawSelf(Graphics g) { }.drawSelf(g); //画出子弹 - if (hero.bullet != null) { - g.draw3DRect(hero.bullet.sx, hero.bullet.sy, 1, 1, false); - } +// if (hero.bullet != null) { +// g.draw3DRect(hero.bullet.sx, hero.bullet.sy, 1, 1, false); +// } //画出敌人坦克 for (EnemyTank s : ets) { diff --git a/gui/src/main/java/com/github/kuangcp/tank/backup/v2/MainPanelV2.java b/gui/src/main/java/com/github/kuangcp/tank/backup/v2/MainPanelV2.java index 0cbf983a..d968e709 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/backup/v2/MainPanelV2.java +++ b/gui/src/main/java/com/github/kuangcp/tank/backup/v2/MainPanelV2.java @@ -66,9 +66,9 @@ public void paint(Graphics g) { hero.drawSelf(g); //画出子弹 - if (hero.bullet != null) { - g.draw3DRect(hero.bullet.sx, hero.bullet.sy, 1, 1, false); - } +// if (hero.bullet != null) { +// g.draw3DRect(hero.bullet.sx, hero.bullet.sy, 1, 1, false); +// } //画出敌人坦克 for (EnemyTank s : ets) { diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/AnyLife.java b/gui/src/main/java/com/github/kuangcp/tank/domain/AnyLife.java index a891194c..8a7dbeb0 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/domain/AnyLife.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/AnyLife.java @@ -7,7 +7,7 @@ */ @Data public abstract class AnyLife { - protected boolean alive; + public boolean alive; public boolean isDead() { return !alive; diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/Bullet.java b/gui/src/main/java/com/github/kuangcp/tank/domain/Bullet.java index 6fedc02d..f4541a63 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/domain/Bullet.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/Bullet.java @@ -1,10 +1,6 @@ package com.github.kuangcp.tank.domain; -import com.github.kuangcp.tank.util.TankTool; -import com.github.kuangcp.tank.util.executor.AbstractLoopEvent; import com.github.kuangcp.tank.mgr.PlayStageMgr; -import lombok.Getter; -import lombok.Setter; import lombok.extern.slf4j.Slf4j; import java.awt.*; @@ -16,98 +12,53 @@ * TODO 抽象类,多种子弹 */ @Slf4j -public class Bullet extends AbstractLoopEvent implements VisualItem { +public class Bullet extends MoveLoopEvent implements VisualItem { public int tankId; - public int sx; - public int sy; - public int direct; - @Setter - @Getter - public static int speed = 3;//如果改动要记得按钮事件里也要改 - public boolean alive = true;//是否还活着 - public static final long fixedDelayTime = 50; public static final long delayStartTime = 50; public Bullet(int sx, int sy, int direct) { - this.sx = sx; - this.sy = sy; + this.x = sx; + this.y = sy; this.direct = direct; this.alive = true; + this.speed = 6; this.setFixedDelayTime(fixedDelayTime); } @Override public void run() { - newRun(); - } - - private void newRun() { if (PlayStageMgr.pause) { return; } switch (direct) { //上下左右 case 0: - sy -= speed; + y -= speed; break; case 1: - sy += speed; + y += speed; break; case 2: - sx -= speed; + x -= speed; break; case 3: - sx += speed; + x += speed; break; } - final boolean hitHome = sx < 440 && sx > 380 && sy < 540 && sy > 480; + final boolean hitHome = x < 440 && x > 380 && y < 540 && y > 480; //判断子弹是否碰到边缘 或者命中基地 if (PlayStageMgr.instance.willInBorder(this) || hitHome) { this.alive = false; this.stop(); } - // log.info("bullet die"); - } - - private void originRun() { - do { - // 每个子弹发射的延迟运动的时间 - TankTool.yieldMsTime(55); - - switch (direct) { - //上下左右 - case 0: - sy -= speed; - break; - case 1: - sy += speed; - break; - case 2: - sx -= speed; - break; - case 3: - sx += speed; - break; - } - - //判断子弹是否碰到边缘 - if (sx < 20 || sx > 740 || sy < 20 || sy > 540) { - this.alive = false; - } - - if (sx < 440 && sx > 380 && sy < 540 && sy > 480) { - this.alive = false; - } - } while (this.alive); -// log.info("bullet die"); } @Override public void drawSelf(Graphics g) { - g.draw3DRect(this.sx, this.sy, 1, 1, false); + g.draw3DRect(this.x, this.y, 1, 1, false); } } diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java b/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java index 48c29bb5..f8ab32c5 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/EnemyTank.java @@ -4,15 +4,13 @@ import com.github.kuangcp.tank.domain.robot.EnemyActionContext; import com.github.kuangcp.tank.domain.robot.RobotRate; import com.github.kuangcp.tank.domain.robot.RoundActionEnum; -import com.github.kuangcp.tank.util.executor.LoopEventExecutor; import com.github.kuangcp.tank.mgr.PlayStageMgr; +import com.github.kuangcp.tank.util.executor.LoopEventExecutor; import lombok.extern.slf4j.Slf4j; import java.awt.*; import java.util.HashMap; -import java.util.List; import java.util.Map; -import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.atomic.AtomicLong; @@ -26,7 +24,6 @@ public class EnemyTank extends Tank implements Runnable, RobotRate { private static final AtomicLong counter = new AtomicLong(); public final long id; - public List bulletList = new CopyOnWriteArrayList<>();//子弹集合 private long lastShotTick = 0; private final long shotCDMs = 200; @@ -150,24 +147,8 @@ public void finalShotAction() { if (this.bulletList.size() >= maxLiveBullet || !this.isAlive()) { return; } - switch (this.getDirect()) { - case DirectType.UP: { - this.shot(this.getX() - 1, this.getY() - 15, DirectType.UP); - break; - } - case DirectType.DOWN: { - this.shot(this.getX() - 2, this.getY() + 15, DirectType.DOWN); - break; - } - case DirectType.LEFT: { - this.shot(this.getX() - 15 - 2, this.getY(), DirectType.LEFT); - break; - } - case DirectType.RIGHT: { - this.shot(this.getX() + 15 - 2, this.getY() - 1, DirectType.RIGHT); - break; - } - } + + this.shotBullet(); // 常规线程池 // ExecutePool.shotPool.execute(s); diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/Hero.java b/gui/src/main/java/com/github/kuangcp/tank/domain/Hero.java index 606091a9..1c0d419b 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/domain/Hero.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/Hero.java @@ -6,27 +6,23 @@ import com.github.kuangcp.tank.util.HoldingKeyStateMgr; import com.github.kuangcp.tank.util.Roll; import com.github.kuangcp.tank.util.TankTool; -import com.github.kuangcp.tank.util.executor.LoopEventExecutor; import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; import java.awt.*; -import java.util.Vector; + @Slf4j @Getter @Setter public class Hero extends Tank { - //子弹集合 - public Vector bulletList = new Vector<>(); private long lastShotTick = 0; private long shotCDMs = 268; private final int originX, originY; - public Bullet bullet = null;//子弹 private int prize = 0;//击敌个数 public int maxLiveShot = 7;//主坦克子弹线程存活的最大数 private long lastDieMs = System.currentTimeMillis(); @@ -36,6 +32,7 @@ public Hero(int x, int y, int speed) { super(x, y, speed); this.originX = x; this.originY = y; + this.direct = DirectType.UP; } @Override @@ -46,12 +43,12 @@ public void run() { if (keyEvent.hasPressMoveEvent()) { // log.info("eventGroup={}", eventGroup); - final int lastDirect = this.getDirect(); + final int lastDirect = this.direct; final int direct = keyEvent.getDirect(); final boolean ablePass = PlayStageMgr.ablePassByHinder(this); if ((ablePass || lastDirect != direct)) { - this.setDirect(direct); + this.direct = direct; if (PlayStageMgr.instance.willInBorder(this) && PlayStageMgr.instance.ableToMove(this)) { this.move(); @@ -130,28 +127,7 @@ public void shotEnemy() { return; } - //判断坦克方向来 初始化子弹的起始发射位置 - switch (this.getDirect()) { - case 0://0123 代表 上下左右 - bullet = new Bullet(this.getX() - 1, this.getY() - 15, 0); - bulletList.add(bullet); - break; - case 1: - bullet = new Bullet(this.getX() - 2, this.getY() + 15, 1); - bulletList.add(bullet); - break; - case 2: - bullet = new Bullet(this.getX() - 15 - 2, this.getY(), 2); - bulletList.add(bullet); - break; - case 3: - bullet = new Bullet(this.getX() + 15 - 2, this.getY() - 1, 3); - bulletList.add(bullet); - break; - } - //启动子弹线程 -// shotExecutePool.execute(bullet); - LoopEventExecutor.addLoopEvent(bullet); + this.shotBullet(); lastShotTick = nowMs; } diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/MoveLoopEvent.java b/gui/src/main/java/com/github/kuangcp/tank/domain/MoveLoopEvent.java new file mode 100644 index 00000000..370096b9 --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/MoveLoopEvent.java @@ -0,0 +1,37 @@ +package com.github.kuangcp.tank.domain; + +import com.github.kuangcp.tank.constant.DirectType; +import com.github.kuangcp.tank.util.executor.AbstractLoopEvent; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * @author Kuangcp on 2024-07-13 10:45 + */ + +@Data +@EqualsAndHashCode(callSuper = false) +public abstract class MoveLoopEvent extends AbstractLoopEvent { + public int x; + public int y; + public int direct = DirectType.NONE; // 初始方向 + public int speed; + + public void move() { + switch (this.direct) { + case DirectType.UP: + y -= this.speed; + break; + case DirectType.DOWN: + y += this.speed; + break; + case DirectType.LEFT: + x -= this.speed; + break; + case DirectType.RIGHT: + x += this.speed; + break; + } + } + +} diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/Tank.java b/gui/src/main/java/com/github/kuangcp/tank/domain/Tank.java index 40d0d0a8..a6e2b162 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/domain/Tank.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/Tank.java @@ -1,12 +1,14 @@ package com.github.kuangcp.tank.domain; import com.github.kuangcp.tank.constant.DirectType; -import com.github.kuangcp.tank.util.executor.AbstractLoopEvent; +import com.github.kuangcp.tank.util.executor.LoopEventExecutor; import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; import java.awt.*; +import java.util.LinkedList; +import java.util.List; import java.util.concurrent.atomic.AtomicInteger; /** @@ -16,28 +18,19 @@ @Setter @Getter @Slf4j -public abstract class Tank extends AbstractLoopEvent implements VisualItem { +public abstract class Tank extends MoveLoopEvent implements VisualItem { private static final AtomicInteger idCnt = new AtomicInteger(0); int id; - int x; // 坦克中心的横坐标 - int y; // 坦克中心的纵坐标 - int direct = DirectType.NONE; // 初始方向 int type = 0; // 坦克的种类 - int speed; // 前进的步长 boolean abort = false; // 重开一局等外部终止因素 int life = 1; //生命值 int halfWidth = 10; int halfHeight = 15; private final int wheelNum = 7; + public List bulletList = new LinkedList<>(); - public void addLife(int delta) { - this.life += delta; - if (this.life <= 0) { - alive = false; - } - } public Tank(int x, int y, int speed) { this.id = idCnt.incrementAndGet(); @@ -47,11 +40,45 @@ public Tank(int x, int y, int speed) { this.alive = true; } + public void addLife(int delta) { + this.life += delta; + if (this.life <= 0) { + alive = false; + } + } + @Override public void run() { } + public void shotBullet() { + switch (this.direct) { + case DirectType.UP: { + this.shot(this.x - 1, this.y - 15, DirectType.UP); + break; + } + case DirectType.DOWN: { + this.shot(this.x - 2, this.y + 15, DirectType.DOWN); + break; + } + case DirectType.LEFT: { + this.shot(this.x - 15 - 2, this.y, DirectType.LEFT); + break; + } + case DirectType.RIGHT: { + this.shot(this.x + 15 - 2, this.y - 1, DirectType.RIGHT); + break; + } + } + } + + private void shot(int x, int y, int direction) { + Bullet s = new Bullet(x, y, direction); + bulletList.add(s); + LoopEventExecutor.addLoopEvent(s); + } + /** * @return draw bomb */ @@ -60,22 +87,6 @@ public boolean hitByBullet() { return true; } - public void move() { - switch (this.direct) { - case DirectType.UP: - y -= this.speed; - break; - case DirectType.DOWN: - y += this.speed; - break; - case DirectType.LEFT: - x -= this.speed; - break; - case DirectType.RIGHT: - x += this.speed; - break; - } - } /** * 画出坦克的函数 XY是坦克中心的坐标,不是画图参照点 diff --git a/gui/src/main/java/com/github/kuangcp/tank/frame/SettingFrame.java b/gui/src/main/java/com/github/kuangcp/tank/frame/SettingFrame.java index 1d49334b..2826fbb5 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/frame/SettingFrame.java +++ b/gui/src/main/java/com/github/kuangcp/tank/frame/SettingFrame.java @@ -1,10 +1,9 @@ package com.github.kuangcp.tank.frame; -import com.github.kuangcp.tank.mgr.PlayStageMgr; import com.github.kuangcp.tank.constant.ButtonCommand; import com.github.kuangcp.tank.constant.SettingCommand; -import com.github.kuangcp.tank.domain.Bullet; import com.github.kuangcp.tank.domain.Hero; +import com.github.kuangcp.tank.mgr.PlayStageMgr; import lombok.extern.slf4j.Slf4j; import javax.swing.*; @@ -83,7 +82,8 @@ public SettingFrame() { public void refreshValLabel() { final Optional heroOpt = Optional.ofNullable(PlayStageMgr.instance).map(v -> v.hero); - this.valArr[0].setText(Bullet.getSpeed() + ""); +// this.valArr[0].setText(Bullet.getSpeed() + ""); + this.valArr[0].setText(""); this.valArr[1].setText(heroOpt.map(v -> v.getSpeed() + "").orElse("")); this.valArr[2].setText(heroOpt.map(v -> v.getLife() + "").orElse("")); this.valArr[3].setText(PlayStageMgr.enemySize + ""); @@ -99,12 +99,12 @@ public void actionPerformed(ActionEvent event) { SettingFrame.activeFocus(); } - if (event.getActionCommand().equals(SettingCommand.SHOT_SPEED_INCREMENT)) { - Bullet.setSpeed(Bullet.getSpeed() + 1); - } - if (event.getActionCommand().equals(SettingCommand.SHOT_SPEED_DECREMENT)) { - Bullet.setSpeed(Bullet.getSpeed() - 1); - } +// if (event.getActionCommand().equals(SettingCommand.SHOT_SPEED_INCREMENT)) { +// Bullet.setSpeed(Bullet.getSpeed() + 1); +// } +// if (event.getActionCommand().equals(SettingCommand.SHOT_SPEED_DECREMENT)) { +// Bullet.setSpeed(Bullet.getSpeed() - 1); +// } if (event.getActionCommand().equals(SettingCommand.TANK_SPEED_INCREMENT)) { PlayStageMgr.instance.hero.addSpeed(1); diff --git a/gui/src/main/java/com/github/kuangcp/tank/mgr/BombMgr.java b/gui/src/main/java/com/github/kuangcp/tank/mgr/BombMgr.java index 65abf31d..107ce18a 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/mgr/BombMgr.java +++ b/gui/src/main/java/com/github/kuangcp/tank/mgr/BombMgr.java @@ -72,10 +72,10 @@ private void checkBong(Tank tank, Bullet bullet) { switch (tank.getDirect()) { case DirectType.UP: case DirectType.DOWN: - if (tank.getX() - 10 <= bullet.sx && - tank.getX() + 10 >= bullet.sx && - tank.getY() - 15 <= bullet.sy && - tank.getY() + 15 >= bullet.sy) { + if (tank.getX() - 10 <= bullet.x && + tank.getX() + 10 >= bullet.x && + tank.getY() - 15 <= bullet.y && + tank.getY() + 15 >= bullet.y) { bullet.alive = false; final int bx = tank.getX() - tank.getHalfWidth(); final int by = tank.getY() - tank.getHalfHeight(); @@ -85,10 +85,10 @@ private void checkBong(Tank tank, Bullet bullet) { break; case DirectType.LEFT: case DirectType.RIGHT: - if (tank.getX() - 15 <= bullet.sx && - tank.getX() + 15 >= bullet.sx && - tank.getY() - 10 <= bullet.sy && - tank.getY() + 10 >= bullet.sy) { + if (tank.getX() - 15 <= bullet.x && + tank.getX() + 15 >= bullet.x && + tank.getY() - 10 <= bullet.y && + tank.getY() + 10 >= bullet.y) { bullet.alive = false; final int bx = tank.getX() - tank.getHalfHeight(); final int by = tank.getY() - tank.getHalfWidth(); diff --git a/gui/src/main/java/com/github/kuangcp/tank/mgr/PlayStageMgr.java b/gui/src/main/java/com/github/kuangcp/tank/mgr/PlayStageMgr.java index 5e8015ef..28c5bf4e 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/mgr/PlayStageMgr.java +++ b/gui/src/main/java/com/github/kuangcp/tank/mgr/PlayStageMgr.java @@ -50,7 +50,7 @@ public class PlayStageMgr { * 敌人的数量 */ @Getter - public static int enemySize = 10; + public static int enemySize = 0; /** * 无敌状态 时间 */ @@ -135,7 +135,7 @@ public void refresh() { for (Iron iron : irons) { TankTool.judgeHint(myBullet, iron); } - if (myBullet.sx < 440 && myBullet.sx > 380 && myBullet.sy < 540 && myBullet.sy > 480) { + if (myBullet.x < 440 && myBullet.x > 380 && myBullet.y < 540 && myBullet.y > 480) { myBullet.alive = false; PlayStageMgr.hero.setAlive(false); } @@ -153,7 +153,7 @@ public void refresh() { for (Iron iron : irons) { TankTool.judgeHint(myBullet, iron); } - if (myBullet.sx < 440 && myBullet.sx > 380 && myBullet.sy < 540 && myBullet.sy > 480) { + if (myBullet.x < 440 && myBullet.x > 380 && myBullet.y < 540 && myBullet.y > 480) { myBullet.alive = false; PlayStageMgr.hero.setAlive(false); } @@ -345,15 +345,15 @@ public boolean willInBorder(Tank tank) { if (Objects.isNull(tank)) { return false; } - switch (tank.getDirect()) { + switch (tank.direct) { case DirectType.UP: - return tank.getY() - tank.getHalfHeight() - tank.getSpeed() > RoundMapMgr.instance.border.getMinY(); + return tank.y - tank.getHalfHeight() - tank.getSpeed() > RoundMapMgr.instance.border.getMinY(); case DirectType.DOWN: - return tank.getY() + tank.getHalfHeight() + tank.getSpeed() < RoundMapMgr.instance.border.getMaxY(); + return tank.y + tank.getHalfHeight() + tank.getSpeed() < RoundMapMgr.instance.border.getMaxY(); case DirectType.LEFT: - return tank.getX() - tank.getHalfHeight() - tank.getSpeed() > RoundMapMgr.instance.border.getMinX(); + return tank.x - tank.getHalfHeight() - tank.getSpeed() > RoundMapMgr.instance.border.getMinX(); case DirectType.RIGHT: - return tank.getX() + tank.getHalfHeight() + tank.getSpeed() < RoundMapMgr.instance.border.getMaxX(); + return tank.x + tank.getHalfHeight() + tank.getSpeed() < RoundMapMgr.instance.border.getMaxX(); } return false; } @@ -362,8 +362,8 @@ public boolean willInBorder(Bullet bullet) { if (Objects.isNull(bullet)) { return false; } - return bullet.sx <= RoundMapMgr.instance.border.getMinX() || bullet.sx >= RoundMapMgr.instance.border.getMaxX() - || bullet.sy <= RoundMapMgr.instance.border.getMinY() || bullet.sy >= RoundMapMgr.instance.border.getMaxY(); + return bullet.x <= RoundMapMgr.instance.border.getMinX() || bullet.x >= RoundMapMgr.instance.border.getMaxX() + || bullet.y <= RoundMapMgr.instance.border.getMinY() || bullet.y >= RoundMapMgr.instance.border.getMaxY(); } public static void setEnemySize(int enemySize) { diff --git a/gui/src/main/java/com/github/kuangcp/tank/panel/StageActionPanel.java b/gui/src/main/java/com/github/kuangcp/tank/panel/StageActionPanel.java index 2ee6b6be..aaec0a80 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/panel/StageActionPanel.java +++ b/gui/src/main/java/com/github/kuangcp/tank/panel/StageActionPanel.java @@ -3,10 +3,9 @@ import com.github.kuangcp.tank.constant.ButtonCommand; -import com.github.kuangcp.tank.domain.Bullet; -import com.github.kuangcp.tank.util.Audio; import com.github.kuangcp.tank.frame.MainFrame; import com.github.kuangcp.tank.mgr.PlayStageMgr; +import com.github.kuangcp.tank.util.Audio; import lombok.extern.slf4j.Slf4j; import javax.swing.*; @@ -57,7 +56,7 @@ public void actionPerformed(ActionEvent ae) { System.out.println(ButtonCommand.START); frame.firstStart = false; PlayStageMgr.newStage = false; - Bullet.setSpeed(8); +// Bullet.setSpeed(8); // frame.remove(frame.centerPanel); EventQueue.invokeLater(frame); @@ -89,7 +88,6 @@ public void startNewStage() { frame.firstStart = false; PlayStageMgr.newStage = true; - Bullet.setSpeed(8); frame.remove(frame.getContentPane()); // log.info("start new stage frame thread"); diff --git a/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java b/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java index ae8a81b4..f5a93937 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java +++ b/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java @@ -96,7 +96,7 @@ public void paint(Graphics g) { // hero bullet for (Bullet bullet : PlayStageMgr.hero.bulletList) { g.setColor(Color.YELLOW); - g.draw3DRect(bullet.sx, bullet.sy, 3, 3, false); + g.draw3DRect(bullet.x, bullet.y, 3, 3, false); } enemyList.stream().filter(Tank::isAlive).forEach(t -> t.drawSelf(g)); @@ -105,7 +105,7 @@ public void paint(Graphics g) { for (EnemyTank et : enemyList) { for (Bullet bullet : et.bulletList) { g.setColor(Color.cyan); - g.draw3DRect(bullet.sx, bullet.sy, 1, 1, false); + g.draw3DRect(bullet.x, bullet.y, 1, 1, false); } } BombMgr.instance.drawBomb(g, this); diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/StrUtil.java b/gui/src/main/java/com/github/kuangcp/tank/util/StrUtil.java new file mode 100644 index 00000000..a709a0fe --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/util/StrUtil.java @@ -0,0 +1,147 @@ +package com.github.kuangcp.tank.util; + +import org.apache.commons.lang3.RandomStringUtils; +import org.apache.commons.lang3.StringUtils; + +import java.security.SecureRandom; +import java.util.Arrays; +import java.util.List; +import java.util.Random; +import java.util.concurrent.ThreadLocalRandom; +import java.util.stream.Stream; + +/** + * @author Kuangcp + * 2023-06-20 18:42 + */ +public class StrUtil { + + private static final List LIST = Arrays.asList("A", "a", "B", "b", "C", "c", "D", "d", "E", "e", "F", "f", + "G", "g", "H", "h", "I", "i", "J", "j", "K", "k", "L", "l", "M", "m", "N", "n", "O", "o", "P", "p", "Q", "q", + "R", "r", "S", "s", "T", "t", "U", "u", "V", "v", "W", "w", "X", "x", "Y", "y", "Z", "z"); + + private static final String[] ALPHA = new String[]{"A", "a", "B", "b", "C", "c", "D", "d", "E", "e", "F", "f", + "G", "g", "H", "h", "I", "i", "J", "j", "K", "k", "L", "l", "M", "m", "N", "n", "O", "o", "P", "p", "Q", "q", + "R", "r", "S", "s", "T", "t", "U", "u", "V", "v", "W", "w", "X", "x", "Y", "y", "Z", "z"}; + + public static final String ALPHA_STR = "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz"; + + public static final int ALPHA_STR_LEN = ALPHA_STR.length(); + + public static final String ALPHA_NUM_STR = "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789"; + public static final int ALPHA_NUM_STR_LEN = ALPHA_NUM_STR.length(); + + public static final String ASCII_STR = "!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"; + public static final int ASCII_STR_LEN = ASCII_STR.length(); + public static final int ASCII_LEN = 127 - 32; + + public static String firstNotBlankStr(String... names) { + return Stream.of(names).filter(StringUtils::isNotBlank).findFirst().orElse(""); + } + + public static String randomAlpha(int len) { + Random random = new Random(); + random.setSeed(System.nanoTime()); + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < len; i++) { + builder.append(LIST.get(random.nextInt(LIST.size()))); + } + return builder.toString(); + } + + public static String randomAlphaA(int len) { + Random random = new Random(); + random.setSeed(System.nanoTime()); + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < len; i++) { + builder.append(ALPHA[random.nextInt(LIST.size())]); + } + return builder.toString(); + } + + public static String randomAlphaAL(int len) { + ThreadLocalRandom random = ThreadLocalRandom.current(); + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < len; i++) { + builder.append(ALPHA[random.nextInt(LIST.size())]); + } + return builder.toString(); + } + + + public static String randomAlphaStrL(int len) { + ThreadLocalRandom random = ThreadLocalRandom.current(); + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < len; i++) { + builder.append(ALPHA_STR.charAt(random.nextInt(ALPHA_STR_LEN))); + } + return builder.toString(); + } + + /** + * 性能高 重复率低 + * 虽然远不及UUID的唯一性,但是性能很高,适用于瞬时唯一性要求不高的场景,例如traceId生成 + */ + public static String randomAlphaCharL(int len) { + ThreadLocalRandom random = ThreadLocalRandom.current(); + char[] tmp = new char[len]; + for (int i = 0; i < len; i++) { + tmp[i] = ALPHA_STR.charAt(random.nextInt(ALPHA_STR_LEN)); + } + return new String(tmp); + } + + public static String randomAlphaCharCalL(int len) { + ThreadLocalRandom random = ThreadLocalRandom.current(); + char[] tmp = new char[len]; + for (int i = 0; i < len; i++) { + int delta = random.nextInt(ALPHA_STR_LEN); + if (delta < 26) { + tmp[i] = (char) (65 + delta); + } else { + tmp[i] = (char) (97 + delta - 26); + } + } + return new String(tmp); + } + + + public static String randomAsciiStrL(int len) { + ThreadLocalRandom random = ThreadLocalRandom.current(); + char[] tmp = new char[len]; + for (int i = 0; i < len; i++) { + tmp[i] = ASCII_STR.charAt(random.nextInt(ASCII_STR_LEN)); + } + return new String(tmp); + } + + /** + * @see RandomStringUtils#randomAscii(int) + */ + public static String randomAsciiL(int len) { + ThreadLocalRandom random = ThreadLocalRandom.current(); + char[] tmp = new char[len]; + for (int i = 0; i < len; i++) { + tmp[i] = (char) (random.nextInt(ASCII_LEN) + 32); + } + return new String(tmp); + } + + public static String randomAlphaNumStrL(int len) { + ThreadLocalRandom random = ThreadLocalRandom.current(); + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < len; i++) { + builder.append(ALPHA_NUM_STR.charAt(random.nextInt(ALPHA_NUM_STR_LEN))); + } + return builder.toString(); + } + + public static String randomAlphaAS(int len) { + Random random = new SecureRandom(); + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < len; i++) { + builder.append(ALPHA[random.nextInt(LIST.size())]); + } + return builder.toString(); + } +} diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/TankTool.java b/gui/src/main/java/com/github/kuangcp/tank/util/TankTool.java index 07aabd3f..9d2eaf57 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/util/TankTool.java +++ b/gui/src/main/java/com/github/kuangcp/tank/util/TankTool.java @@ -228,8 +228,8 @@ public static boolean ablePass(Tank t, Hinder h) { * 子弹和障碍物 碰撞监测 */ public static void judgeHint(Bullet s, Hinder h) { - if (s.sx >= h.getHx() - 1 && s.sx <= h.getHx() + h.getWidth() - && s.sy >= h.getHy() - 1 && s.sy <= h.getHy() + h.getHeight()) { + if (s.x >= h.getHx() - 1 && s.x <= h.getHx() + h.getWidth() + && s.y >= h.getHy() - 1 && s.y <= h.getHy() + h.getHeight()) { s.alive = false; if (h instanceof Brick) { diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/executor/AbstractLoopEvent.java b/gui/src/main/java/com/github/kuangcp/tank/util/executor/AbstractLoopEvent.java index 24e12cac..72fd75fd 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/util/executor/AbstractLoopEvent.java +++ b/gui/src/main/java/com/github/kuangcp/tank/util/executor/AbstractLoopEvent.java @@ -1,12 +1,13 @@ package com.github.kuangcp.tank.util.executor; import com.github.kuangcp.tank.domain.AnyLife; +import lombok.Setter; import lombok.extern.slf4j.Slf4j; import java.util.Objects; -import java.util.UUID; import java.util.concurrent.Delayed; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; /** * @author Kuangcp on 2021-09-16 01:44 @@ -14,34 +15,28 @@ @Slf4j public abstract class AbstractLoopEvent extends AnyLife implements LoopEvent { + private static final AtomicLong counter = new AtomicLong(); private static final long defaultDelay = 20; + // 构建任务的补充参数 /** - * 时间id + * 事件id */ - public String id; + public Long id; /** * 下次事件触发时机 */ long nextTickTime; + @Setter long fixedDelayTime = 40; Runnable stopHook; public AbstractLoopEvent() { - this.id = UUID.randomUUID().toString(); + this.id = counter.incrementAndGet(); this.nextTickTime = System.currentTimeMillis() + defaultDelay; } - // 构建任务的补充参数 - public void setId(String id) { - this.id = id; - } - - public void setFixedDelayTime(long fixedDelayTime) { - this.fixedDelayTime = fixedDelayTime; - } - public void setDelayStart(long delayStart, TimeUnit timeUnit) { this.nextTickTime += timeUnit.toMillis(delayStart); } diff --git a/gui/src/test/java/com/github/kuangcp/tank/util/LoopEventExecutorTest.java b/gui/src/test/java/com/github/kuangcp/tank/util/LoopEventExecutorTest.java index baf5ed2f..f6934360 100644 --- a/gui/src/test/java/com/github/kuangcp/tank/util/LoopEventExecutorTest.java +++ b/gui/src/test/java/com/github/kuangcp/tank/util/LoopEventExecutorTest.java @@ -67,11 +67,10 @@ static class EventTask extends AbstractLoopEvent { public int direct = 0; public static int speed = 3;//如果改动要记得按钮事件里也要改 - public EventTask(String id, long fixedDelay, long delayStart, TimeUnit timeUnit, int x, int y) { + public EventTask(long fixedDelay, long delayStart, TimeUnit timeUnit, int x, int y) { this.sx = x; this.sy = y; - this.setId(id); this.setFixedDelayTime(fixedDelay); this.setDelayStart(delayStart, timeUnit); } @@ -141,7 +140,7 @@ public void testRun() throws Exception { @Test public void testShotBullet() throws Exception { BlockingQueue delayQueue = new DelayQueue<>(); - final EventTask originTask = new EventTask("xx", 55, 55, TimeUnit.MILLISECONDS, 60, 120); + final EventTask originTask = new EventTask( 55, 55, TimeUnit.MILLISECONDS, 60, 120); originTask.registerHook(() -> { System.out.println("end"); }); From 70fc3b9cc926ba3eb0cbedd2bb1cf46fea5fad00 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Sat, 13 Jul 2024 20:32:22 +0800 Subject: [PATCH 417/476] wip: refactor --- gui/pom.xml | 2 +- .../tank/{MainTankGame.java => TankGame.java} | 4 +++- .../github/kuangcp/tank/domain/Bullet.java | 22 +++++-------------- .../kuangcp/tank/domain/StageBorder.java | 12 ++++++++-- .../com/github/kuangcp/tank/domain/Tank.java | 5 +++-- .../kuangcp/tank/domain/VisualImgItem.java | 12 ++++++++++ .../event/DelayEvent.java} | 8 +++---- .../event/LoopEvent.java} | 17 +++++--------- .../domain/{ => event}/MoveLoopEvent.java | 5 ++--- .../github/kuangcp/tank/mgr/PlayStageMgr.java | 6 ++--- .../github/kuangcp/tank/mgr/RoundMapMgr.java | 9 +++++++- .../kuangcp/tank/panel/TankGroundPanel.java | 4 +--- .../kuangcp/tank/resource/AvatarImgMgr.java | 16 ++++++++++++-- .../kuangcp/tank/resource/ResourceMgr.java | 4 ---- .../util/executor/CommonEventExecutor.java | 10 +++++---- .../tank/util/executor/DelayEvent.java | 9 -------- .../tank/util/executor/DelayExecutor.java | 5 +++-- .../kuangcp/tank/util/executor/LoopEvent.java | 22 ------------------- .../tank/util/executor/LoopEventExecutor.java | 5 +++-- .../tank/util/executor/MonitorExecutor.java | 7 +++--- gui/src/main/resources/tank/client/main.js | 2 +- .../tank/util/LoopEventExecutorTest.java | 4 ++-- 22 files changed, 91 insertions(+), 99 deletions(-) rename gui/src/main/java/com/github/kuangcp/tank/{MainTankGame.java => TankGame.java} (92%) create mode 100644 gui/src/main/java/com/github/kuangcp/tank/domain/VisualImgItem.java rename gui/src/main/java/com/github/kuangcp/tank/{util/executor/AbstractDelayEvent.java => domain/event/DelayEvent.java} (77%) rename gui/src/main/java/com/github/kuangcp/tank/{util/executor/AbstractLoopEvent.java => domain/event/LoopEvent.java} (87%) rename gui/src/main/java/com/github/kuangcp/tank/domain/{ => event}/MoveLoopEvent.java (82%) delete mode 100644 gui/src/main/java/com/github/kuangcp/tank/util/executor/DelayEvent.java delete mode 100644 gui/src/main/java/com/github/kuangcp/tank/util/executor/LoopEvent.java diff --git a/gui/pom.xml b/gui/pom.xml index a05bd7ea..5e43571c 100644 --- a/gui/pom.xml +++ b/gui/pom.xml @@ -62,7 +62,7 @@ true - com.github.kuangcp.tank.MainTankGame + com.github.kuangcp.tank.TankGame tank diff --git a/gui/src/main/java/com/github/kuangcp/tank/MainTankGame.java b/gui/src/main/java/com/github/kuangcp/tank/TankGame.java similarity index 92% rename from gui/src/main/java/com/github/kuangcp/tank/MainTankGame.java rename to gui/src/main/java/com/github/kuangcp/tank/TankGame.java index 60300ed4..eacd18ef 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/MainTankGame.java +++ b/gui/src/main/java/com/github/kuangcp/tank/TankGame.java @@ -1,6 +1,7 @@ package com.github.kuangcp.tank; import com.github.kuangcp.tank.frame.MainFrame; +import com.github.kuangcp.tank.mgr.RoundMapMgr; import com.github.kuangcp.tank.resource.ResourceMgr; import com.github.kuangcp.tank.util.executor.MonitorExecutor; import com.github.kuangcp.tank.ws.WsBizHandler; @@ -11,7 +12,7 @@ import java.awt.*; @Slf4j -public class MainTankGame { +public class TankGame { /** * -Xmx300m -Xms300m -XX:+UseG1GC @@ -22,6 +23,7 @@ public static void main(String[] args) { MonitorExecutor.init(); try { ResourceMgr.loadResource(); + RoundMapMgr.init(); log.info("finish load resources"); startWsServer(); diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/Bullet.java b/gui/src/main/java/com/github/kuangcp/tank/domain/Bullet.java index f4541a63..f1813a76 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/domain/Bullet.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/Bullet.java @@ -1,6 +1,8 @@ package com.github.kuangcp.tank.domain; +import com.github.kuangcp.tank.domain.event.MoveLoopEvent; import com.github.kuangcp.tank.mgr.PlayStageMgr; +import com.github.kuangcp.tank.mgr.RoundMapMgr; import lombok.extern.slf4j.Slf4j; import java.awt.*; @@ -33,25 +35,11 @@ public void run() { if (PlayStageMgr.pause) { return; } - switch (direct) { - //上下左右 - case 0: - y -= speed; - break; - case 1: - y += speed; - break; - case 2: - x -= speed; - break; - case 3: - x += speed; - break; - } - final boolean hitHome = x < 440 && x > 380 && y < 540 && y > 480; + this.move(); + //判断子弹是否碰到边缘 或者命中基地 - if (PlayStageMgr.instance.willInBorder(this) || hitHome) { + if (PlayStageMgr.instance.willInBorder(this) || RoundMapMgr.instance.border.hitHome(x, y)) { this.alive = false; this.stop(); } diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/StageBorder.java b/gui/src/main/java/com/github/kuangcp/tank/domain/StageBorder.java index 01ad3b1f..b50c9c12 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/domain/StageBorder.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/StageBorder.java @@ -1,17 +1,22 @@ package com.github.kuangcp.tank.domain; -import lombok.Getter; +import lombok.Data; /** * @author Kuangcp on 2021-09-21 23:35 */ -@Getter +@Data public class StageBorder { private final int minX; private final int maxX; private final int minY; private final int maxY; + private int homeX; + private int homeY; + private int homeW; + private int homeH; + public StageBorder(int minX, int maxX, int minY, int maxY) { this.minX = minX; this.maxX = maxX; @@ -27,4 +32,7 @@ public int getTotalY() { return minY + maxY; } + public boolean hitHome(int x, int y) { + return x < homeX + homeW && x > homeX && y < homeY + homeH && y > homeY; + } } diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/Tank.java b/gui/src/main/java/com/github/kuangcp/tank/domain/Tank.java index a6e2b162..ccd501f2 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/domain/Tank.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/Tank.java @@ -1,12 +1,14 @@ package com.github.kuangcp.tank.domain; import com.github.kuangcp.tank.constant.DirectType; +import com.github.kuangcp.tank.domain.event.MoveLoopEvent; import com.github.kuangcp.tank.util.executor.LoopEventExecutor; import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; import java.awt.*; +import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; @@ -29,8 +31,7 @@ public abstract class Tank extends MoveLoopEvent implements VisualItem { int halfWidth = 10; int halfHeight = 15; private final int wheelNum = 7; - public List bulletList = new LinkedList<>(); - + public List bulletList = Collections.synchronizedList(new LinkedList<>()); public Tank(int x, int y, int speed) { this.id = idCnt.incrementAndGet(); diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/VisualImgItem.java b/gui/src/main/java/com/github/kuangcp/tank/domain/VisualImgItem.java new file mode 100644 index 00000000..4d4131b2 --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/VisualImgItem.java @@ -0,0 +1,12 @@ +package com.github.kuangcp.tank.domain; + +import java.awt.*; +import java.awt.image.ImageObserver; + +/** + * @author Kuangcp on 2024-07-13 20:21 + */ +public interface VisualImgItem { + + void drawSelf(Graphics g, ImageObserver observer); +} diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/executor/AbstractDelayEvent.java b/gui/src/main/java/com/github/kuangcp/tank/domain/event/DelayEvent.java similarity index 77% rename from gui/src/main/java/com/github/kuangcp/tank/util/executor/AbstractDelayEvent.java rename to gui/src/main/java/com/github/kuangcp/tank/domain/event/DelayEvent.java index 84a5cbaa..e4290a70 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/util/executor/AbstractDelayEvent.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/event/DelayEvent.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.tank.util.executor; +package com.github.kuangcp.tank.domain.event; import java.util.concurrent.Delayed; import java.util.concurrent.TimeUnit; @@ -6,14 +6,14 @@ /** * @author Kuangcp on 2021-09-17 01:39 */ -public abstract class AbstractDelayEvent implements DelayEvent { +public abstract class DelayEvent implements Runnable, Delayed { /** * 下次事件触发时机 */ - long delayTime; + public long delayTime; - public AbstractDelayEvent(long delayTime) { + public DelayEvent(long delayTime) { this.delayTime = delayTime + System.currentTimeMillis(); } diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/executor/AbstractLoopEvent.java b/gui/src/main/java/com/github/kuangcp/tank/domain/event/LoopEvent.java similarity index 87% rename from gui/src/main/java/com/github/kuangcp/tank/util/executor/AbstractLoopEvent.java rename to gui/src/main/java/com/github/kuangcp/tank/domain/event/LoopEvent.java index 72fd75fd..905c4be5 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/util/executor/AbstractLoopEvent.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/event/LoopEvent.java @@ -1,4 +1,4 @@ -package com.github.kuangcp.tank.util.executor; +package com.github.kuangcp.tank.domain.event; import com.github.kuangcp.tank.domain.AnyLife; import lombok.Setter; @@ -13,7 +13,7 @@ * @author Kuangcp on 2021-09-16 01:44 */ @Slf4j -public abstract class AbstractLoopEvent extends AnyLife implements LoopEvent { +public abstract class LoopEvent extends AnyLife implements Runnable, Delayed { private static final AtomicLong counter = new AtomicLong(); private static final long defaultDelay = 20; @@ -25,14 +25,14 @@ public abstract class AbstractLoopEvent extends AnyLife implements LoopEvent { /** * 下次事件触发时机 */ - long nextTickTime; + public long nextTickTime; @Setter - long fixedDelayTime = 40; + public long fixedDelayTime = 40; - Runnable stopHook; + public Runnable stopHook; - public AbstractLoopEvent() { + public LoopEvent() { this.id = counter.incrementAndGet(); this.nextTickTime = System.currentTimeMillis() + defaultDelay; } @@ -42,12 +42,10 @@ public void setDelayStart(long delayStart, TimeUnit timeUnit) { } // 业务动作 - @Override public void addDelay(long delay) { this.nextTickTime += delay; } - @Override public boolean addFixedDelay() { if (fixedDelayTime <= 0) { return false; @@ -56,17 +54,14 @@ public boolean addFixedDelay() { return isContinue(); } - @Override public boolean isStop() { return this.nextTickTime <= 0; } - @Override public boolean isContinue() { return !isStop(); } - @Override public void stop() { // log.info("{} stop", this); this.nextTickTime = 0; diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/MoveLoopEvent.java b/gui/src/main/java/com/github/kuangcp/tank/domain/event/MoveLoopEvent.java similarity index 82% rename from gui/src/main/java/com/github/kuangcp/tank/domain/MoveLoopEvent.java rename to gui/src/main/java/com/github/kuangcp/tank/domain/event/MoveLoopEvent.java index 370096b9..8341ffb9 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/domain/MoveLoopEvent.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/event/MoveLoopEvent.java @@ -1,7 +1,6 @@ -package com.github.kuangcp.tank.domain; +package com.github.kuangcp.tank.domain.event; import com.github.kuangcp.tank.constant.DirectType; -import com.github.kuangcp.tank.util.executor.AbstractLoopEvent; import lombok.Data; import lombok.EqualsAndHashCode; @@ -11,7 +10,7 @@ @Data @EqualsAndHashCode(callSuper = false) -public abstract class MoveLoopEvent extends AbstractLoopEvent { +public abstract class MoveLoopEvent extends LoopEvent { public int x; public int y; public int direct = DirectType.NONE; // 初始方向 diff --git a/gui/src/main/java/com/github/kuangcp/tank/mgr/PlayStageMgr.java b/gui/src/main/java/com/github/kuangcp/tank/mgr/PlayStageMgr.java index 28c5bf4e..be251cfa 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/mgr/PlayStageMgr.java +++ b/gui/src/main/java/com/github/kuangcp/tank/mgr/PlayStageMgr.java @@ -12,7 +12,7 @@ import com.github.kuangcp.tank.resource.VictoryImgMgr; import com.github.kuangcp.tank.util.ExecutePool; import com.github.kuangcp.tank.util.TankTool; -import com.github.kuangcp.tank.util.executor.AbstractDelayEvent; +import com.github.kuangcp.tank.domain.event.DelayEvent; import com.github.kuangcp.tank.util.executor.DelayExecutor; import com.github.kuangcp.tank.util.executor.LoopEventExecutor; import lombok.Getter; @@ -67,8 +67,8 @@ public class PlayStageMgr { public Map heroMap = new ConcurrentHashMap<>(); - //TODO 渲染在一个线程里,不用考虑并发安全 public static List enemyList; + // todo 转移 public static List bricks; public static List irons; @@ -184,7 +184,7 @@ public void refresh() { } demon.delayRemove = true; - DelayExecutor.addEvent(new AbstractDelayEvent(7_000) { + DelayExecutor.addEvent(new DelayEvent(7_000) { @Override public void run() { enemyList.remove(demon); diff --git a/gui/src/main/java/com/github/kuangcp/tank/mgr/RoundMapMgr.java b/gui/src/main/java/com/github/kuangcp/tank/mgr/RoundMapMgr.java index 4c4dc8f4..f4ee6be2 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/mgr/RoundMapMgr.java +++ b/gui/src/main/java/com/github/kuangcp/tank/mgr/RoundMapMgr.java @@ -3,6 +3,7 @@ import com.github.kuangcp.tank.domain.Brick; import com.github.kuangcp.tank.domain.Iron; import com.github.kuangcp.tank.domain.StageBorder; +import com.github.kuangcp.tank.resource.AvatarImgMgr; import java.util.List; @@ -18,7 +19,13 @@ public class RoundMapMgr { public StageBorder border = null; public static void init() { - instance.border = new StageBorder(20, 740, 20, 540); + final RoundMapMgr roundMap = instance; + roundMap.border = new StageBorder(20, 740, 20, 540); + roundMap.border.setHomeX(380); + roundMap.border.setHomeY(480); + roundMap.border.setHomeW(AvatarImgMgr.instance.width); + roundMap.border.setHomeH(AvatarImgMgr.instance.height); + // instance.border = new StageBorder(20, 1600, 20, 900); } } diff --git a/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java b/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java index f5a93937..15df0bb3 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java +++ b/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java @@ -79,9 +79,7 @@ public void paint(Graphics g) { } /*画出头像*/ - // fixme 像素偏移 - g.drawImage(AvatarImgMgr.instance.curImg, 380, 480, - AvatarImgMgr.instance.width, AvatarImgMgr.instance.height, this); + AvatarImgMgr.instance.drawSelf(g, this); /*画出主坦克*/ final List enemyList = PlayStageMgr.enemyList; diff --git a/gui/src/main/java/com/github/kuangcp/tank/resource/AvatarImgMgr.java b/gui/src/main/java/com/github/kuangcp/tank/resource/AvatarImgMgr.java index 3cd58dd8..c766452b 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/resource/AvatarImgMgr.java +++ b/gui/src/main/java/com/github/kuangcp/tank/resource/AvatarImgMgr.java @@ -1,12 +1,18 @@ package com.github.kuangcp.tank.resource; +import com.github.kuangcp.tank.domain.StageBorder; +import com.github.kuangcp.tank.domain.VisualImgItem; +import com.github.kuangcp.tank.mgr.RoundMapMgr; import lombok.extern.slf4j.Slf4j; +import java.awt.*; +import java.awt.image.ImageObserver; + /** * @author Kuangcp on 2021-09-11 16:41 */ @Slf4j -public class AvatarImgMgr extends AbstractImgListMgr { +public class AvatarImgMgr extends AbstractImgListMgr implements VisualImgItem { public static final AvatarImgMgr instance = new AvatarImgMgr(); @@ -15,7 +21,13 @@ public AvatarImgMgr() { super.height = 60; } - public String getConfigKey(){ + public String getConfigKey() { return PropertiesMgr.Key.Img.ROUND_AVATAR; } + + @Override + public void drawSelf(Graphics g, ImageObserver observer) { + final StageBorder border = RoundMapMgr.instance.border; + g.drawImage(instance.curImg, border.getHomeX(), border.getHomeY(), instance.width, instance.height, observer); + } } diff --git a/gui/src/main/java/com/github/kuangcp/tank/resource/ResourceMgr.java b/gui/src/main/java/com/github/kuangcp/tank/resource/ResourceMgr.java index 3f49393f..99973658 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/resource/ResourceMgr.java +++ b/gui/src/main/java/com/github/kuangcp/tank/resource/ResourceMgr.java @@ -1,7 +1,6 @@ package com.github.kuangcp.tank.resource; import com.github.kuangcp.tank.mgr.BombMgr; -import com.github.kuangcp.tank.mgr.RoundMapMgr; import lombok.extern.slf4j.Slf4j; import java.io.IOException; @@ -17,12 +16,9 @@ public static void loadResource() throws IOException { log.info("[init] start load resource"); - // image BombMgr.instance.loadImg(); DefeatImgMgr.instance.loadImg(); AvatarImgMgr.instance.loadImg(); VictoryImgMgr.instance.loadImg(); - - RoundMapMgr.init(); } } diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/executor/CommonEventExecutor.java b/gui/src/main/java/com/github/kuangcp/tank/util/executor/CommonEventExecutor.java index 9443bbbe..11fa3121 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/util/executor/CommonEventExecutor.java +++ b/gui/src/main/java/com/github/kuangcp/tank/util/executor/CommonEventExecutor.java @@ -1,5 +1,7 @@ package com.github.kuangcp.tank.util.executor; +import com.github.kuangcp.tank.domain.event.DelayEvent; +import com.github.kuangcp.tank.domain.event.LoopEvent; import lombok.extern.slf4j.Slf4j; import java.util.concurrent.BlockingQueue; @@ -14,10 +16,10 @@ public class CommonEventExecutor { /** * 事件循环任务 */ - public static void loopEventSpin(String type, BlockingQueue queue) { + public static void loopEventSpin(String type, BlockingQueue queue) { while (true) { try { - final AbstractLoopEvent event = queue.take(); + final LoopEvent event = queue.take(); final long delay = event.getDelay(TimeUnit.MILLISECONDS); // 事件延迟调度警告,且过滤掉初次运行任务 if (delay < -200 && delay > -1000_000) { @@ -42,10 +44,10 @@ public static void loopEventSpin(String type, BlockingQueue q /** * 单次延迟任务 */ - public static void delayEventSpin(BlockingQueue queue) { + public static void delayEventSpin(BlockingQueue queue) { while (true) { try { - final AbstractDelayEvent event = queue.take(); + final DelayEvent event = queue.take(); event.run(); } catch (InterruptedException e) { log.error("invoke delay event error", e); diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/executor/DelayEvent.java b/gui/src/main/java/com/github/kuangcp/tank/util/executor/DelayEvent.java deleted file mode 100644 index 905dd9f8..00000000 --- a/gui/src/main/java/com/github/kuangcp/tank/util/executor/DelayEvent.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.github.kuangcp.tank.util.executor; - -import java.util.concurrent.Delayed; - -/** - * @author Kuangcp on 2021-09-17 01:35 - */ -public interface DelayEvent extends Runnable, Delayed { -} diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/executor/DelayExecutor.java b/gui/src/main/java/com/github/kuangcp/tank/util/executor/DelayExecutor.java index c9ef0d27..ac51d57c 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/util/executor/DelayExecutor.java +++ b/gui/src/main/java/com/github/kuangcp/tank/util/executor/DelayExecutor.java @@ -1,5 +1,6 @@ package com.github.kuangcp.tank.util.executor; +import com.github.kuangcp.tank.domain.event.DelayEvent; import com.github.kuangcp.tank.util.ExecutePool; import java.util.concurrent.BlockingQueue; @@ -11,7 +12,7 @@ */ public class DelayExecutor { - private static final BlockingQueue queue = new DelayQueue<>(); + private static final BlockingQueue queue = new DelayQueue<>(); /** * 循环事件线程池 @@ -22,7 +23,7 @@ public static void init() { delayEventPool.execute(() -> CommonEventExecutor.delayEventSpin(queue)); } - public static void addEvent(AbstractDelayEvent event) { + public static void addEvent(DelayEvent event) { queue.add(event); } } diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/executor/LoopEvent.java b/gui/src/main/java/com/github/kuangcp/tank/util/executor/LoopEvent.java deleted file mode 100644 index ac6dce71..00000000 --- a/gui/src/main/java/com/github/kuangcp/tank/util/executor/LoopEvent.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.github.kuangcp.tank.util.executor; - -import java.util.concurrent.Delayed; - -/** - * @author Kuangcp on 2021-09-16 01:21 - */ -public interface LoopEvent extends Runnable, Delayed { - - void addDelay(long delay); - - boolean addFixedDelay(); - - /** - * 事件需要被移除 - */ - boolean isStop(); - - boolean isContinue(); - - void stop(); -} diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/executor/LoopEventExecutor.java b/gui/src/main/java/com/github/kuangcp/tank/util/executor/LoopEventExecutor.java index a254f970..93bf0284 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/util/executor/LoopEventExecutor.java +++ b/gui/src/main/java/com/github/kuangcp/tank/util/executor/LoopEventExecutor.java @@ -1,5 +1,6 @@ package com.github.kuangcp.tank.util.executor; +import com.github.kuangcp.tank.domain.event.LoopEvent; import com.github.kuangcp.tank.util.ExecutePool; import lombok.extern.slf4j.Slf4j; @@ -13,7 +14,7 @@ @Slf4j public class LoopEventExecutor { - static final BlockingQueue queue = new DelayQueue<>(); + static final BlockingQueue queue = new DelayQueue<>(); private static final int EVENT_POOL_SIZE = 8; @@ -28,7 +29,7 @@ public static void init() { } } - public static void addLoopEvent(AbstractLoopEvent loopEvent) { + public static void addLoopEvent(LoopEvent loopEvent) { // log.info("add: loopEvent={}", loopEvent); queue.add(loopEvent); } diff --git a/gui/src/main/java/com/github/kuangcp/tank/util/executor/MonitorExecutor.java b/gui/src/main/java/com/github/kuangcp/tank/util/executor/MonitorExecutor.java index 1238222d..a9a73d5b 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/util/executor/MonitorExecutor.java +++ b/gui/src/main/java/com/github/kuangcp/tank/util/executor/MonitorExecutor.java @@ -1,5 +1,6 @@ package com.github.kuangcp.tank.util.executor; +import com.github.kuangcp.tank.domain.event.LoopEvent; import com.github.kuangcp.tank.util.ExecutePool; import lombok.extern.slf4j.Slf4j; @@ -14,7 +15,7 @@ public class MonitorExecutor { public static final Info info = new Info(); - private static final BlockingQueue queue = new DelayQueue<>(); + private static final BlockingQueue queue = new DelayQueue<>(); private static final ExecutorService monitorEventPool = ExecutePool.buildFixedPool("monitorEvent", 1); public static void init() { @@ -39,7 +40,7 @@ public String toString() { } private static void registerEventMonitor() { - final AbstractLoopEvent loopEventMonitor = new AbstractLoopEvent() { + final LoopEvent loopEventMonitor = new LoopEvent() { @Override public void run() { info.loopEventCount = LoopEventExecutor.queue.size(); @@ -50,7 +51,7 @@ public void run() { queue.add(loopEventMonitor); } - public static void addLoopEvent(AbstractLoopEvent loopEvent) { + public static void addLoopEvent(LoopEvent loopEvent) { queue.add(loopEvent); } } diff --git a/gui/src/main/resources/tank/client/main.js b/gui/src/main/resources/tank/client/main.js index 006b2adc..024cbc1f 100644 --- a/gui/src/main/resources/tank/client/main.js +++ b/gui/src/main/resources/tank/client/main.js @@ -54,7 +54,7 @@ var wsUsable =false; function connected() { try { wsUsable = false; - let ws = new WebSocket("ws://192.168.1.103:7094/ws?uid=120"); + let ws = new WebSocket("ws://192.168.1.104:7094/ws?uid=120"); ws.onopen = function () { console.log('connected'); // 等 open 函数回调后才可以调用 send,否则会报错 no longer object diff --git a/gui/src/test/java/com/github/kuangcp/tank/util/LoopEventExecutorTest.java b/gui/src/test/java/com/github/kuangcp/tank/util/LoopEventExecutorTest.java index f6934360..00d53c72 100644 --- a/gui/src/test/java/com/github/kuangcp/tank/util/LoopEventExecutorTest.java +++ b/gui/src/test/java/com/github/kuangcp/tank/util/LoopEventExecutorTest.java @@ -2,7 +2,7 @@ import com.github.kuangcp.tank.domain.EnemyTank; import com.github.kuangcp.tank.domain.Hero; -import com.github.kuangcp.tank.util.executor.AbstractLoopEvent; +import com.github.kuangcp.tank.domain.event.LoopEvent; import com.github.kuangcp.tank.util.executor.LoopEventExecutor; import com.github.kuangcp.tank.mgr.PlayStageMgr; import lombok.extern.slf4j.Slf4j; @@ -61,7 +61,7 @@ public String toString() { } } - static class EventTask extends AbstractLoopEvent { + static class EventTask extends LoopEvent { public int sx; public int sy; public int direct = 0; From c776ea0ddf32bba4c31f7d361d28e37a255527d2 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Sun, 14 Jul 2024 23:39:55 +0800 Subject: [PATCH 418/476] clean --- .../com/github/kuangcp/tank/TankGame.java | 1 - .../kuangcp/tank/backup/v2/MainPanelV2.java | 3 +- .../kuangcp/tank/backup/v2/TankGameV2.java | 8 ++--- .../github/kuangcp/tank/domain/Bullet.java | 4 +-- .../github/kuangcp/tank/frame/MainFrame.java | 4 +-- .../github/kuangcp/tank/mgr/PlayStageMgr.java | 28 ++++++++++------- .../github/kuangcp/tank/mgr/RoundMapMgr.java | 31 ------------------- .../kuangcp/tank/panel/TankGroundPanel.java | 7 ++--- .../kuangcp/tank/resource/AvatarImgMgr.java | 4 +-- 9 files changed, 30 insertions(+), 60 deletions(-) delete mode 100644 gui/src/main/java/com/github/kuangcp/tank/mgr/RoundMapMgr.java diff --git a/gui/src/main/java/com/github/kuangcp/tank/TankGame.java b/gui/src/main/java/com/github/kuangcp/tank/TankGame.java index eacd18ef..714fc906 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/TankGame.java +++ b/gui/src/main/java/com/github/kuangcp/tank/TankGame.java @@ -23,7 +23,6 @@ public static void main(String[] args) { MonitorExecutor.init(); try { ResourceMgr.loadResource(); - RoundMapMgr.init(); log.info("finish load resources"); startWsServer(); diff --git a/gui/src/main/java/com/github/kuangcp/tank/backup/v2/MainPanelV2.java b/gui/src/main/java/com/github/kuangcp/tank/backup/v2/MainPanelV2.java index d968e709..3fb5452e 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/backup/v2/MainPanelV2.java +++ b/gui/src/main/java/com/github/kuangcp/tank/backup/v2/MainPanelV2.java @@ -6,7 +6,6 @@ import com.github.kuangcp.tank.domain.Hero; import com.github.kuangcp.tank.domain.StageBorder; import com.github.kuangcp.tank.mgr.PlayStageMgr; -import com.github.kuangcp.tank.mgr.RoundMapMgr; import com.github.kuangcp.tank.util.TankTool; import lombok.extern.slf4j.Slf4j; @@ -59,7 +58,7 @@ public MainPanelV2() { public void paint(Graphics g) { super.paint(g); - final StageBorder border = RoundMapMgr.instance.border; + final StageBorder border = new StageBorder(20, 740, 20, 540); g.fillRect(0, 0, border.getMaxX() + border.getMinX(), border.getMaxY() + border.getMinY()); //调用函数绘画出主坦克 diff --git a/gui/src/main/java/com/github/kuangcp/tank/backup/v2/TankGameV2.java b/gui/src/main/java/com/github/kuangcp/tank/backup/v2/TankGameV2.java index d04fcc1a..63797f70 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/backup/v2/TankGameV2.java +++ b/gui/src/main/java/com/github/kuangcp/tank/backup/v2/TankGameV2.java @@ -1,10 +1,9 @@ package com.github.kuangcp.tank.backup.v2; import com.github.kuangcp.tank.domain.Hero; -import com.github.kuangcp.tank.util.executor.MonitorExecutor; -import com.github.kuangcp.tank.mgr.PlayStageMgr; -import com.github.kuangcp.tank.mgr.RoundMapMgr; import com.github.kuangcp.tank.domain.StageBorder; +import com.github.kuangcp.tank.mgr.PlayStageMgr; +import com.github.kuangcp.tank.util.executor.MonitorExecutor; import javax.swing.*; import java.util.Collections; @@ -25,7 +24,6 @@ public static void main(String[] args) { //最外层JFrame的构造函数 public TankGameV2() { MonitorExecutor.init(); - RoundMapMgr.init(); PlayStageMgr.init(new Hero(50, 20, 5), Collections.emptyList(), Collections.emptyList(), Collections.emptyList()); MainPanelV2 panel = new MainPanelV2(); @@ -35,7 +33,7 @@ public TankGameV2() { panelThread.start(); - final StageBorder border = RoundMapMgr.instance.border; + final StageBorder border = new StageBorder(20, 740, 20, 540); this.setLocation(200, 50); this.setSize(border.getMaxX() + border.getMinX(), border.getMaxY() + border.getMinY() * 2); diff --git a/gui/src/main/java/com/github/kuangcp/tank/domain/Bullet.java b/gui/src/main/java/com/github/kuangcp/tank/domain/Bullet.java index f1813a76..884ef9e1 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/domain/Bullet.java +++ b/gui/src/main/java/com/github/kuangcp/tank/domain/Bullet.java @@ -2,7 +2,6 @@ import com.github.kuangcp.tank.domain.event.MoveLoopEvent; import com.github.kuangcp.tank.mgr.PlayStageMgr; -import com.github.kuangcp.tank.mgr.RoundMapMgr; import lombok.extern.slf4j.Slf4j; import java.awt.*; @@ -39,7 +38,8 @@ public void run() { this.move(); //判断子弹是否碰到边缘 或者命中基地 - if (PlayStageMgr.instance.willInBorder(this) || RoundMapMgr.instance.border.hitHome(x, y)) { + final PlayStageMgr stage = PlayStageMgr.instance; + if (stage.willInBorder(this) || stage.border.hitHome(x, y)) { this.alive = false; this.stop(); } diff --git a/gui/src/main/java/com/github/kuangcp/tank/frame/MainFrame.java b/gui/src/main/java/com/github/kuangcp/tank/frame/MainFrame.java index 2165b01c..fbf7a1b2 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/frame/MainFrame.java +++ b/gui/src/main/java/com/github/kuangcp/tank/frame/MainFrame.java @@ -1,6 +1,5 @@ package com.github.kuangcp.tank.frame; -import com.github.kuangcp.tank.mgr.RoundMapMgr; import com.github.kuangcp.tank.panel.StageActionPanel; import com.github.kuangcp.tank.panel.StarterPanel; import com.github.kuangcp.tank.panel.TankGroundPanel; @@ -48,7 +47,8 @@ public MainFrame() { this.setTitle("Tank"); this.setLocation(680, 290); - this.setSize(RoundMapMgr.instance.border.getTotalX(), RoundMapMgr.instance.border.getTotalY()); + // TODO 启动panel 依据 stage resize + this.setSize(760, 560); this.setUndecorated(true); } diff --git a/gui/src/main/java/com/github/kuangcp/tank/mgr/PlayStageMgr.java b/gui/src/main/java/com/github/kuangcp/tank/mgr/PlayStageMgr.java index be251cfa..1c4d8c50 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/mgr/PlayStageMgr.java +++ b/gui/src/main/java/com/github/kuangcp/tank/mgr/PlayStageMgr.java @@ -7,7 +7,9 @@ import com.github.kuangcp.tank.domain.Hero; import com.github.kuangcp.tank.domain.Hinder; import com.github.kuangcp.tank.domain.Iron; +import com.github.kuangcp.tank.domain.StageBorder; import com.github.kuangcp.tank.domain.Tank; +import com.github.kuangcp.tank.resource.AvatarImgMgr; import com.github.kuangcp.tank.resource.DefeatImgMgr; import com.github.kuangcp.tank.resource.VictoryImgMgr; import com.github.kuangcp.tank.util.ExecutePool; @@ -60,11 +62,6 @@ public class PlayStageMgr { public static volatile boolean newStage = true; public static volatile boolean invokeNewStage = false; - // 场景 上下文 -// public List enemyTanks; -// public List bricks; // 砖 -// public List irons; // 铁 - public Map heroMap = new ConcurrentHashMap<>(); public static List enemyList; @@ -72,6 +69,9 @@ public class PlayStageMgr { public static List bricks; public static List irons; + public StageBorder border = null; + + //所有按下键的code集合 public static int[][] enemyTankMap = new int[12][2]; public static int[] myself = new int[6]; @@ -98,6 +98,12 @@ public void markStartLogic() { */ public static void init(Hero hero, List enemyTanks, List bricks, List irons) { instance = new PlayStageMgr(hero, enemyTanks, bricks, irons); + + instance.border = new StageBorder(20, 740, 20, 540); + instance.border.setHomeX(380); + instance.border.setHomeY(480); + instance.border.setHomeW(AvatarImgMgr.instance.width); + instance.border.setHomeH(AvatarImgMgr.instance.height); } private PlayStageMgr(Hero hero, List enemyTanks, List bricks, List irons) { @@ -347,13 +353,13 @@ public boolean willInBorder(Tank tank) { } switch (tank.direct) { case DirectType.UP: - return tank.y - tank.getHalfHeight() - tank.getSpeed() > RoundMapMgr.instance.border.getMinY(); + return tank.y - tank.getHalfHeight() - tank.getSpeed() > border.getMinY(); case DirectType.DOWN: - return tank.y + tank.getHalfHeight() + tank.getSpeed() < RoundMapMgr.instance.border.getMaxY(); + return tank.y + tank.getHalfHeight() + tank.getSpeed() < border.getMaxY(); case DirectType.LEFT: - return tank.x - tank.getHalfHeight() - tank.getSpeed() > RoundMapMgr.instance.border.getMinX(); + return tank.x - tank.getHalfHeight() - tank.getSpeed() > border.getMinX(); case DirectType.RIGHT: - return tank.x + tank.getHalfHeight() + tank.getSpeed() < RoundMapMgr.instance.border.getMaxX(); + return tank.x + tank.getHalfHeight() + tank.getSpeed() < border.getMaxX(); } return false; } @@ -362,8 +368,8 @@ public boolean willInBorder(Bullet bullet) { if (Objects.isNull(bullet)) { return false; } - return bullet.x <= RoundMapMgr.instance.border.getMinX() || bullet.x >= RoundMapMgr.instance.border.getMaxX() - || bullet.y <= RoundMapMgr.instance.border.getMinY() || bullet.y >= RoundMapMgr.instance.border.getMaxY(); + return bullet.x <= border.getMinX() || bullet.x >= border.getMaxX() + || bullet.y <= border.getMinY() || bullet.y >= border.getMaxY(); } public static void setEnemySize(int enemySize) { diff --git a/gui/src/main/java/com/github/kuangcp/tank/mgr/RoundMapMgr.java b/gui/src/main/java/com/github/kuangcp/tank/mgr/RoundMapMgr.java deleted file mode 100644 index f4ee6be2..00000000 --- a/gui/src/main/java/com/github/kuangcp/tank/mgr/RoundMapMgr.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.github.kuangcp.tank.mgr; - -import com.github.kuangcp.tank.domain.Brick; -import com.github.kuangcp.tank.domain.Iron; -import com.github.kuangcp.tank.domain.StageBorder; -import com.github.kuangcp.tank.resource.AvatarImgMgr; - -import java.util.List; - -/** - * @author Kuangcp on 2021-09-25 15:57 - */ -public class RoundMapMgr { - - public static final RoundMapMgr instance = new RoundMapMgr(); - public List bricks; // 砖 - public List irons; // 铁 - - public StageBorder border = null; - - public static void init() { - final RoundMapMgr roundMap = instance; - roundMap.border = new StageBorder(20, 740, 20, 540); - roundMap.border.setHomeX(380); - roundMap.border.setHomeY(480); - roundMap.border.setHomeW(AvatarImgMgr.instance.width); - roundMap.border.setHomeH(AvatarImgMgr.instance.height); - -// instance.border = new StageBorder(20, 1600, 20, 900); - } -} diff --git a/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java b/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java index 15df0bb3..d7d34ebd 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java +++ b/gui/src/main/java/com/github/kuangcp/tank/panel/TankGroundPanel.java @@ -10,7 +10,6 @@ import com.github.kuangcp.tank.frame.SettingFrame; import com.github.kuangcp.tank.mgr.BombMgr; import com.github.kuangcp.tank.mgr.PlayStageMgr; -import com.github.kuangcp.tank.mgr.RoundMapMgr; import com.github.kuangcp.tank.resource.AvatarImgMgr; import com.github.kuangcp.tank.resource.ColorMgr; import com.github.kuangcp.tank.util.HoldingKeyStateMgr; @@ -29,7 +28,7 @@ public class TankGroundPanel extends JPanel implements java.awt.event.KeyListene private void drawBg(Graphics g) { g.setColor(ColorMgr.instance.bgColor); - final StageBorder border = RoundMapMgr.instance.border; + final StageBorder border = PlayStageMgr.instance.border; g.fillRect(0, 0, border.getMinX() + border.getMaxX(), border.getMinY() + border.getMaxY()); g.setColor(Color.green); g.drawRect(border.getMinX(), border.getMinY(), @@ -41,13 +40,13 @@ private void drawHeroInfo(Graphics g) { final String lifeInfo = "Life:" + PlayStageMgr.hero.getLife() + " Enemy: " + PlayStageMgr.instance.getLiveEnemy() + " Prize: " + PlayStageMgr.hero.getPrize(); - g.drawString(lifeInfo, RoundMapMgr.instance.border.getMinX(), 15); + g.drawString(lifeInfo, PlayStageMgr.instance.border.getMinX(), 15); } private void drawMonitorInfo(Graphics g) { g.setColor(Color.LIGHT_GRAY); g.drawString(MonitorExecutor.info.toString(), - RoundMapMgr.instance.border.getMinX(), RoundMapMgr.instance.border.getTotalY() - 3); + PlayStageMgr.instance.border.getMinX(), PlayStageMgr.instance.border.getTotalY() - 3); } @Override diff --git a/gui/src/main/java/com/github/kuangcp/tank/resource/AvatarImgMgr.java b/gui/src/main/java/com/github/kuangcp/tank/resource/AvatarImgMgr.java index c766452b..e7a7bd3b 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/resource/AvatarImgMgr.java +++ b/gui/src/main/java/com/github/kuangcp/tank/resource/AvatarImgMgr.java @@ -2,7 +2,7 @@ import com.github.kuangcp.tank.domain.StageBorder; import com.github.kuangcp.tank.domain.VisualImgItem; -import com.github.kuangcp.tank.mgr.RoundMapMgr; +import com.github.kuangcp.tank.mgr.PlayStageMgr; import lombok.extern.slf4j.Slf4j; import java.awt.*; @@ -27,7 +27,7 @@ public String getConfigKey() { @Override public void drawSelf(Graphics g, ImageObserver observer) { - final StageBorder border = RoundMapMgr.instance.border; + final StageBorder border = PlayStageMgr.instance.border; g.drawImage(instance.curImg, border.getHomeX(), border.getHomeY(), instance.width, instance.height, observer); } } From 5d4b30bdf812dcc02c87c71586947ca127419cb0 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Mon, 15 Jul 2024 00:09:44 +0800 Subject: [PATCH 419/476] compile --- gui/pom.xml | 2 +- gui/src/main/java/com/github/kuangcp/tank/TankGame.java | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/gui/pom.xml b/gui/pom.xml index 5e43571c..baf82289 100644 --- a/gui/pom.xml +++ b/gui/pom.xml @@ -16,7 +16,7 @@ UTF-8 UTF-8 - 1.0.5-SNAPSHOT + 1.0.5-RELEASE diff --git a/gui/src/main/java/com/github/kuangcp/tank/TankGame.java b/gui/src/main/java/com/github/kuangcp/tank/TankGame.java index 714fc906..f148d830 100644 --- a/gui/src/main/java/com/github/kuangcp/tank/TankGame.java +++ b/gui/src/main/java/com/github/kuangcp/tank/TankGame.java @@ -1,7 +1,6 @@ package com.github.kuangcp.tank; import com.github.kuangcp.tank.frame.MainFrame; -import com.github.kuangcp.tank.mgr.RoundMapMgr; import com.github.kuangcp.tank.resource.ResourceMgr; import com.github.kuangcp.tank.util.executor.MonitorExecutor; import com.github.kuangcp.tank.ws.WsBizHandler; From 300a0fdaeece8aed55bdb7819580f61c581434ba Mon Sep 17 00:00:00 2001 From: kuangcp Date: Tue, 16 Jul 2024 22:20:15 +0800 Subject: [PATCH 420/476] fix --- .../java/netty/websocket/NioWebSocketHandler.java | 13 ++++++++++--- netty/src/main/java/netty/websocket/Readme.md | 8 ++++++-- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/netty/src/main/java/netty/websocket/NioWebSocketHandler.java b/netty/src/main/java/netty/websocket/NioWebSocketHandler.java index 28e1c7a7..ac54410a 100644 --- a/netty/src/main/java/netty/websocket/NioWebSocketHandler.java +++ b/netty/src/main/java/netty/websocket/NioWebSocketHandler.java @@ -81,6 +81,7 @@ private static void sendHttpResponse(ChannelHandlerContext ctx, FullHttpRequest /** * 将路径参数转换成Map对象,如果路径参数出现重复参数名,将以最后的参数值为准 + * * @param uri 传入的携带参数的路径 */ public static Map getParams(String uri) { @@ -117,12 +118,16 @@ public static String getBasePath(String uri) { /** * 唯一的一次http请求,用于升级至websocket 需要正确响应 */ - private void fullHttpRequestHandler(ChannelHandlerContext ctx, FullHttpRequest request) { + private void httpHandShark(ChannelHandlerContext ctx, FullHttpRequest request) { String uri = request.uri(); Map params = getParams(uri); // log.info("客户端请求参数:{}", params); + HttpHeaders headers = request.trailingHeaders(); String userIdStr = params.get("userId"); + if (StringUtils.isBlank(userIdStr)) { + userIdStr = headers.get("userId"); + } if (StringUtils.isNotBlank(userIdStr)) { long userId = Long.parseLong(userIdStr); userMap.put(userId, ctx.channel()); @@ -133,7 +138,7 @@ private void fullHttpRequestHandler(ChannelHandlerContext ctx, FullHttpRequest r // 因为有可能携带了参数,导致客户端一直无法返回握手包,因此在校验通过后,重置请求路径 request.setUri(Const.webSocketPath); } else { - ctx.close(); + throw new WebSocketHandshakeException("invalid hand shark"); } } @@ -153,7 +158,7 @@ protected void channelRead0(ChannelHandlerContext ctx, WebSocketFrame frame) { public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { // log.info("客户端请求数据类型:{}", msg.getClass()); if (msg instanceof FullHttpRequest) { - fullHttpRequestHandler(ctx, (FullHttpRequest) msg); + httpHandShark(ctx, (FullHttpRequest) msg); } super.channelRead(ctx, msg); } @@ -161,6 +166,7 @@ public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception /** * 客户端发送断开请求处理 + * * @param ctx * @param frame */ @@ -170,6 +176,7 @@ private void closeWebSocketFrameHandler(ChannelHandlerContext ctx, CloseWebSocke /** * 创建连接之后,客户端发送的消息都会在这里处理 + * * @param ctx * @param frame */ diff --git a/netty/src/main/java/netty/websocket/Readme.md b/netty/src/main/java/netty/websocket/Readme.md index 39c5f495..69a1e293 100644 --- a/netty/src/main/java/netty/websocket/Readme.md +++ b/netty/src/main/java/netty/websocket/Readme.md @@ -1,7 +1,11 @@ Netty 实现的 websocket -[SpringBoot整合Netty处理WebSocket(支持url参数)](https://blog.csdn.net/RisenMyth/article/details/104441155) +> [SpringBoot starter](https://github.com/Kuangcp/netty-ws-starter) + -https://blog.csdn.net/linuu/article/details/51404264 +[SpringBoot整合Netty处理WebSocket(支持url参数)](https://blog.csdn.net/RisenMyth/article/details/104441155) TODO 对比 Tomcat实现 进行性能测试(固定 JVM 参数 相同硬件,测试最大连接数,失败数,GC情况,延迟) + + + From e34f1dc30cd63eee698b0beafabf71b11bf2777c Mon Sep 17 00:00:00 2001 From: kuangcp Date: Fri, 9 Aug 2024 15:24:03 +0800 Subject: [PATCH 421/476] metaspace --- class/src/main/java/jvm/oom/MetaspaceOOM.java | 16 +++++++ .../test/java/jvm/oom/MetaspaceOOMTest.java | 20 +++++++++ .../kuangcp/time/DateTimeFormatterTest.java | 17 +++---- .../bio/chattingroom/ClientThread.java | 45 ++++++++++--------- 4 files changed, 68 insertions(+), 30 deletions(-) diff --git a/class/src/main/java/jvm/oom/MetaspaceOOM.java b/class/src/main/java/jvm/oom/MetaspaceOOM.java index aebe9051..f9140225 100644 --- a/class/src/main/java/jvm/oom/MetaspaceOOM.java +++ b/class/src/main/java/jvm/oom/MetaspaceOOM.java @@ -6,6 +6,7 @@ import java.lang.reflect.Method; import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.concurrent.TimeUnit; @@ -74,4 +75,19 @@ public static void jacksonAndReflect() throws Exception { } } + public static void anonymousClass() { + log.info("run"); + for (int i = 0; i < 10000000; i++) { + invokeOnce(); + } + } + + public static void invokeOnce() { + HashMap map = new HashMap() {{ + put("A", "B"); + }}; + String val = map.get("A"); + val = "xxx"; + } + } diff --git a/class/src/test/java/jvm/oom/MetaspaceOOMTest.java b/class/src/test/java/jvm/oom/MetaspaceOOMTest.java index 2c00de8d..b01d2cc5 100644 --- a/class/src/test/java/jvm/oom/MetaspaceOOMTest.java +++ b/class/src/test/java/jvm/oom/MetaspaceOOMTest.java @@ -1,12 +1,16 @@ package jvm.oom; +import lombok.extern.slf4j.Slf4j; import org.junit.Test; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; /** * @author Kuangcp on 2023-08-26 14:36 */ +@Slf4j public class MetaspaceOOMTest { /** @@ -32,4 +36,20 @@ public void testArthasInject() throws Exception { // 如果是等业务方法执行完后注入,arthas会注入失败,提示OOM TimeUnit.HOURS.sleep(1); } + + /** + * 通过 每3s 千万次周期运行 可以看到metaspace有缓慢增长的趋势,但是离引发故障还差很远, + * 需要继续确认是否确实双括号实例化对象是否有匿名类泄漏问题 + */ + @Test + public void testAnonymousClass() throws Exception { + TimeUnit.SECONDS.sleep(20); + log.info("start"); + ScheduledExecutorService pool = Executors.newScheduledThreadPool(1); + pool.scheduleAtFixedRate(MetaspaceOOM::anonymousClass, 0, 3, TimeUnit.SECONDS); +// MetaspaceOOM.anonymousClass(); +// log.info("end"); + + TimeUnit.HOURS.sleep(1); + } } \ No newline at end of file diff --git a/java8/src/test/java/com/github/kuangcp/time/DateTimeFormatterTest.java b/java8/src/test/java/com/github/kuangcp/time/DateTimeFormatterTest.java index b2d6eeaa..1ce14173 100644 --- a/java8/src/test/java/com/github/kuangcp/time/DateTimeFormatterTest.java +++ b/java8/src/test/java/com/github/kuangcp/time/DateTimeFormatterTest.java @@ -1,19 +1,20 @@ package com.github.kuangcp.time; +import org.junit.Test; + import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; -import org.junit.Test; /** * @author Kuangcp on 2019-12-11 20:41 */ public class DateTimeFormatterTest { - @Test - public void testFormat(){ - LocalDateTime now = LocalDateTime.now(); - DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); - String dateStr = formatter.format(now); - System.out.println(dateStr); - } + @Test + public void testFormat() { + LocalDateTime now = LocalDateTime.now(); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + String dateStr = formatter.format(now); + System.out.println(dateStr); + } } diff --git a/network/src/main/java/com/github/kuangcp/bio/chattingroom/ClientThread.java b/network/src/main/java/com/github/kuangcp/bio/chattingroom/ClientThread.java index 3da1ef09..86a1d3f8 100644 --- a/network/src/main/java/com/github/kuangcp/bio/chattingroom/ClientThread.java +++ b/network/src/main/java/com/github/kuangcp/bio/chattingroom/ClientThread.java @@ -1,9 +1,10 @@ package com.github.kuangcp.bio.chattingroom; +import lombok.extern.slf4j.Slf4j; + import java.io.BufferedReader; import java.io.InputStreamReader; import java.net.Socket; -import lombok.extern.slf4j.Slf4j; /** * Created by Myth on 2017/4/2 0002 @@ -11,29 +12,29 @@ @Slf4j public class ClientThread implements Runnable { - private String name; - private BufferedReader br; + private String name; + private BufferedReader br; - ClientThread(String name, Socket s) throws Exception { - this.name = name; - br = new BufferedReader(new InputStreamReader(s.getInputStream())); - } + ClientThread(String name, Socket s) throws Exception { + this.name = name; + br = new BufferedReader(new InputStreamReader(s.getInputStream())); + } - @Override - public void run() { - try { - String content; - //不断读取输入流中内容并打印输出 - while ((content = br.readLine()) != null) { - if (content.startsWith(name)) { - log.info("receive: self={}", content); - continue; - } + @Override + public void run() { + try { + String content; + //不断读取输入流中内容并打印输出 + while ((content = br.readLine()) != null) { + if (content.startsWith(name)) { + log.info("receive: self={}", content); + continue; + } - log.info("receive: content={}", content); - } - } catch (Exception e) { - log.error(e.getMessage(), e); + log.info("receive: content={}", content); + } + } catch (Exception e) { + log.error(e.getMessage(), e); + } } - } } From e9aa9e25adfccb5d75b4059dec81ed0aae5d5237 Mon Sep 17 00:00:00 2001 From: kuangcp Date: Fri, 16 Aug 2024 13:45:25 +0800 Subject: [PATCH 422/476] record --- .../src/test/java/syntax/base/FinalTest.java | 2 +- concurrency/pom.xml | 16 ++++++ .../test/java/thread/pool/PoolErrorTest.java | 28 +++++++++ .../kuangcp/stream/filter/FilterTest.java | 57 ++++++++++--------- 4 files changed, 74 insertions(+), 29 deletions(-) create mode 100644 concurrency/src/test/java/thread/pool/PoolErrorTest.java diff --git a/class/src/test/java/syntax/base/FinalTest.java b/class/src/test/java/syntax/base/FinalTest.java index ad6b97c6..cc5762f5 100644 --- a/class/src/test/java/syntax/base/FinalTest.java +++ b/class/src/test/java/syntax/base/FinalTest.java @@ -7,7 +7,6 @@ import java.util.function.Predicate; /** - * * @author Kuangcp * 2024-03-12 13:44 */ @@ -21,6 +20,7 @@ public class FinalTest { static Predicate uu = v -> Objects.equals(v, "uu"); // 只有这种情况会编译错误 + // TODO 为什么 // private static final String x5 = "x" + Optional.ofNullable(System.getenv("")).filter(v -> Objects.equals(v, "uu")).orElse("vvv"); private static final String x6 = "x" + Optional.ofNullable(System.getenv("")).filter(uu).orElse("vvv"); diff --git a/concurrency/pom.xml b/concurrency/pom.xml index ede0dfc7..6bdaa07d 100644 --- a/concurrency/pom.xml +++ b/concurrency/pom.xml @@ -104,6 +104,22 @@ resilience4j-timelimiter + + + commons-math3 + org.apache.commons + 3.1.1 + test + + + + + commons-math3 + org.apache.commons + 3.6.1 + test + + diff --git a/concurrency/src/test/java/thread/pool/PoolErrorTest.java b/concurrency/src/test/java/thread/pool/PoolErrorTest.java new file mode 100644 index 00000000..f957f13b --- /dev/null +++ b/concurrency/src/test/java/thread/pool/PoolErrorTest.java @@ -0,0 +1,28 @@ +package thread.pool; + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.math3.util.Pair; +import org.junit.Test; + +/** + * @author Kuangcp + * 2024-08-16 10:59 + */ +@Slf4j +public class PoolErrorTest { + + @Test + public void testNoMethod() throws Exception { + ExecutorsThreadPool.pool.execute(() -> { + //TODO 3.1.1 3.6.1 版本差异 低版本没有该方法,报NoSuchMethodError, 但是日志中没有错误栈,导致问题难定位 + try { + Pair a = Pair.create("", ""); + log.info("finish"); + } catch (Throwable e) { + log.error("", e); + } + }); + + Thread.currentThread().join(); + } +} diff --git a/java8/src/test/java/com/github/kuangcp/stream/filter/FilterTest.java b/java8/src/test/java/com/github/kuangcp/stream/filter/FilterTest.java index ba3c099a..39c3cb13 100644 --- a/java8/src/test/java/com/github/kuangcp/stream/filter/FilterTest.java +++ b/java8/src/test/java/com/github/kuangcp/stream/filter/FilterTest.java @@ -1,14 +1,15 @@ package com.github.kuangcp.stream.filter; -import static org.hamcrest.Matchers.equalTo; -import static org.junit.Assert.assertThat; +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; import java.util.Arrays; import java.util.List; import java.util.Objects; import java.util.stream.IntStream; -import lombok.extern.slf4j.Slf4j; -import org.junit.Test; + +import static org.hamcrest.Matchers.equalTo; +import static org.junit.Assert.assertThat; /** * @author kuangcp on 3/6/19-5:08 PM @@ -16,31 +17,31 @@ @Slf4j public class FilterTest { - // 当改成 private 时, 才会在下面的方法警告在拆箱时可能导致NPE, Idea 2018 3.5 - public Boolean isTrue(int count) { - if (count < 5) { - return false; + // 当改成 private 时, 才会在下面的方法警告在拆箱时可能导致NPE, Idea 2018 3.5 + public Boolean isTrue(int count) { + if (count < 5) { + return false; + } + if (count == 6) { + return null; + } + return true; } - if (count == 6) { - return null; - } - return true; - } - @Test(expected = NullPointerException.class) - public void testFilterWithNull() { - long count = IntStream.rangeClosed(1, 10).filter(this::isTrue).count(); - System.out.println(count); - } + @Test(expected = NullPointerException.class) + public void testFilterWithNull() { + long count = IntStream.rangeClosed(1, 10).filter(this::isTrue).count(); + System.out.println(count); + } - // 多个 filter 具有 && 一样的短路效应 - @Test - public void testAnd() { - List data = Arrays.asList("", null, "2", "12"); - long count = data.stream() - .filter(Objects::nonNull) - .filter(v -> v.length() > 1) - .count(); - assertThat(count, equalTo(1L)); - } + // 多个 filter 具有 && 一样的短路效应 + @Test + public void testAnd() { + List data = Arrays.asList("", null, "2", "12"); + long count = data.stream() + .filter(Objects::nonNull) + .filter(v -> v.length() > 1) + .count(); + assertThat(count, equalTo(1L)); + } } From bee33b7bf371209c7482301724addfacaaef090e Mon Sep 17 00:00:00 2001 From: kuangcp Date: Tue, 20 Aug 2024 16:49:59 +0800 Subject: [PATCH 423/476] async --- .../kuangcp/future/CompletableFutureTest.java | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/java8/src/test/java/com/github/kuangcp/future/CompletableFutureTest.java b/java8/src/test/java/com/github/kuangcp/future/CompletableFutureTest.java index c5ce6d51..e9223753 100644 --- a/java8/src/test/java/com/github/kuangcp/future/CompletableFutureTest.java +++ b/java8/src/test/java/com/github/kuangcp/future/CompletableFutureTest.java @@ -36,7 +36,7 @@ public void testAsync() throws Exception { } /** - * @see java.util.concurrent.CompletableFuture.asyncPool + * @see java.util.concurrent.CompletableFuture.asyncPool 默认线程池 */ @Test public void testAsyncPoolSize() throws Exception { @@ -98,6 +98,32 @@ public void testFutureAndCombine() throws Exception { executorService.shutdown(); } + @Test + public void testSyncException() throws Exception { + try { + CompletableFuture first = CompletableFuture.supplyAsync(() -> { + if (System.currentTimeMillis() % 1000 < 500) { + throw new RuntimeException("invalid"); + } + return "first"; + }); + + CompletableFuture second = CompletableFuture.supplyAsync(() -> { + if (System.currentTimeMillis() % 1000 < 300) { + throw new RuntimeException("invalid"); + } + return "second"; + }); + + CompletableFuture.allOf(first, second); + + // 异步代码块中发生异常后会包装为 ExecutionException 在 get()时抛出 + log.info("end: {} {} ", first.get(), second.get()); + } catch (Exception e) { + log.error("", e); + } + } + private int func1(int value) { return value + 1; } From c7301b18f2302fb9175631e4094c24873adc74c8 Mon Sep 17 00:00:00 2001 From: kuangcp Date: Wed, 21 Aug 2024 16:49:17 +0800 Subject: [PATCH 424/476] all future --- .../thread/pool/RecommendUsePoolTest.java | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/concurrency/src/test/java/thread/pool/RecommendUsePoolTest.java b/concurrency/src/test/java/thread/pool/RecommendUsePoolTest.java index 498e85c6..df12a2be 100644 --- a/concurrency/src/test/java/thread/pool/RecommendUsePoolTest.java +++ b/concurrency/src/test/java/thread/pool/RecommendUsePoolTest.java @@ -4,9 +4,12 @@ import com.hellokaton.blade.Blade; import com.hellokaton.blade.mvc.RouteContext; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.concurrent.BasicThreadFactory; import org.junit.Test; import web.Application; +import java.util.ArrayList; +import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.concurrent.*; @@ -258,4 +261,51 @@ public void testKillCoreThread() throws Exception { Thread.currentThread().join(15000); log.info("end"); } + + + /** + * @see CompletableFutureTest + */ + @Test + public void testTmpNewAsyncPool() throws Exception { + TimeUnit.SECONDS.sleep(10); + int taskNum = 50; + ExecutorService mainPool = Executors.newFixedThreadPool(1); + + mainPool.execute(() -> { + ThreadPoolExecutor coreCachePool = new ThreadPoolExecutor(4, 4, + 8L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(taskNum), + new BasicThreadFactory.Builder().namingPattern("tmp-%d").build(), + new RecommendUsePool.TrackDiscardPolicy()); + List> wait = new ArrayList<>(); + + try { + for (int i = 0; i < taskNum; i++) { + int finalI = i; + CompletableFuture future = CompletableFuture.runAsync(() -> { + log.info("sleep start {}", finalI); + try { + TimeUnit.SECONDS.sleep(1); + } catch (InterruptedException e) { + log.error("", e); + } + log.info("end {}", finalI); + }, coreCachePool); + wait.add(future); + } + + CompletableFuture[] all = wait.stream().toArray(CompletableFuture[]::new); + CompletableFuture.allOf(all).join(); + } catch (Exception e) { + log.error("", e); + } finally { + log.info("shutdown"); + coreCachePool.shutdown(); + } + log.info("Finish All"); + }); + + log.info("start"); + Thread.currentThread().join(); + } } From 78553aec0a793fec7e32b457db09f8a81c72b460 Mon Sep 17 00:00:00 2001 From: kuangcp Date: Thu, 22 Aug 2024 21:55:37 +0800 Subject: [PATCH 425/476] time format parse --- class/src/test/java/time/LocalDateTest.java | 33 ++++++++++++++----- .../src/test/java/time/LocalDateTimeTest.java | 12 +++++++ 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/class/src/test/java/time/LocalDateTest.java b/class/src/test/java/time/LocalDateTest.java index 9d0885a0..1c020437 100644 --- a/class/src/test/java/time/LocalDateTest.java +++ b/class/src/test/java/time/LocalDateTest.java @@ -1,22 +1,37 @@ package time; -import java.time.LocalDate; -import java.time.LocalDateTime; import lombok.extern.slf4j.Slf4j; import org.junit.Test; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.time.temporal.TemporalAccessor; + /** * @author kuangcp on 18-9-12-下午7:05 */ @Slf4j public class LocalDateTest { - @Test - public void testFromLocalDateTime() { - LocalDate localDate = LocalDateTime.now().toLocalDate(); - LocalDate last = LocalDateTime.now().plusDays(-1).toLocalDate(); + @Test + public void testFromLocalDateTime() { + LocalDate localDate = LocalDateTime.now().toLocalDate(); + LocalDate last = LocalDateTime.now().plusDays(-1).toLocalDate(); + + log.info("{}", localDate); + log.info("{}", last); + } + + @Test + public void testFormatParse() throws Exception { + // 注意接口使用差异 + DateTimeFormatter format = DateTimeFormatter.ofPattern("yyyyMMdd"); + TemporalAccessor ta = format.parse("20230804"); + System.out.println(ta); - log.info("{}", localDate); - log.info("{}", last); - } + LocalDate parse = LocalDate.parse("20171018", format); + System.out.println(parse); + System.out.println(format.format(parse)); + } } diff --git a/class/src/test/java/time/LocalDateTimeTest.java b/class/src/test/java/time/LocalDateTimeTest.java index f58ac54d..489310fd 100644 --- a/class/src/test/java/time/LocalDateTimeTest.java +++ b/class/src/test/java/time/LocalDateTimeTest.java @@ -4,9 +4,11 @@ import lombok.extern.slf4j.Slf4j; import org.junit.Test; +import java.time.LocalDate; import java.time.LocalDateTime; import java.time.ZoneOffset; import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; import java.util.Date; import static org.hamcrest.Matchers.lessThan; @@ -61,6 +63,16 @@ public void testFormat() throws Exception { LocalDateTime last = LocalDateTime.parse(str, format); System.out.println(last); System.out.println(format.format(last)); + + format = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + System.out.println(format.format(LocalDate.now())); + } + + @Test(expected = DateTimeParseException.class) + public void testParseError() throws Exception { + // 字符串内不含时间,解析失败 + LocalDateTime yyyyMMdd = LocalDateTime.parse("20230804", DateTimeFormatter.ofPattern("yyyyMMdd")); + System.out.println(yyyyMMdd); } } From 7f0926a055077a371fc08c0ad14a1adebe1af356 Mon Sep 17 00:00:00 2001 From: kuangcp Date: Fri, 23 Aug 2024 17:47:52 +0800 Subject: [PATCH 426/476] match group --- .../test/java/syntax/string/RegexTest.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/class/src/test/java/syntax/string/RegexTest.java b/class/src/test/java/syntax/string/RegexTest.java index ec574f30..42ed26b3 100644 --- a/class/src/test/java/syntax/string/RegexTest.java +++ b/class/src/test/java/syntax/string/RegexTest.java @@ -5,9 +5,13 @@ import syntax.bit.BitOperatorsTest; import java.util.Objects; +import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.IntStream; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.MatcherAssert.assertThat; + /** * @author Kuangcp * 2024-04-28 22:02 @@ -161,4 +165,19 @@ public void testPossessive() throws Exception { System.out.println(opt.matcher("abbbc").find()); System.out.println(opt.matcher("abbbbc").find()); } + + @Test + public void testGroup() throws Exception { + Pattern idxGroup = Pattern.compile("(\\d{4})-(\\d{2})"); + Matcher matcher = idxGroup.matcher("2012-12"); + System.out.println(matcher.matches()); + assertThat(matcher.group(1), equalTo("2012")); + assertThat(matcher.group(2), equalTo("12")); + + Pattern nameGroup = Pattern.compile("(?\\d{4})-(?\\d{2})"); + matcher = nameGroup.matcher("2012-12"); + System.out.println(matcher.matches()); + assertThat(matcher.group("year"), equalTo("2012")); + assertThat(matcher.group("month"), equalTo("12")); + } } From 1d08ff0d309a89c9f2aa135f69f529ed0b481efb Mon Sep 17 00:00:00 2001 From: kuangcp Date: Mon, 2 Sep 2024 17:21:43 +0800 Subject: [PATCH 427/476] local date --- class/src/test/java/time/LocalDateTest.java | 75 +++++++++++++++++++-- 1 file changed, 70 insertions(+), 5 deletions(-) diff --git a/class/src/test/java/time/LocalDateTest.java b/class/src/test/java/time/LocalDateTest.java index 1c020437..ae1833cb 100644 --- a/class/src/test/java/time/LocalDateTest.java +++ b/class/src/test/java/time/LocalDateTest.java @@ -1,12 +1,18 @@ package time; import lombok.extern.slf4j.Slf4j; +import org.junit.Assert; import org.junit.Test; +import java.text.SimpleDateFormat; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatterBuilder; +import java.time.format.DateTimeParseException; +import java.time.temporal.ChronoField; import java.time.temporal.TemporalAccessor; +import java.util.Date; /** * @author kuangcp on 18-9-12-下午7:05 @@ -26,12 +32,71 @@ public void testFromLocalDateTime() { @Test public void testFormatParse() throws Exception { // 注意接口使用差异 - DateTimeFormatter format = DateTimeFormatter.ofPattern("yyyyMMdd"); - TemporalAccessor ta = format.parse("20230804"); - System.out.println(ta); + { + DateTimeFormatter format = DateTimeFormatter.ofPattern("yyyyMMdd"); + TemporalAccessor ta = format.parse("20230804"); + System.out.println(ta); - LocalDate parse = LocalDate.parse("20171018", format); + LocalDate parse = LocalDate.parse("20171018", format); + System.out.println(parse); + System.out.println(format.format(parse)); + } + { + DateTimeFormatter format = DateTimeFormatter.ofPattern("yyyy-MMdd"); + TemporalAccessor ta = format.parse("2023-0804"); + System.out.println(ta); + + LocalDate parse = LocalDate.parse("2017-1018", format); + System.out.println(parse); + System.out.println(format.format(parse)); + } + + { + SimpleDateFormat fmt = new SimpleDateFormat("yyyyMM"); + Date xx = fmt.parse("202407"); + System.out.println(xx); + + DateTimeFormatter monthFMT = new DateTimeFormatterBuilder() + .appendPattern("yyyyMM") + .parseDefaulting(ChronoField.DAY_OF_MONTH, 1) + .toFormatter(); + LocalDate parse = LocalDate.parse("201707", monthFMT); + Assert.assertEquals(LocalDate.of(2017, 7, 1), parse); + Assert.assertEquals("201707", monthFMT.format(parse)); + } + { + DateTimeFormatter monthFMT = new DateTimeFormatterBuilder() + .appendPattern("yyyy") + .parseDefaulting(ChronoField.MONTH_OF_YEAR, 1) + .parseDefaulting(ChronoField.DAY_OF_MONTH, 1) + .toFormatter(); + LocalDate parse = LocalDate.parse("2017", monthFMT); + + Assert.assertEquals(LocalDate.of(2017, 1, 1), parse); + Assert.assertEquals("2017", monthFMT.format(parse)); + } + } + + @Test(expected = DateTimeParseException.class) + public void testParseYM() throws Exception { + // 违反直觉,纯浪费时间 + DateTimeFormatter monthFMT = DateTimeFormatter.ofPattern("yyyyMM"); + LocalDate parse = LocalDate.parse("201707", monthFMT); + System.out.println(parse); + System.out.println(monthFMT.format(parse)); + } + + @Test(expected = DateTimeParseException.class) + public void testParseYear() throws Exception { + DateTimeFormatter format = DateTimeFormatter.ofPattern("yyyy"); + LocalDate parse = LocalDate.parse("2017", format); + System.out.println(parse); + } + + @Test(expected = DateTimeParseException.class) + public void testParseYearDay() throws Exception { + DateTimeFormatter format = DateTimeFormatter.ofPattern("yyyy-dd"); + LocalDate parse = LocalDate.parse("2017-21", format); System.out.println(parse); - System.out.println(format.format(parse)); } } From b7e68c3b6066ba8f6bbbb231c45a9511a129ee88 Mon Sep 17 00:00:00 2001 From: kuangcp Date: Fri, 13 Sep 2024 11:11:10 +0800 Subject: [PATCH 428/476] fmt --- .../timoutpool/TimeoutExecPoolTest.java | 28 ++++++++++++------- .../kuangcp/future/CompletableFutureTest.java | 21 +++++++++++++- 2 files changed, 38 insertions(+), 11 deletions(-) diff --git a/concurrency/src/test/java/situation/timoutpool/TimeoutExecPoolTest.java b/concurrency/src/test/java/situation/timoutpool/TimeoutExecPoolTest.java index 20ea65cc..cdcb20aa 100644 --- a/concurrency/src/test/java/situation/timoutpool/TimeoutExecPoolTest.java +++ b/concurrency/src/test/java/situation/timoutpool/TimeoutExecPoolTest.java @@ -23,7 +23,7 @@ public void testRequest() throws InterruptedException { List params = IntStream.range(0, 300).mapToObj(v -> UUID.randomUUID().toString().substring(0, 5) + v).collect(Collectors.toList()); // log.info("start param={}", params); log.info("start"); - List result = timeoutPool.execute(params, this::logic); + List result = timeoutPool.execute(params, this::bizTask); // log.info("end: result={}", result); log.info("end"); // Thread.currentThread().join(); @@ -31,18 +31,21 @@ public void testRequest() throws InterruptedException { /** * 可行方案: 使用 ExecutorCompletionService 和 CompletableFuture 组合限制执行时间,汇聚执行结果,取消后续任务 + *

+ * runPool 并行执行一批任务, CompletableFuture 单线程等待和合并全部任务的执行结果,如果超时了获取不到已完成的任务结果 */ @Test public void testCompleteService() { log.info("start"); - CompletionService cs = new ExecutorCompletionService<>(Executors.newFixedThreadPool(3)); + ExecutorService runPool = Executors.newFixedThreadPool(3); + CompletionService cs = new ExecutorCompletionService<>(runPool); int max = 10; List params = IntStream.range(0, max) .mapToObj(v -> UUID.randomUUID().toString() + v) .collect(Collectors.toList()); - List> futures = params.stream().map(v -> cs.submit(() -> this.logic(v))).collect(Collectors.toList()); + List> futures = params.stream().map(v -> cs.submit(() -> this.bizTask(v))).collect(Collectors.toList()); CompletableFuture> future = CompletableFuture.supplyAsync(() -> { List results = new ArrayList<>(); for (int i = 0; i < max; i++) { @@ -58,16 +61,18 @@ public void testCompleteService() { } catch (InterruptedException | ExecutionException e) { throw new RuntimeException(e); } - log.info("{}", result); + log.info("finish {}", result); results.add(result); } + log.info("all task finish"); return results; }); try { - List results = future.get(80000, TimeUnit.MILLISECONDS); + List results = future.get(800, TimeUnit.MILLISECONDS); log.info("{}", results); } catch (InterruptedException | ExecutionException | TimeoutException e) { log.error("timeout", e); + for (Future longFuture : futures) { longFuture.cancel(true); } @@ -76,6 +81,8 @@ public void testCompleteService() { /** * 保留部分任务执行数据 中断后续任务 + *

+ * runPool 并行执行一批任务, CompletableFuture 单线程等待和合并全部任务的执行结果,如果超时了获取已完成的这部分任务的结果 */ @Test public void testCompleteServicePart() { @@ -84,9 +91,9 @@ public void testCompleteServicePart() { int max = 10; List params = IntStream.range(0, max) - .mapToObj(v -> UUID.randomUUID().toString() + v) + .mapToObj(v -> UUID.randomUUID() + "-" + v) .collect(Collectors.toList()); - List> futures = params.stream().map(v -> cs.submit(() -> this.logic(v))).collect(Collectors.toList()); + List> futures = params.stream().map(v -> cs.submit(() -> this.bizTask(v))).collect(Collectors.toList()); Vector vs = new Vector<>(); CompletableFuture future = CompletableFuture.runAsync(() -> { @@ -104,12 +111,13 @@ public void testCompleteServicePart() { } catch (InterruptedException | ExecutionException e) { throw new RuntimeException(e); } - log.info("{}", result); + log.info("finish {}", result); } + log.info("all task finish"); }); log.info("wait result"); try { - future.get(800, TimeUnit.MILLISECONDS); + future.get(2100, TimeUnit.MILLISECONDS); log.info("{}", vs); } catch (InterruptedException | ExecutionException | TimeoutException e) { log.error("timeout", e); @@ -120,7 +128,7 @@ public void testCompleteServicePart() { } } - private Long logic(String param) { + private Long bizTask(String param) { if (Objects.isNull(param)) { return 0L; } diff --git a/java8/src/test/java/com/github/kuangcp/future/CompletableFutureTest.java b/java8/src/test/java/com/github/kuangcp/future/CompletableFutureTest.java index e9223753..cba2f881 100644 --- a/java8/src/test/java/com/github/kuangcp/future/CompletableFutureTest.java +++ b/java8/src/test/java/com/github/kuangcp/future/CompletableFutureTest.java @@ -3,6 +3,7 @@ import lombok.extern.slf4j.Slf4j; import org.junit.Test; +import java.time.LocalTime; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CompletableFuture; @@ -115,7 +116,8 @@ public void testSyncException() throws Exception { return "second"; }); - CompletableFuture.allOf(first, second); + CompletableFuture all = CompletableFuture.allOf(first, second); + all.get(); // 异步代码块中发生异常后会包装为 ExecutionException 在 get()时抛出 log.info("end: {} {} ", first.get(), second.get()); @@ -133,4 +135,21 @@ private int func2(int value) { } // TODO 理解 lettuce 中 Future 的使用 + + + /** + * TODO 可控耗时自旋 + */ + @Test + public void testSpinWait() throws Exception { + CompletableFuture first = CompletableFuture.supplyAsync(this::enableRun); +// first.thenCombine(); + } + + private final LocalTime now = LocalTime.now(); + + private boolean enableRun() { + LocalTime now1 = LocalTime.now(); + return now1.isAfter(now.plusMinutes(1)); + } } From b4e9fb8c3aaf6708f6ce9331c2697d1dfdee227c Mon Sep 17 00:00:00 2001 From: kuangcp Date: Fri, 13 Sep 2024 18:44:18 +0800 Subject: [PATCH 429/476] wait --- .../kuangcp/future/CompletableFutureTest.java | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/java8/src/test/java/com/github/kuangcp/future/CompletableFutureTest.java b/java8/src/test/java/com/github/kuangcp/future/CompletableFutureTest.java index cba2f881..131cd566 100644 --- a/java8/src/test/java/com/github/kuangcp/future/CompletableFutureTest.java +++ b/java8/src/test/java/com/github/kuangcp/future/CompletableFutureTest.java @@ -11,6 +11,7 @@ import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import static org.hamcrest.Matchers.equalTo; import static org.junit.Assert.assertThat; @@ -142,7 +143,25 @@ private int func2(int value) { */ @Test public void testSpinWait() throws Exception { - CompletableFuture first = CompletableFuture.supplyAsync(this::enableRun); + log.info("start"); + CompletableFuture first = CompletableFuture.runAsync(() -> { + boolean enable = this.enableRun(); + while (!enable) { + try { + TimeUnit.MILLISECONDS.sleep(100); + } catch (InterruptedException ignored) { + } + enable = enableRun(); + } + }); + + + try { + first.get(5, TimeUnit.SECONDS); + } catch (TimeoutException e) { + log.error("timeout", e); + } + log.info("finish"); // first.thenCombine(); } @@ -150,6 +169,6 @@ public void testSpinWait() throws Exception { private boolean enableRun() { LocalTime now1 = LocalTime.now(); - return now1.isAfter(now.plusMinutes(1)); + return now1.isAfter(now.plusSeconds(3)); } } From 2b2e9052e0209916ff7be3ba4d86596a2556515a Mon Sep 17 00:00:00 2001 From: kuangcp Date: Tue, 24 Sep 2024 10:06:31 +0800 Subject: [PATCH 430/476] overflow --- .../src/test/java/syntax/bit/BitOperatorsTest.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/class/src/test/java/syntax/bit/BitOperatorsTest.java b/class/src/test/java/syntax/bit/BitOperatorsTest.java index 676fac5b..53d53172 100644 --- a/class/src/test/java/syntax/bit/BitOperatorsTest.java +++ b/class/src/test/java/syntax/bit/BitOperatorsTest.java @@ -156,6 +156,20 @@ public void testXORByByte() { Assert.assertNotEquals(oneStr, originMsg); } + @Test + public void testOverFlow() throws Exception { + // int最大值为 2^31 -1 以下值刚好溢出1位到了符号位,又因为负数采用的是反码所以字面值就会变成int的最小值 + System.out.println(2048 * 1024 * 1024); + show(2048 * 1024 * 1024); + + // 同样的,对溢出的值 -1 又将符号位退回0,值就变为最大值了 + System.out.println(2048 * 1024 * 1024 - 1); + show(2048 * 1024 * 1024 - 1); + + System.out.println(2048 * 1024 * 1024L); + System.out.println(2048 * 1024 * 1024L - 1); + } + private byte[] transfer(byte[] data) { byte[] key = {3, 56, 12, 22, 35, 87, 123, 83, 111, 34, 23, 56, 34, 56}; int maxKey = key.length; From e1ad67d28c4d2c1b813603f5b502751de89a3e02 Mon Sep 17 00:00:00 2001 From: kuangcp Date: Tue, 24 Sep 2024 15:08:50 +0800 Subject: [PATCH 431/476] junit test --- .../src/main/java/junit4/param/Calculate.java | 15 +++ .../com/github/kuangcp/util/StrUtilTest.java | 1 - test/src/test/java/junit4/async/JoinTest.java | 97 +++++++++++++++++++ .../test/java/junit4/param/CalculateTest.java | 54 +++++++++++ .../test/java/junit4/rule/TimeoutTest.java | 41 ++++++++ .../src/test/java/junit4/suite/PartATest.java | 23 +++++ .../src/test/java/junit4/suite/PartBTest.java | 23 +++++ .../src/test/java/junit4/suite/SuiteTest.java | 17 ++++ .../junit4/suite/category/TimeSuiteTest.java | 21 ++++ .../suite/category/TimeTestCategory.java | 8 ++ 10 files changed, 299 insertions(+), 1 deletion(-) create mode 100644 test/src/main/java/junit4/param/Calculate.java create mode 100644 test/src/test/java/junit4/async/JoinTest.java create mode 100644 test/src/test/java/junit4/param/CalculateTest.java create mode 100644 test/src/test/java/junit4/rule/TimeoutTest.java create mode 100644 test/src/test/java/junit4/suite/PartATest.java create mode 100644 test/src/test/java/junit4/suite/PartBTest.java create mode 100644 test/src/test/java/junit4/suite/SuiteTest.java create mode 100644 test/src/test/java/junit4/suite/category/TimeSuiteTest.java create mode 100644 test/src/test/java/junit4/suite/category/TimeTestCategory.java diff --git a/test/src/main/java/junit4/param/Calculate.java b/test/src/main/java/junit4/param/Calculate.java new file mode 100644 index 00000000..679741f4 --- /dev/null +++ b/test/src/main/java/junit4/param/Calculate.java @@ -0,0 +1,15 @@ +package junit4.param; + +/** + * @author Kuangcp + * 2024-09-24 14:28 + */ +public class Calculate { + public double add(double numA, double numB) { + return numA + numB; + } + + public double divide(double numA, int i) { + return numA / i; + } +} diff --git a/test/src/test/java/com/github/kuangcp/util/StrUtilTest.java b/test/src/test/java/com/github/kuangcp/util/StrUtilTest.java index cb439cfc..1848e08c 100644 --- a/test/src/test/java/com/github/kuangcp/util/StrUtilTest.java +++ b/test/src/test/java/com/github/kuangcp/util/StrUtilTest.java @@ -14,7 +14,6 @@ import java.util.stream.Collectors; /** - * * @author Kuangcp * 2024-03-29 14:12 */ diff --git a/test/src/test/java/junit4/async/JoinTest.java b/test/src/test/java/junit4/async/JoinTest.java new file mode 100644 index 00000000..ab6bf0e8 --- /dev/null +++ b/test/src/test/java/junit4/async/JoinTest.java @@ -0,0 +1,97 @@ +package junit4.async; + +import org.junit.Test; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +/** + * @author Kuangcp + * 2024-09-24 14:13 + */ +public class JoinTest { + + /** + * 错误使用: 此时新建的线程还没有执行,单元测试线程就已经退出了 + */ + @Test + public void testThread() throws Exception { + new Thread(() -> { + try { + TimeUnit.SECONDS.sleep(1); + System.out.println("async by new thread"); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + }).start(); + + System.out.println("finish " + Thread.currentThread().getName()); + } + + /** + * 显式指定等待的线程 + */ + @Test + public void testThread2() throws Exception { + Thread part = new Thread(() -> { + try { + TimeUnit.SECONDS.sleep(1); + System.out.println("async by new thread"); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + }); + part.start(); + + // main 线程会等待 part 线程执行结束 + part.join(); + System.out.println("finish " + Thread.currentThread().getName()); + } + + + /** + * 使用 同步组件 阻塞单元测试main线程 + */ + @Test + public void testThreadPool() throws Exception { + CountDownLatch latch = new CountDownLatch(1); + ExecutorService pool = Executors.newFixedThreadPool(2); + pool.submit(() -> { + try { + TimeUnit.SECONDS.sleep(1); + System.out.println("async by new thread"); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } finally { + latch.countDown(); + } + }); + + latch.await(); + + System.out.println("finish " + Thread.currentThread().getName()); + } + + @Test + public void testThreadPool2() throws Exception { + ExecutorService pool = Executors.newFixedThreadPool(2); + pool.submit(() -> { + try { + TimeUnit.SECONDS.sleep(1); + System.out.println("async by new thread"); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + }); + + // 阻塞单元测试线程 2s + Thread.currentThread().join(2000); + + // 将会一直阻塞单元测试线程,直到手动中断该进程,调试耗时不可控的单元测试场景时可使用 +// Thread.currentThread().join(); + + System.out.println("finish " + Thread.currentThread().getName()); + } +} diff --git a/test/src/test/java/junit4/param/CalculateTest.java b/test/src/test/java/junit4/param/CalculateTest.java new file mode 100644 index 00000000..6dd8ae54 --- /dev/null +++ b/test/src/test/java/junit4/param/CalculateTest.java @@ -0,0 +1,54 @@ +package junit4.param; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.util.Arrays; +import java.util.Collection; + +/** + * @author Kuangcp + * 2024-09-24 14:23 + */ +// 1 +@RunWith(Parameterized.class) +public class CalculateTest { + // 2 + private final double numA; + private final double numB; + + // 3 + public CalculateTest(double numA, double numB) { + this.numA = numA; + this.numB = numB; + } + + // 4 + @Parameterized.Parameters + public static Collection data() { + Object[][] data = new Object[][]{ + {2, 4}, + {3, 5}, + {7, 3} + }; + return Arrays.asList(data); + } + + // 5 + @Test + public void testAdd() throws Exception { + Calculate calc = new Calculate(); + double result = calc.add(numA, numB); + System.out.println("input " + numA + " + " + numB + " = " + result); + assert result != 0; + } + + @Test + public void testDivide() { + Calculate calc = new Calculate(); + double result = calc.divide(numA, 3); + System.out.println("input " + numA + " / " + 3 + " = " + result); + assert result != 0; + } +} diff --git a/test/src/test/java/junit4/rule/TimeoutTest.java b/test/src/test/java/junit4/rule/TimeoutTest.java new file mode 100644 index 00000000..d28f9fda --- /dev/null +++ b/test/src/test/java/junit4/rule/TimeoutTest.java @@ -0,0 +1,41 @@ +package junit4.rule; + +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.Timeout; +import org.junit.runners.model.TestTimedOutException; + +import java.util.concurrent.TimeUnit; + +/** + * @author Kuangcp + * 2024-09-24 14:37 + */ +public class TimeoutTest { + + @Rule + public Timeout timeout = new Timeout(100); + + /** + * 注意 except 不生效 + */ + @Test(expected = TestTimedOutException.class) + public void testRun() throws Exception { + TimeUnit.SECONDS.sleep(1); + } + + @Test + public void testAdd() throws Exception { + Assert.assertEquals(3, 1 + 2); + } + + @Test + public void testSin() throws Exception { + double c = 0; + for (int i = 0; i < 100000; i++) { + double val = Math.sin(i) * Math.tan(i); + c = Math.sin(val + c); + } + } +} diff --git a/test/src/test/java/junit4/suite/PartATest.java b/test/src/test/java/junit4/suite/PartATest.java new file mode 100644 index 00000000..6a3cd8a1 --- /dev/null +++ b/test/src/test/java/junit4/suite/PartATest.java @@ -0,0 +1,23 @@ +package junit4.suite; + +import junit4.suite.category.TimeTestCategory; +import org.junit.Test; +import org.junit.experimental.categories.Category; + +/** + * @author Kuangcp + * 2024-09-24 14:12 + */ +public class PartATest { + + @Test + public void testA() throws Exception { + System.out.println("A"); + } + + @Category(TimeTestCategory.class) + @Test + public void testTime() throws Exception { + System.out.println("A time"); + } +} diff --git a/test/src/test/java/junit4/suite/PartBTest.java b/test/src/test/java/junit4/suite/PartBTest.java new file mode 100644 index 00000000..7dd7c895 --- /dev/null +++ b/test/src/test/java/junit4/suite/PartBTest.java @@ -0,0 +1,23 @@ +package junit4.suite; + +import junit4.suite.category.TimeTestCategory; +import org.junit.Test; +import org.junit.experimental.categories.Category; + +/** + * @author Kuangcp + * 2024-09-24 14:12 + */ +public class PartBTest { + @Test + public void testB() throws Exception { + System.out.println("B"); + } + + @Category(TimeTestCategory.class) + @Test + public void testTime() throws Exception { + System.out.println("B time"); + } + +} diff --git a/test/src/test/java/junit4/suite/SuiteTest.java b/test/src/test/java/junit4/suite/SuiteTest.java new file mode 100644 index 00000000..49fcb627 --- /dev/null +++ b/test/src/test/java/junit4/suite/SuiteTest.java @@ -0,0 +1,17 @@ +package junit4.suite; + +import org.junit.runner.RunWith; +import org.junit.runners.Suite; + +/** + * @author Kuangcp + * 2024-09-24 14:12 + */ +@RunWith(Suite.class) +@Suite.SuiteClasses({ + // 注意此处的类顺序 就是单元测试方法的执行顺序 + PartATest.class, + PartBTest.class, +}) +public class SuiteTest { +} diff --git a/test/src/test/java/junit4/suite/category/TimeSuiteTest.java b/test/src/test/java/junit4/suite/category/TimeSuiteTest.java new file mode 100644 index 00000000..bab42ef9 --- /dev/null +++ b/test/src/test/java/junit4/suite/category/TimeSuiteTest.java @@ -0,0 +1,21 @@ +package junit4.suite.category; + +import junit4.suite.PartATest; +import junit4.suite.PartBTest; +import org.junit.experimental.categories.Categories; +import org.junit.runner.RunWith; +import org.junit.runners.Suite; + +/** + * @author Kuangcp + * 2024-09-24 15:01 + */ +@RunWith(Categories.class) +@Suite.SuiteClasses({ + PartATest.class, + PartBTest.class, +}) +//@Categories.ExcludeCategory(TimeTestCategory.class) // 排除标记的分类 +@Categories.IncludeCategory(TimeTestCategory.class) // 只执行标记的分类 +public class TimeSuiteTest { +} diff --git a/test/src/test/java/junit4/suite/category/TimeTestCategory.java b/test/src/test/java/junit4/suite/category/TimeTestCategory.java new file mode 100644 index 00000000..b5ea69ad --- /dev/null +++ b/test/src/test/java/junit4/suite/category/TimeTestCategory.java @@ -0,0 +1,8 @@ +package junit4.suite.category; + +/** + * @author Kuangcp + * 2024-09-24 15:01 + */ +public interface TimeTestCategory { +} From 206c88a84bd2b17687e7f561116213dc8a26a71e Mon Sep 17 00:00:00 2001 From: kuangcp Date: Tue, 24 Sep 2024 16:27:54 +0800 Subject: [PATCH 432/476] disruptor --- concurrency/pom.xml | 6 + .../queue/disruptor/first/OrderEvent.java | 12 ++ .../disruptor/first/OrderEventHandler.java | 33 ++++++ .../disruptor/first/OrderEventProducer.java | 26 +++++ .../queue/disruptor/first/FirstTest.java | 104 ++++++++++++++++++ .../kuangcp/future/CompletableFutureTest.java | 10 +- 6 files changed, 190 insertions(+), 1 deletion(-) create mode 100644 concurrency/src/main/java/com/github/kuangcp/queue/disruptor/first/OrderEvent.java create mode 100644 concurrency/src/main/java/com/github/kuangcp/queue/disruptor/first/OrderEventHandler.java create mode 100644 concurrency/src/main/java/com/github/kuangcp/queue/disruptor/first/OrderEventProducer.java create mode 100644 concurrency/src/test/java/com/github/kuangcp/queue/disruptor/first/FirstTest.java diff --git a/concurrency/pom.xml b/concurrency/pom.xml index 6bdaa07d..86100654 100644 --- a/concurrency/pom.xml +++ b/concurrency/pom.xml @@ -120,6 +120,12 @@ test + + com.lmax + disruptor + 3.4.4 + + diff --git a/concurrency/src/main/java/com/github/kuangcp/queue/disruptor/first/OrderEvent.java b/concurrency/src/main/java/com/github/kuangcp/queue/disruptor/first/OrderEvent.java new file mode 100644 index 00000000..b3141dfa --- /dev/null +++ b/concurrency/src/main/java/com/github/kuangcp/queue/disruptor/first/OrderEvent.java @@ -0,0 +1,12 @@ +package com.github.kuangcp.queue.disruptor.first; + +import lombok.Data; + +/** + * @author Kuangcp + * 2024-09-24 16:07 + */ +@Data +public class OrderEvent { + private String id; +} diff --git a/concurrency/src/main/java/com/github/kuangcp/queue/disruptor/first/OrderEventHandler.java b/concurrency/src/main/java/com/github/kuangcp/queue/disruptor/first/OrderEventHandler.java new file mode 100644 index 00000000..773f2481 --- /dev/null +++ b/concurrency/src/main/java/com/github/kuangcp/queue/disruptor/first/OrderEventHandler.java @@ -0,0 +1,33 @@ +package com.github.kuangcp.queue.disruptor.first; + +import com.lmax.disruptor.EventHandler; +import com.lmax.disruptor.WorkHandler; +import lombok.extern.slf4j.Slf4j; + +/** + * @author Kuangcp + * 2024-09-24 16:08 + */ +@Slf4j +public class OrderEventHandler implements EventHandler, WorkHandler { + + private final String id; + + public OrderEventHandler() { + this.id = ""; + } + + public OrderEventHandler(String id) { + this.id = id; + } + + @Override + public void onEvent(OrderEvent event, long sequence, boolean endOfBatch) { + log.info("{} event: {}, sequence: {}, endOfBatch: {}", id, event, sequence, endOfBatch); + } + + @Override + public void onEvent(OrderEvent event) { + log.info("{} event: {}", id, event); + } +} diff --git a/concurrency/src/main/java/com/github/kuangcp/queue/disruptor/first/OrderEventProducer.java b/concurrency/src/main/java/com/github/kuangcp/queue/disruptor/first/OrderEventProducer.java new file mode 100644 index 00000000..b55b0f0f --- /dev/null +++ b/concurrency/src/main/java/com/github/kuangcp/queue/disruptor/first/OrderEventProducer.java @@ -0,0 +1,26 @@ +package com.github.kuangcp.queue.disruptor.first; + +import com.lmax.disruptor.RingBuffer; + +/** + * @author Kuangcp + * 2024-09-24 16:07 + */ +public class OrderEventProducer { + + private final RingBuffer ringBuffer; + + public OrderEventProducer(RingBuffer ringBuffer) { + this.ringBuffer = ringBuffer; + } + + public void onData(String orderId) { + long sequence = ringBuffer.next(); + try { + OrderEvent orderEvent = ringBuffer.get(sequence); + orderEvent.setId(orderId); + } finally { + ringBuffer.publish(sequence); + } + } +} diff --git a/concurrency/src/test/java/com/github/kuangcp/queue/disruptor/first/FirstTest.java b/concurrency/src/test/java/com/github/kuangcp/queue/disruptor/first/FirstTest.java new file mode 100644 index 00000000..63b79915 --- /dev/null +++ b/concurrency/src/test/java/com/github/kuangcp/queue/disruptor/first/FirstTest.java @@ -0,0 +1,104 @@ +package com.github.kuangcp.queue.disruptor.first; + +import com.lmax.disruptor.RingBuffer; +import com.lmax.disruptor.YieldingWaitStrategy; +import com.lmax.disruptor.dsl.Disruptor; +import com.lmax.disruptor.dsl.ProducerType; +import org.junit.Test; + +import java.util.UUID; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +/** + * @author Kuangcp + * 2024-09-24 16:05 + * @link 高性能队列 Disruptor 使用教程 + */ +public class FirstTest { + + @Test + public void testSingleProductSinConsumer() throws Exception { + Disruptor disruptor = new Disruptor<>( + OrderEvent::new, + 1024 * 1024, + Executors.defaultThreadFactory(), + ProducerType.SINGLE, + new YieldingWaitStrategy() + ); + disruptor.handleEventsWith(new OrderEventHandler()); + disruptor.start(); + RingBuffer ringBuffer = disruptor.getRingBuffer(); + OrderEventProducer eventProducer = new OrderEventProducer(ringBuffer); + eventProducer.onData(UUID.randomUUID().toString()); + + Thread.currentThread().join(); + } + + @Test + public void testSingleProductMultiConsumer() throws Exception { + Disruptor disruptor = new Disruptor<>( + OrderEvent::new, + 1024 * 1024, + Executors.defaultThreadFactory(), + ProducerType.SINGLE, + new YieldingWaitStrategy() + ); + disruptor.handleEventsWith(new OrderEventHandler(), new OrderEventHandler()); + disruptor.start(); + RingBuffer ringBuffer = disruptor.getRingBuffer(); + OrderEventProducer eventProducer = new OrderEventProducer(ringBuffer); + eventProducer.onData(UUID.randomUUID().toString()); + + Thread.currentThread().join(); + } + + /** + * 多生产者 多消费者 + */ + @Test + public void testProductConsumer() throws Exception { + Disruptor disruptor = new Disruptor<>( + OrderEvent::new, + 1024 * 1024, + Executors.defaultThreadFactory(), + // 这里的枚举修改为多生产者 + ProducerType.MULTI, + new YieldingWaitStrategy() + ); + // 多个消费者不重复消费(一个消息只会被消费一次,但是消费者间可以并行处理消息), 需要实现WorkHandler接口 + disruptor.handleEventsWithWorkerPool(new OrderEventHandler("a"), new OrderEventHandler("b")); + disruptor.start(); + RingBuffer ringBuffer = disruptor.getRingBuffer(); + OrderEventProducer eventProducer = new OrderEventProducer(ringBuffer); + // 创建一个线程池,模拟多个生产者 + ExecutorService fixedThreadPool = Executors.newFixedThreadPool(100); + for (int i = 0; i < 100; i++) { + fixedThreadPool.execute(() -> eventProducer.onData(UUID.randomUUID().toString())); + } + + Thread.currentThread().join(2000); + } + + @Test + public void testConsumerChain() throws Exception { + Disruptor disruptor = new Disruptor<>( + OrderEvent::new, + 1024 * 1024, + Executors.defaultThreadFactory(), + ProducerType.SINGLE, + new YieldingWaitStrategy() + ); + + disruptor.handleEventsWith(new OrderEventHandler()) + .thenHandleEventsWithWorkerPool(new OrderEventHandler("a"), new OrderEventHandler("b"), new OrderEventHandler("c")) + .then(new OrderEventHandler()); + + disruptor.start(); + RingBuffer ringBuffer = disruptor.getRingBuffer(); + OrderEventProducer eventProducer = new OrderEventProducer(ringBuffer); + eventProducer.onData(UUID.randomUUID().toString()); + + Thread.currentThread().join(3000); + } +} diff --git a/java8/src/test/java/com/github/kuangcp/future/CompletableFutureTest.java b/java8/src/test/java/com/github/kuangcp/future/CompletableFutureTest.java index 131cd566..62b91293 100644 --- a/java8/src/test/java/com/github/kuangcp/future/CompletableFutureTest.java +++ b/java8/src/test/java/com/github/kuangcp/future/CompletableFutureTest.java @@ -168,7 +168,15 @@ public void testSpinWait() throws Exception { private final LocalTime now = LocalTime.now(); private boolean enableRun() { + log.info("check"); LocalTime now1 = LocalTime.now(); - return now1.isAfter(now.plusSeconds(3)); + return now1.isAfter(now.plusSeconds(10)); + } + + @Test + public void testNow() throws Exception { + // 1726284853554 + // 1685014528 + System.out.println(System.currentTimeMillis()); } } From 20d421d8208b9d5bc1d183711a5848f0f39dc07c Mon Sep 17 00:00:00 2001 From: kuangcp Date: Tue, 24 Sep 2024 16:35:21 +0800 Subject: [PATCH 433/476] thread bind --- .../disruptor/first/OrderEventHandler.java | 2 + .../first/OrderEventSlowHandler.java | 40 +++++++++++++++++++ .../queue/disruptor/first/FirstTest.java | 30 ++++++++++++++ 3 files changed, 72 insertions(+) create mode 100644 concurrency/src/main/java/com/github/kuangcp/queue/disruptor/first/OrderEventSlowHandler.java diff --git a/concurrency/src/main/java/com/github/kuangcp/queue/disruptor/first/OrderEventHandler.java b/concurrency/src/main/java/com/github/kuangcp/queue/disruptor/first/OrderEventHandler.java index 773f2481..3bbf35c4 100644 --- a/concurrency/src/main/java/com/github/kuangcp/queue/disruptor/first/OrderEventHandler.java +++ b/concurrency/src/main/java/com/github/kuangcp/queue/disruptor/first/OrderEventHandler.java @@ -2,6 +2,7 @@ import com.lmax.disruptor.EventHandler; import com.lmax.disruptor.WorkHandler; +import lombok.Getter; import lombok.extern.slf4j.Slf4j; /** @@ -9,6 +10,7 @@ * 2024-09-24 16:08 */ @Slf4j +@Getter public class OrderEventHandler implements EventHandler, WorkHandler { private final String id; diff --git a/concurrency/src/main/java/com/github/kuangcp/queue/disruptor/first/OrderEventSlowHandler.java b/concurrency/src/main/java/com/github/kuangcp/queue/disruptor/first/OrderEventSlowHandler.java new file mode 100644 index 00000000..f15c33fe --- /dev/null +++ b/concurrency/src/main/java/com/github/kuangcp/queue/disruptor/first/OrderEventSlowHandler.java @@ -0,0 +1,40 @@ +package com.github.kuangcp.queue.disruptor.first; + +import lombok.extern.slf4j.Slf4j; + +import java.util.concurrent.TimeUnit; + +/** + * @author Kuangcp + * 2024-09-24 16:28 + */ +@Slf4j +public class OrderEventSlowHandler extends OrderEventHandler { + + public OrderEventSlowHandler() { + } + + public OrderEventSlowHandler(String id) { + super(id); + } + + @Override + public void onEvent(OrderEvent event, long sequence, boolean endOfBatch) { + log.info("{} event: {}, sequence: {}, endOfBatch: {}", getId(), event, sequence, endOfBatch); + try { + TimeUnit.SECONDS.sleep(1); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public void onEvent(OrderEvent event) { + log.info("{} event: {}", getId(), event); + try { + TimeUnit.SECONDS.sleep(1); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } +} diff --git a/concurrency/src/test/java/com/github/kuangcp/queue/disruptor/first/FirstTest.java b/concurrency/src/test/java/com/github/kuangcp/queue/disruptor/first/FirstTest.java index 63b79915..ba3b3fff 100644 --- a/concurrency/src/test/java/com/github/kuangcp/queue/disruptor/first/FirstTest.java +++ b/concurrency/src/test/java/com/github/kuangcp/queue/disruptor/first/FirstTest.java @@ -101,4 +101,34 @@ public void testConsumerChain() throws Exception { Thread.currentThread().join(3000); } + + + /** + * 多生产者 多消费者 + */ + @Test + public void testProductConsumerSlow() throws Exception { + Disruptor disruptor = new Disruptor<>( + OrderEvent::new, + 1024 * 1024, + Executors.defaultThreadFactory(), + // 这里的枚举修改为多生产者 + ProducerType.MULTI, + new YieldingWaitStrategy() + ); + // 有多少个消费者就会创建多少线程去执行消费,此时会创建三个线程 + disruptor + .handleEventsWithWorkerPool(new OrderEventSlowHandler("a"), new OrderEventSlowHandler("b")) + .then(new OrderEventSlowHandler("c")); + disruptor.start(); + RingBuffer ringBuffer = disruptor.getRingBuffer(); + OrderEventProducer eventProducer = new OrderEventProducer(ringBuffer); + // 创建一个线程池,模拟多个生产者 + ExecutorService fixedThreadPool = Executors.newFixedThreadPool(10); + for (int i = 0; i < 100; i++) { + fixedThreadPool.execute(() -> eventProducer.onData(UUID.randomUUID().toString())); + } + + Thread.currentThread().join(); + } } From 1bd6a27a44aa50e7614eb79446a3ce86f497908e Mon Sep 17 00:00:00 2001 From: kuangcp Date: Tue, 24 Sep 2024 16:47:49 +0800 Subject: [PATCH 434/476] doc --- .../github/kuangcp/queue/disruptor/first/FirstTest.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/concurrency/src/test/java/com/github/kuangcp/queue/disruptor/first/FirstTest.java b/concurrency/src/test/java/com/github/kuangcp/queue/disruptor/first/FirstTest.java index ba3b3fff..c788b6af 100644 --- a/concurrency/src/test/java/com/github/kuangcp/queue/disruptor/first/FirstTest.java +++ b/concurrency/src/test/java/com/github/kuangcp/queue/disruptor/first/FirstTest.java @@ -104,7 +104,7 @@ public void testConsumerChain() throws Exception { /** - * 多生产者 多消费者 + * 多生产者 多消费者(长耗时) */ @Test public void testProductConsumerSlow() throws Exception { @@ -118,17 +118,18 @@ public void testProductConsumerSlow() throws Exception { ); // 有多少个消费者就会创建多少线程去执行消费,此时会创建三个线程 disruptor - .handleEventsWithWorkerPool(new OrderEventSlowHandler("a"), new OrderEventSlowHandler("b")) + .handleEventsWithWorkerPool(new OrderEventSlowHandler("a1"), new OrderEventSlowHandler("a2"), new OrderEventSlowHandler("a3")) .then(new OrderEventSlowHandler("c")); disruptor.start(); RingBuffer ringBuffer = disruptor.getRingBuffer(); OrderEventProducer eventProducer = new OrderEventProducer(ringBuffer); // 创建一个线程池,模拟多个生产者 ExecutorService fixedThreadPool = Executors.newFixedThreadPool(10); - for (int i = 0; i < 100; i++) { + for (int i = 0; i < 30; i++) { fixedThreadPool.execute(() -> eventProducer.onData(UUID.randomUUID().toString())); } + // 注意当所有消息都被消费后,消费者线程会进入无休止的CAS,导致CPU占用率高企 Thread.currentThread().join(); } } From defd14a8c5025a9d5ca9c1c831000e86eb32e540 Mon Sep 17 00:00:00 2001 From: kuangcp Date: Wed, 25 Sep 2024 10:31:52 +0800 Subject: [PATCH 435/476] interrupt blocked thread --- .../main/java/thread/InterruptBizDemo.java | 11 +- .../java/thread/ThreadStatusTransfer.java | 43 ++++- .../java/thread/InterruptBizDemoTest.java | 27 +++ .../java/thread/ThreadStatusTransferTest.java | 172 +++++++++++++++++- 4 files changed, 236 insertions(+), 17 deletions(-) create mode 100644 concurrency/src/test/java/thread/InterruptBizDemoTest.java diff --git a/concurrency/src/main/java/thread/InterruptBizDemo.java b/concurrency/src/main/java/thread/InterruptBizDemo.java index 9c2a80ea..728a6a5d 100644 --- a/concurrency/src/main/java/thread/InterruptBizDemo.java +++ b/concurrency/src/main/java/thread/InterruptBizDemo.java @@ -18,17 +18,10 @@ public class InterruptBizDemo { private static final BlockingQueue queue = new LinkedBlockingQueue<>(); private static final AtomicBoolean run = new AtomicBoolean(true); - public static void main(String[] args) throws InterruptedException { - log.info("start"); - - singleThreadMode(); -// consumerScheduler(); - } - /** * 保活 */ - private static void consumerScheduler() throws InterruptedException { + static void consumerScheduler() throws InterruptedException { Thread producer = new Thread(() -> { AtomicInteger count = new AtomicInteger(); while (true) { @@ -78,7 +71,7 @@ private static void cpuWork(int x) { } } - private static void singleThreadMode() throws InterruptedException { + static void singleThreadMode() throws InterruptedException { Thread normal = new Thread(() -> log.info("normal")); normal.setName("normal"); normal.start(); diff --git a/concurrency/src/main/java/thread/ThreadStatusTransfer.java b/concurrency/src/main/java/thread/ThreadStatusTransfer.java index 719c0d80..c9d72faf 100644 --- a/concurrency/src/main/java/thread/ThreadStatusTransfer.java +++ b/concurrency/src/main/java/thread/ThreadStatusTransfer.java @@ -2,12 +2,14 @@ import lombok.extern.slf4j.Slf4j; +import java.util.concurrent.TimeUnit; + /** * Created by https://github.com/kuangcp on 17-8-20 下午8:46 * 1. 使用 wait notify notifyAll 需要对调用对象加锁 * 2. 调用 wait 后 线程状态Running变为Waiting, 将当前线程放入对象的等待队列上 * 3. notify 调用后, 等待线程 wait 处 不会立即返回, 需要等到 notify 这个线程释放锁后才有机会 - * 4. notify 调用后 将等待线程从等待队列放入同步队列, 线程状态 Waiting 变成 Blocked + * 4. notify 调用后 将等待线程从等待队列放入同步队列, 等待的线程状态会从 Waiting 变成 Blocked */ @Slf4j public class ThreadStatusTransfer { @@ -41,15 +43,50 @@ static class Notify implements Runnable { public void run() { synchronized (lock) { log.info("hold lock. notify"); - lock.notify(); flag = false; + // 除非有特殊考虑,一般都是使用notifyAll,避免有线程一直等不到唤醒 +// lock.notify(); + lock.notifyAll(); } - // 这一段就是重新获取锁, 会和wait进行竞争, 所以执行顺序不定 + // 这一段就是重新获取锁, 会和wait逻辑进行竞争, 所以执行顺序不定 synchronized (lock) { log.info("hold lock again"); } } } + + static class BlockMarkWait extends Thread { + private boolean holdLock = false; + private final Object lock; + + public BlockMarkWait(Object lock) { + this.lock = lock; + } + + @Override + public void run() { + synchronized (lock) { + try { + log.info("start wait"); + lock.wait(); + holdLock = true; + log.info("get lock"); + TimeUnit.SECONDS.sleep(1); + } catch (InterruptedException e) { + log.error(e.getMessage(), e); + } + } + log.info("finish"); + holdLock = false; + } + + public void tryInterrupt() { + if (!holdLock) { + this.interrupt(); + } + } + } + } diff --git a/concurrency/src/test/java/thread/InterruptBizDemoTest.java b/concurrency/src/test/java/thread/InterruptBizDemoTest.java new file mode 100644 index 00000000..2e19506b --- /dev/null +++ b/concurrency/src/test/java/thread/InterruptBizDemoTest.java @@ -0,0 +1,27 @@ +package thread; + +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; + +/** + * @author Kuangcp + * 2024-09-25 09:34 + */ +@Slf4j +public class InterruptBizDemoTest { + + + @Test + public void testSingleThread() throws Exception { + log.info("start sin"); + + InterruptBizDemo.singleThreadMode(); + } + + @Test + public void testConsumer() throws Exception { + log.info("start"); + + InterruptBizDemo.consumerScheduler(); + } +} diff --git a/concurrency/src/test/java/thread/ThreadStatusTransferTest.java b/concurrency/src/test/java/thread/ThreadStatusTransferTest.java index 0e434ef7..5b1b21fa 100644 --- a/concurrency/src/test/java/thread/ThreadStatusTransferTest.java +++ b/concurrency/src/test/java/thread/ThreadStatusTransferTest.java @@ -1,24 +1,186 @@ package thread; -import java.util.concurrent.TimeUnit; - +import lombok.extern.slf4j.Slf4j; import org.junit.Test; import thread.ThreadStatusTransfer.Notify; import thread.ThreadStatusTransfer.Wait; +import thread.ThreadStatusTransfer.BlockMarkWait; + +import java.util.concurrent.TimeUnit; /** * @author kuangcp on 2019-04-22 9:40 AM */ +@Slf4j public class ThreadStatusTransferTest { @Test public void testMain() throws InterruptedException { - Thread waitThread = new Thread(new Wait(), "WaitThread"); + Thread waitThread = new Thread(new Wait(), "Waiter"); waitThread.start(); - TimeUnit.SECONDS.sleep(100); + TimeUnit.SECONDS.sleep(10); - Thread notifyThread = new Thread(new Notify(), "NotifyThread"); + Thread notifyThread = new Thread(new Notify(), "Notify"); notifyThread.start(); } + + /** + * 效果为 三个线程进入waiting状态,等notify后三个线程抢锁依次执行 + */ + @Test + public void testMultipleWait() throws Exception { + Object lock = new Object(); + + Runnable taskHandler = () -> { + synchronized (lock) { + try { + log.info("start wait"); + lock.wait(); + TimeUnit.SECONDS.sleep(1); + } catch (InterruptedException e) { + log.error(e.getMessage(), e); + } + log.info("finish"); + } + }; + new Thread(taskHandler).start(); + new Thread(taskHandler).start(); + new Thread(taskHandler).start(); + + TimeUnit.SECONDS.sleep(1); + new Thread(() -> { + synchronized (lock) { + log.info("hold lock. notify"); + lock.notifyAll(); + } + synchronized (lock) { + log.info("try lock"); + } + }).start(); + + Thread.currentThread().join(5000); + log.info("finish All"); + } + + /** + * 响应中断的方法: 线程进入等待或是超时等待的状态后,调用interrupt方法都是会响应中断的, + * 即:Object.wait()、Thread.join、Thread.sleep、LockSupport.park的有参和无参方法。 + *

+ * 不响应中断的方法:线程进入阻塞状态后,是不响应中断的,等待进入synchronized的方法或是代码块,都是会被阻塞的,此时不会响应中断, + * 另外还有一个不响应中断的,那就是阻塞在ReentrantLock.lock方法里面的线程,也是不响应中断的, + * 如果想要响应中断,可以使用ReentrantLock.lockInterruptibly方法。 + */ + @Test + public void testBlockInterrupt() throws Exception { + TimeUnit.SECONDS.sleep(6); + log.info("start"); + Object lock = new Object(); + + Runnable taskHandler = () -> { + synchronized (lock) { + try { + log.info("start wait"); + lock.wait(); + log.info("get lock"); + TimeUnit.SECONDS.sleep(1); + } catch (InterruptedException e) { + log.error(e.getMessage(), e); + } + log.info("finish"); + } + }; + Thread a = new Thread(taskHandler); + Thread b = new Thread(taskHandler); + Thread c = new Thread(taskHandler); + + a.start(); + b.start(); + c.start(); + + TimeUnit.SECONDS.sleep(1); + new Thread(() -> { + synchronized (lock) { + try { + log.info("hold lock. start notify"); + TimeUnit.MILLISECONDS.sleep(200); + lock.notifyAll(); + log.info("finish notify"); + } catch (Exception e) { + log.error("", e); + } + } + synchronized (lock) { + log.info("try lock"); + } + }).start(); + + Thread kill = new Thread(() -> { + try { + TimeUnit.MILLISECONDS.sleep(500); + // 由于中断了所有线程,包括了处于 timed_waiting 的线程也被中断,锁也因此释放,blocked的线程也因此拿到了锁后执行sleep方法抛出中断异常 + // 时间间隔短没法明确感受到 blocked 线程在发出中断信号的那一刻没作出响应 + a.interrupt(); + b.interrupt(); + c.interrupt(); + log.info("finish all interrupt"); + } catch (Exception e) { + log.error("", e); + } + }); + kill.start(); + + Thread.currentThread().join(10000); + log.info("exit"); + } + + @Test + public void testBlockInterrupt2() throws Exception { + TimeUnit.SECONDS.sleep(6); + log.info("start"); + Object lock = new Object(); + + BlockMarkWait a = new BlockMarkWait(lock); + BlockMarkWait b = new BlockMarkWait(lock); + BlockMarkWait c = new BlockMarkWait(lock); + + a.start(); + b.start(); + c.start(); + + TimeUnit.SECONDS.sleep(1); + new Thread(() -> { + synchronized (lock) { + try { + log.info("hold lock. start notify"); + TimeUnit.MILLISECONDS.sleep(200); + lock.notifyAll(); + log.info("finish notify"); + } catch (Exception e) { + log.error("", e); + } + } + synchronized (lock) { + log.info("try lock"); + } + }).start(); + + Thread kill = new Thread(() -> { + try { + TimeUnit.MILLISECONDS.sleep(500); + // 加上判断后,只中断blocked的线程,timed_waiting线程不动,就能看到在interrupt的那一刻,blocked线程都是没反应的 + // 只有等waiting线程释放锁后(间隔700ms左右 1000-(500-200))blocked线程获得锁 执行sleep方法时才报错。 + a.tryInterrupt(); + b.tryInterrupt(); + c.tryInterrupt(); + log.info("finish all interrupt"); + } catch (Exception e) { + log.error("", e); + } + }); + kill.start(); + + Thread.currentThread().join(10000); + log.info("exit"); + } } From 3e36aee75d4839d87dbd912f79df887f2753b8fc Mon Sep 17 00:00:00 2001 From: kuangcp Date: Wed, 9 Oct 2024 16:32:21 +0800 Subject: [PATCH 436/476] sort --- .../github/kuangcp/found/BinarySearch.java | 44 ++-- .../java/com/github/kuangcp/sort/Bubble.java | 30 ++- .../java/com/github/kuangcp/sort/Insert.java | 34 ++- .../java/com/github/kuangcp/sort/Quick.java | 229 +++++++++--------- .../java/com/github/kuangcp/sort/Radix.java | 72 +++--- .../java/com/github/kuangcp/sort/Select.java | 28 +-- .../java/com/github/kuangcp/sort/Shell.java | 72 ------ .../github/kuangcp/sort/SortAlgorithm.java | 13 - .../com/github/kuangcp/sort/SortHelper.java | 91 +++---- .../java/com/github/kuangcp/sort/Sorter.java | 11 + .../java/com/github/kuangcp/sort/Tim.java | 2 +- .../kuangcp/found/BinarySearchTest.java | 6 +- .../com/github/kuangcp/sort/SortTest.java | 97 +++++--- 13 files changed, 334 insertions(+), 395 deletions(-) delete mode 100644 algorithms/src/main/java/com/github/kuangcp/sort/Shell.java delete mode 100644 algorithms/src/main/java/com/github/kuangcp/sort/SortAlgorithm.java create mode 100644 algorithms/src/main/java/com/github/kuangcp/sort/Sorter.java diff --git a/algorithms/src/main/java/com/github/kuangcp/found/BinarySearch.java b/algorithms/src/main/java/com/github/kuangcp/found/BinarySearch.java index 011216c0..4dd84994 100644 --- a/algorithms/src/main/java/com/github/kuangcp/found/BinarySearch.java +++ b/algorithms/src/main/java/com/github/kuangcp/found/BinarySearch.java @@ -1,34 +1,34 @@ package com.github.kuangcp.found; /** - * TODO 当数据有重复的时候,这个算法并不能做到返回数据第一次出现的地方 + * 当数据有重复的时候,这个算法并不能做到返回数据第一次出现的地方 */ public class BinarySearch { - private static int index = -2; + private static int index = -2; - private static void found(int[] arr, int left, int right, int data) { + private static void found(int[] arr, int left, int right, int data) { - int mid = (left + right) / 2; - if (arr[mid] == data) { - index = mid; - } + int mid = (left + right) / 2; + if (arr[mid] == data) { + index = mid; + } - if (arr[mid] > data && left < mid - 1) { - found(arr, left, mid, data); - } + if (arr[mid] > data && left < mid - 1) { + found(arr, left, mid, data); + } - if (arr[mid] < data && right > mid + 1) { - found(arr, mid, right, data); + if (arr[mid] < data && right > mid + 1) { + found(arr, mid, right, data); + } } - } - /** - * find num from arr - * - * @return index not found then return -1 - */ - public int find(int[] arr, int data) { - found(arr, 0, arr.length, data); - return index + 1; - } + /** + * find num from arr + * + * @return index not found then return -1 + */ + public int find(int[] arr, int data) { + found(arr, 0, arr.length, data); + return index + 1; + } } diff --git a/algorithms/src/main/java/com/github/kuangcp/sort/Bubble.java b/algorithms/src/main/java/com/github/kuangcp/sort/Bubble.java index 6710be56..a1a106c7 100644 --- a/algorithms/src/main/java/com/github/kuangcp/sort/Bubble.java +++ b/algorithms/src/main/java/com/github/kuangcp/sort/Bubble.java @@ -8,24 +8,22 @@ * * @author kcp */ -public enum Bubble implements SortAlgorithm { +public class Bubble { - INSTANCE; + public static int[] sort(int[] data) { + int[] result = Arrays.copyOf(data, data.length); - public int[] sort(int[] data) { - int[] result = Arrays.copyOf(data, data.length); - - for (int i = 1; i < result.length; i++) { - //用来冒泡的语句,0到已排序的部分 - for (int j = 0; j < result.length - i; j++) { - //大就交换,把最大的沉入最后 - if (result[j] > result[j + 1]) { - int temp = result[j + 1]; - result[j + 1] = result[j]; - result[j] = temp; + for (int i = 1; i < result.length; i++) { + //用来冒泡的语句,0到已排序的部分 + for (int j = 0; j < result.length - i; j++) { + //大就交换,把最大的沉入最后 + if (result[j] > result[j + 1]) { + int temp = result[j + 1]; + result[j + 1] = result[j]; + result[j] = temp; + } + } } - } + return result; } - return result; - } } diff --git a/algorithms/src/main/java/com/github/kuangcp/sort/Insert.java b/algorithms/src/main/java/com/github/kuangcp/sort/Insert.java index 0172628e..431c4402 100644 --- a/algorithms/src/main/java/com/github/kuangcp/sort/Insert.java +++ b/algorithms/src/main/java/com/github/kuangcp/sort/Insert.java @@ -5,30 +5,28 @@ /** * 插入法排序,由小到大 * 最坏的情况就是数列是有序的大到小,那么需要比较和移动 n(n+1)/2 次 时间复杂度是O(n^2) - * + *

* 指针从第二个数开始,后移,发现当前数比前面一个数小就把前面那个数后移,往前比较,知道找到那个数小于当前数为止,指针后移 * 直至到最后一个 * 思想是,指针位置之前的数都是有序的 */ -public enum Insert implements SortAlgorithm { +public class Insert { - INSTANCE; + public static int[] sort(int[] data) { + int[] result = Arrays.copyOf(data, data.length); + int i, j, tmp; - public int[] sort(int[] data) { - int[] result = Arrays.copyOf(data, data.length); - int i, j, tmp; - - for (i = 1; i < result.length; i++) { - tmp = result[i]; - j = i - 1; - //比较,如果是比前面还要小,就将数字往后移动一位。将小的那一位插入到合适位置 - while (j >= 0 && tmp < result[j]) { - result[j + 1] = result[j]; - j--; - } - result[j + 1] = tmp; + for (i = 1; i < result.length; i++) { + tmp = result[i]; + j = i - 1; + //比较,如果是比前面还要小,就将数字往后移动一位。将小的那一位插入到合适位置 + while (j >= 0 && tmp < result[j]) { + result[j + 1] = result[j]; + j--; + } + result[j + 1] = tmp; + } + return result; } - return result; - } } diff --git a/algorithms/src/main/java/com/github/kuangcp/sort/Quick.java b/algorithms/src/main/java/com/github/kuangcp/sort/Quick.java index 8d2c0d8b..1c1029af 100644 --- a/algorithms/src/main/java/com/github/kuangcp/sort/Quick.java +++ b/algorithms/src/main/java/com/github/kuangcp/sort/Quick.java @@ -9,137 +9,134 @@ * * @author Myth */ -public enum Quick implements SortAlgorithm { +public class Quick { - INSTANCE; - - @Override - public int[] sort(int[] data) { - int[] result = Arrays.copyOf(data, data.length); - QuickSort sort = new FirstImpl(); - sort.sortData(result, 0, result.length - 1); - return result; - } - - - interface QuickSort { - - void sortData(int[] data, int low, int high); - } + public static int[] sort(int[] data) { + int[] result = Arrays.copyOf(data, data.length); + QuickSort sort = new FirstImpl(); + sort.sortData(result, 0, result.length - 1); + return result; + } - /** - * 高效的写法 - */ - static class FirstImpl implements QuickSort { - @Override - public void sortData(int[] data, int low, int high) { - if (low >= high) { - return; - } + interface QuickSort { - int lowIndex = low; - int highIndex = high; + void sortData(int[] data, int low, int high); + } - int value = data[low]; - while (lowIndex < highIndex) { - // 找出右边小于低位所在的标识值 - while (lowIndex < highIndex && data[highIndex] >= value) { - highIndex -= 1; + /** + * 高效的写法 + */ + static class FirstImpl implements QuickSort { + + @Override + public void sortData(int[] data, int low, int high) { + if (low >= high) { + return; + } + + int lowIndex = low; + int highIndex = high; + + int value = data[low]; + while (lowIndex < highIndex) { + // 找出右边小于低位所在的标识值 + while (lowIndex < highIndex && data[highIndex] >= value) { + highIndex -= 1; + } + data[lowIndex] = data[highIndex]; + + // 找出左边大于标识值 + while (lowIndex < highIndex && data[lowIndex] <= value) { + lowIndex += 1; + } + data[highIndex] = data[lowIndex]; + } + + data[lowIndex] = value; + + sortData(data, low, lowIndex - 1); + sortData(data, highIndex + 1, high); } - data[lowIndex] = data[highIndex]; + } - // 找出左边大于标识值 - while (lowIndex < highIndex && data[lowIndex] <= value) { - lowIndex += 1; + /** + * 个人手写 + */ + static class SecondImpl implements QuickSort { + + @Override + public void sortData(int[] data, int low, int high) { + int lowIndex = low; + int highIndex = high; + int index = data[low]; + + while (lowIndex < highIndex) { + while (lowIndex < highIndex && data[highIndex] >= index) { + highIndex--; + } + if (lowIndex < highIndex) { + int temp = data[highIndex]; + data[highIndex] = data[lowIndex]; + data[lowIndex] = temp; + lowIndex++; + } + + while (lowIndex < highIndex && data[lowIndex] <= index) { + lowIndex++; + } + if (lowIndex < highIndex) { + int temp = data[highIndex]; + data[highIndex] = data[lowIndex]; + data[lowIndex] = temp; + highIndex--; + } + } + + if (lowIndex > low) { + sortData(data, low, lowIndex - 1); + } + if (highIndex < high) { + sortData(data, lowIndex + 1, high); + } } - data[highIndex] = data[lowIndex]; - } + } - data[lowIndex] = value; + /** + * 对象数组排序 + */ + public > T[] quickSort(T[] data, int start, int end) { + int low = start + 1, high = end; + T key = data[start]; - sortData(data, low, lowIndex - 1); - sortData(data, highIndex + 1, high); - } - } - - /** - * 个人手写 - */ - static class SecondImpl implements QuickSort { - - @Override - public void sortData(int[] data, int low, int high) { - int lowIndex = low; - int highIndex = high; - int index = data[low]; - - while (lowIndex < highIndex) { - while (lowIndex < highIndex && data[highIndex] >= index) { - highIndex--; + if (start >= end) { + return (data); } - if (lowIndex < highIndex) { - int temp = data[highIndex]; - data[highIndex] = data[lowIndex]; - data[lowIndex] = temp; - lowIndex++; + + while (true) { + while (data[high].compareTo(key) > 0) { + high--; + } + while (data[low].compareTo(key) < 0 && low < high) { + low++; + } + if (low >= high) { + break; + } + if (data[low] == key) { + high--; + } else { + low++; + } } - while (lowIndex < highIndex && data[lowIndex] <= index) { - lowIndex++; + if (start < low - 1) { + this.quickSort(data, start, low - 1); } - if (lowIndex < highIndex) { - int temp = data[highIndex]; - data[highIndex] = data[lowIndex]; - data[lowIndex] = temp; - highIndex--; + if (high + 1 < end) { + this.quickSort(data, high + 1, end); } - } - - if (lowIndex > low) { - sortData(data, low, lowIndex - 1); - } - if (highIndex < high) { - sortData(data, lowIndex + 1, high); - } - } - } - - /** - * 对象数组排序 - */ - public > T[] quickSort(T[] data, int start, int end) { - int low = start + 1, high = end; - T key = data[start]; - - if (start >= end) { - return (data); - } - while (true) { - while (data[high].compareTo(key) > 0) { - high--; - } - while (data[low].compareTo(key) < 0 && low < high) { - low++; - } - if (low >= high) { - break; - } - if (data[low] == key) { - high--; - } else { - low++; - } + return data; } - - if (start < low - 1) { - this.quickSort(data, start, low - 1); - } - if (high + 1 < end) { - this.quickSort(data, high + 1, end); - } - - return data; - } } \ No newline at end of file diff --git a/algorithms/src/main/java/com/github/kuangcp/sort/Radix.java b/algorithms/src/main/java/com/github/kuangcp/sort/Radix.java index 3ed184e0..1a9c0d08 100644 --- a/algorithms/src/main/java/com/github/kuangcp/sort/Radix.java +++ b/algorithms/src/main/java/com/github/kuangcp/sort/Radix.java @@ -14,53 +14,51 @@ * * @author Myth */ -public enum Radix implements SortAlgorithm { +public class Radix { - INSTANCE; + //需要确定盒子的轮数 也就是数值的最大位数 + static int maxLen = 5; - //需要确定盒子的轮数 也就是数值的最大位数 - static int maxLen = 5; + public static int[] sort(int[] data) { + int[] result = Arrays.copyOf(data, data.length); + List temp = Arrays.stream(data).boxed().collect(Collectors.toList()); - public int[] sort(int[] data) { - int[] result = Arrays.copyOf(data, data.length); - List temp = Arrays.stream(data).boxed().collect(Collectors.toList()); + // 0 -> []. 1 -> [], ... 9 -> [] + Map> box = new HashMap<>(); - // 0 -> []. 1 -> [], ... 9 -> [] - Map> box = new HashMap<>(); + for (int i = 0; i < maxLen; i++) { + box.clear(); + for (int value : temp) { + int weight = (int) ((value / Math.pow(10, i)) % 10); + addValue(box, weight, value); + } - for (int i = 0; i < maxLen; i++) { - box.clear(); - for (int value : temp) { - int weight = (int) ((value / Math.pow(10, i)) % 10); - addValue(box, weight, value); - } - - temp = new ArrayList<>(data.length); - for (int j = 0; j < 10; j++) { - List list = box.get(j); - if (Objects.nonNull(list)) { - temp.addAll(list); + temp = new ArrayList<>(data.length); + for (int j = 0; j < 10; j++) { + List list = box.get(j); + if (Objects.nonNull(list)) { + temp.addAll(list); + } + } } - } + + setArray(temp, result); + return result; } - setArray(temp, result); - return result; - } + private static void addValue(Map> box, int weight, int value) { + List result = box.get(weight); + if (Objects.isNull(result)) { + result = new LinkedList<>(); + box.put(weight, result); + } - private static void addValue(Map> box, int weight, int value) { - List result = box.get(weight); - if (Objects.isNull(result)) { - result = new LinkedList<>(); - box.put(weight, result); + result.add(value); } - result.add(value); - } - - private static void setArray(List result, int[] data) { - for (int i = 0; i < data.length; i++) { - data[i] = result.get(i); + private static void setArray(List result, int[] data) { + for (int i = 0; i < data.length; i++) { + data[i] = result.get(i); + } } - } } diff --git a/algorithms/src/main/java/com/github/kuangcp/sort/Select.java b/algorithms/src/main/java/com/github/kuangcp/sort/Select.java index 24d17e2b..366232b9 100644 --- a/algorithms/src/main/java/com/github/kuangcp/sort/Select.java +++ b/algorithms/src/main/java/com/github/kuangcp/sort/Select.java @@ -7,24 +7,22 @@ * 原理是:第一个依次与后面所有元素进行比较,遇到比自己小的就交换直到最后,第二轮就拿第二个元素去依次比较 * 最坏的情况是: 时间复杂度是O(n^2) */ -public enum Select implements SortAlgorithm { +public class Select { - INSTANCE; + public static int[] sort(int[] data) { + int[] result = Arrays.copyOf(data, data.length); - public int[] sort(int[] data) { - int[] result = Arrays.copyOf(data, data.length); - - for (int i = 0; i < result.length - 1; i++) { - //这个循环就是把较小数堆叠在数组头,依次与后面比较再交换 - for (int j = i + 1; j < result.length; j++) { - if (result[i] > result[j]) { - int temp = result[i]; - result[i] = result[j]; - result[j] = temp; + for (int i = 0; i < result.length - 1; i++) { + //这个循环就是把较小数堆叠在数组头,依次与后面比较再交换 + for (int j = i + 1; j < result.length; j++) { + if (result[i] > result[j]) { + int temp = result[i]; + result[i] = result[j]; + result[j] = temp; + } + } } - } + return result; } - return result; - } } diff --git a/algorithms/src/main/java/com/github/kuangcp/sort/Shell.java b/algorithms/src/main/java/com/github/kuangcp/sort/Shell.java deleted file mode 100644 index 186005bc..00000000 --- a/algorithms/src/main/java/com/github/kuangcp/sort/Shell.java +++ /dev/null @@ -1,72 +0,0 @@ -package com.github.kuangcp.sort; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -/** - * shell排序,从小到大 - * 算法思想: - * 对一个线性数据,先取一个随机数d1(d1<数据总数)将所有数据分成d组,将所有距离为d倍数的分成一组 - * 对各组进行直接插入排序,然后再取第二个数d2,直到dn=1(dn<……d3> dataList = new ArrayList<>(); - List arrs = new ArrayList<>(); - for (int anArr : arr) { - arrs.add(anArr); - } - dataList.add(arrs); - int last = arr.length; - int randomNum; - - boolean flag = true; - while (flag) { - randomNum = (int) (Math.random() * last - 1) + 1;//取得随机数.比上一次取得值要小 -// System.out.println("\n随机数 : "+d); - List> temp = new ArrayList<>(); -// int index = 0; - //将数据分组 - for (int i = 0; i < randomNum; i++) { - List dat = new ArrayList<>(); - for (List temps : dataList) { - for (int k = i; k < temps.size(); k += randomNum) { -// System.out.println(temps.get(k)); - if (temps.get(k) != null) { - dat.add(temps.get(k)); - } - } - } - int[] arrdat = new int[dat.size()]; - for (int h = 0; h < dat.size(); h++) { - arrdat[h] = dat.get(h); - } - Insert.INSTANCE.sort(arrdat);//直接插入排序 - if (randomNum == 1) { - System.arraycopy(arrdat, 0, arr, 0, arr.length); - flag = false; - } -// TestSortTime.display(arrdat); - temp.add(dat);//加入集合 -// System.out.println("缓存数组内存长度:"+dat.size()); - - } - dataList = temp; - last = randomNum; - } - return arr; - } -} diff --git a/algorithms/src/main/java/com/github/kuangcp/sort/SortAlgorithm.java b/algorithms/src/main/java/com/github/kuangcp/sort/SortAlgorithm.java deleted file mode 100644 index f8599153..00000000 --- a/algorithms/src/main/java/com/github/kuangcp/sort/SortAlgorithm.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.github.kuangcp.sort; - -/** - * @author kuangcp on 3/28/19-7:29 PM - */ -public interface SortAlgorithm { - - int[] sort(int[] data); - - default String getName() { - return this.getClass().getSimpleName(); - } -} diff --git a/algorithms/src/main/java/com/github/kuangcp/sort/SortHelper.java b/algorithms/src/main/java/com/github/kuangcp/sort/SortHelper.java index cc01b10c..27349e04 100644 --- a/algorithms/src/main/java/com/github/kuangcp/sort/SortHelper.java +++ b/algorithms/src/main/java/com/github/kuangcp/sort/SortHelper.java @@ -1,9 +1,11 @@ package com.github.kuangcp.sort; -import java.util.Arrays; -import java.util.List; -import java.util.concurrent.ThreadLocalRandom; import lombok.extern.slf4j.Slf4j; +import org.junit.Assert; + +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.concurrent.ThreadLocalRandom; /** * Created by https://github.com/kuangcp @@ -14,58 +16,59 @@ @Slf4j class SortHelper { - // 数量级 - static int AMOUNT = 1000; + // 数量级 + static int AMOUNT = 1000; - // 数据范围 - static int SCOPE = 999999; - static boolean show = false; + // 数据范围 + static int SCOPE = 999999; + static boolean show = false; - static List algorithms = Arrays.asList( - Radix.INSTANCE - , Bubble.INSTANCE - , Insert.INSTANCE - , Select.INSTANCE - , Quick.INSTANCE -// , Shell.INSTANCE - ); + static Map algorithms = new LinkedHashMap<>(); - static int[] data; + static { + algorithms.put("Bubble", Bubble::sort); + algorithms.put("Select", Select::sort); + algorithms.put("Insert", Insert::sort); + algorithms.put("Radix", Radix::sort); + algorithms.put("Quick", Quick::sort); + } - static void init() { - Radix.maxLen = getScopeLength(); + static int[] data; - data = new int[AMOUNT]; - for (int i = 0; i < AMOUNT; i++) { - data[i] = ThreadLocalRandom.current().nextInt(SCOPE); - } - } + static void init() { + Radix.maxLen = getScopeLength(); - private static int getScopeLength() { - return String.valueOf(SCOPE).length(); - } + data = new int[AMOUNT]; + for (int i = 0; i < AMOUNT; i++) { + data[i] = ThreadLocalRandom.current().nextInt(SCOPE); + } + } - static void showData(int[] data) { - if (!show) { - return; + private static int getScopeLength() { + return String.valueOf(SCOPE).length(); } - for (int i = 0; i < data.length; i++) { - int length = getScopeLength(); - System.out.printf("%" + length + "d ", data[i]); - if ((i + 1) % 19 == 0) { + static void showData(int[] data) { + if (!show) { + return; + } + + for (int i = 0; i < data.length; i++) { + int length = getScopeLength(); + System.out.printf("%" + length + "d ", data[i]); + if ((i + 1) % 19 == 0) { + System.out.println(); + } + } System.out.println(); - } } - System.out.println(); - } - static void validate(int[] data) { - for (int i = 1; i < data.length; i++) { - if (data[i - 1] > data[i]) { - log.error("sort algorithm has error"); - System.exit(1); - } + static void validate(int[] data) { + for (int i = 1; i < data.length; i++) { + if (data[i - 1] > data[i]) { + log.error("sort algorithm has error"); + Assert.fail(); + } + } } - } } \ No newline at end of file diff --git a/algorithms/src/main/java/com/github/kuangcp/sort/Sorter.java b/algorithms/src/main/java/com/github/kuangcp/sort/Sorter.java new file mode 100644 index 00000000..2cb22858 --- /dev/null +++ b/algorithms/src/main/java/com/github/kuangcp/sort/Sorter.java @@ -0,0 +1,11 @@ +package com.github.kuangcp.sort; + +/** + * @author kuangcp on 3/28/19-7:29 PM + */ +@FunctionalInterface +public interface Sorter { + + int[] sort(int[] data); + +} diff --git a/algorithms/src/main/java/com/github/kuangcp/sort/Tim.java b/algorithms/src/main/java/com/github/kuangcp/sort/Tim.java index bd43398d..ff901f66 100644 --- a/algorithms/src/main/java/com/github/kuangcp/sort/Tim.java +++ b/algorithms/src/main/java/com/github/kuangcp/sort/Tim.java @@ -8,6 +8,6 @@ * * @author kuangcp on 18-8-27-上午12:19 */ -public enum Tim { +public class Tim { } diff --git a/algorithms/src/test/java/com/github/kuangcp/found/BinarySearchTest.java b/algorithms/src/test/java/com/github/kuangcp/found/BinarySearchTest.java index 8a93df95..44f37138 100644 --- a/algorithms/src/test/java/com/github/kuangcp/found/BinarySearchTest.java +++ b/algorithms/src/test/java/com/github/kuangcp/found/BinarySearchTest.java @@ -25,7 +25,7 @@ public void testFind() { dat[i] = (int) (Math.random() * dataRange + dataBaseValue); } - Insert.INSTANCE.sort(dat); + Insert.sort(dat); for (int i = 0; i < dat.length; i++) { //将数组遍历一下 System.out.print(dat[i] + " "); @@ -37,9 +37,9 @@ public void testFind() { int randomValue = (int) (Math.random() * dataRange + dataBaseValue); int result = s.find(dat, randomValue); if (result != -1) { - log.debug("你要找的数据是第 {} 个数字 {}", result, randomValue); + log.info("你要找的数据是第 {} 个数字 {}", result, randomValue); } else { - log.debug("该数据不存在,查找失败!value={}", randomValue); + log.info("该数据不存在,查找失败!value={}", randomValue); } } } \ No newline at end of file diff --git a/algorithms/src/test/java/com/github/kuangcp/sort/SortTest.java b/algorithms/src/test/java/com/github/kuangcp/sort/SortTest.java index fb9275d4..c0a8fc71 100644 --- a/algorithms/src/test/java/com/github/kuangcp/sort/SortTest.java +++ b/algorithms/src/test/java/com/github/kuangcp/sort/SortTest.java @@ -12,42 +12,63 @@ @Slf4j public class SortTest { - // https://github.com/Kuangcp/GoBase/tree/master/algorithm/sort - // test sort algorithms was correct - @Test - public void testSortCorrect() { - SortHelper.AMOUNT = 170; - SortHelper.SCOPE = 999; - SortHelper.show = true; - - SortHelper.init(); - - SortHelper.algorithms.forEach(sort -> { - log.info("sort: name={}", sort.getName()); - int[] data = SortHelper.data; - - SortHelper.showData(data); - int[] result = sort.sort(data); - SortHelper.showData(result); - - SortHelper.validate(result); - System.out.println(); - }); - } - - @Test - public void testSortPerformance() { - SortHelper.AMOUNT = 2000; - SortHelper.SCOPE = 10; - - SortHelper.init(); - - SortHelper.algorithms.forEach(sort -> { - GetRunTime runTime = new GetRunTime().startCount(); - int[] result = sort.sort(SortHelper.data); - runTime.endCountOneLine(sort.getName()); - - SortHelper.validate(result); - }); - } + // https://github.com/Kuangcp/GoBase/tree/master/algorithm/sort + // test sort algorithms was correct + @Test + public void testSortCorrect() { + SortHelper.AMOUNT = 170; + SortHelper.SCOPE = 999; + SortHelper.show = true; + + SortHelper.init(); + + SortHelper.algorithms.forEach((key, value) -> { + log.info("sort: name={}", key); + int[] data = SortHelper.data; + + SortHelper.showData(data); + int[] result = value.sort(data); + SortHelper.showData(result); + + SortHelper.validate(result); + System.out.println(); + }); + } + + @Test + public void testSingleSortCorrect() { + SortHelper.AMOUNT = 170; + SortHelper.SCOPE = 999; + SortHelper.show = true; + Sorter sort = Insert::sort; + + SortHelper.init(); + int[] data = SortHelper.data; + + SortHelper.showData(data); + int[] result = sort.sort(data); + SortHelper.showData(result); + + SortHelper.validate(result); + System.out.println(); + } + + @Test + public void testSortPerformance() { + SortHelper.AMOUNT = 30000; + SortHelper.SCOPE = 1000000; + + SortHelper.init(); + + for (int i = 0; i < 10; i++) { + SortHelper.algorithms.forEach((key, value) -> { + GetRunTime runTime = new GetRunTime().startCount(); + int[] result = value.sort(SortHelper.data); + runTime.endCountOneLine(key); + +// SortHelper.validate(result); + }); + System.out.println(); + } + } } From de0ab98f4c26fb7fe091a26a8e557622b3b920a2 Mon Sep 17 00:00:00 2001 From: kuangcp Date: Wed, 6 Nov 2024 14:09:54 +0800 Subject: [PATCH 437/476] fmt --- README.md | 16 ++--- .../kuangcp/validation/BeanValidatorTest.java | 72 +++++++++---------- 2 files changed, 44 insertions(+), 44 deletions(-) diff --git a/README.md b/README.md index 7a31e10e..031c78ee 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,12 @@ -# Java 基础学习 +

+

Java 基础

-[![Java Version](https://img.shields.io/badge/JDK-Java%208-red.svg)](https://www.java.com/zh_CN/download/) -[![Gradle 5.3](https://img.shields.io/badge/Gradle-5.3-green.svg)](https://docs.gradle.org/5.3/userguide/userguide.html) -[![996.icu](https://img.shields.io/badge/link-996.icu-red.svg)](https://996.icu) -[![codebeat badge](https://codebeat.co/badges/9145f9a8-a1aa-4c67-bb2b-f9dd12e924d4)](https://codebeat.co/projects/github-com-kuangcp-javabase-master) -[![Maintainability](https://api.codeclimate.com/v1/badges/23134c0d2348845fecec/maintainability)](https://codeclimate.com/github/Kuangcp/JavaBase/maintainability) +

+ + + + +

************************ @@ -16,8 +18,6 @@ | [Java Gui](/gui) | [测试](/test) | [业务问题](/question) | - - ************************ ## 同类仓库 diff --git a/web/src/test/java/com/github/kuangcp/validation/BeanValidatorTest.java b/web/src/test/java/com/github/kuangcp/validation/BeanValidatorTest.java index 0fda64b0..16266e82 100644 --- a/web/src/test/java/com/github/kuangcp/validation/BeanValidatorTest.java +++ b/web/src/test/java/com/github/kuangcp/validation/BeanValidatorTest.java @@ -7,45 +7,45 @@ @Slf4j public class BeanValidatorTest { - @Test - public void testValid() { - TestParam param = new TestParam(); - param.setState(ActiveState.ACTIVE); - param.setStr(StrState.SECOND); - BeanValidator.check(param); - } + @Test + public void testValid() { + TestParam param = new TestParam(); + param.setState(ActiveState.ACTIVE); + param.setStr(StrState.SECOND); + BeanValidator.check(param); + } - @Test - public void testInvalid() { - TestParam param = new TestParam(); - param.setState(-1); - param.setStr(StrState.SECOND); - try { - BeanValidator.check(param); - Assert.fail(); - } catch (Exception e) { - Assert.assertEquals(e.getMessage(), ErrorMsgConstant.STATE_ERROR); + @Test + public void testInvalid() { + TestParam param = new TestParam(); + param.setState(-1); + param.setStr(StrState.SECOND); + try { + BeanValidator.check(param); + Assert.fail(); + } catch (Exception e) { + Assert.assertEquals(e.getMessage(), ErrorMsgConstant.STATE_ERROR); + } } - } - @Test - public void testValidStr() { - TestParam param = new TestParam(); - param.setStr(StrState.SECOND); - param.setState(ActiveState.NONE); - BeanValidator.check(param); - } + @Test + public void testValidStr() { + TestParam param = new TestParam(); + param.setStr(StrState.SECOND); + param.setState(ActiveState.NONE); + BeanValidator.check(param); + } - @Test - public void testInvalidStr() { - TestParam param = new TestParam(); - param.setStr("2s"); - param.setState(3); - try { - BeanValidator.check(param); - Assert.fail(); - } catch (Exception e) { - Assert.assertEquals(e.getMessage(), ErrorMsgConstant.STR_STATE_ERROR); + @Test + public void testInvalidStr() { + TestParam param = new TestParam(); + param.setStr("2s"); + param.setState(3); + try { + BeanValidator.check(param); + Assert.fail(); + } catch (Exception e) { + Assert.assertEquals(e.getMessage(), ErrorMsgConstant.STR_STATE_ERROR); + } } - } } From 7f01f1adae7862c149b89b10afac453b88b5356e Mon Sep 17 00:00:00 2001 From: kuangcp Date: Thu, 7 Nov 2024 15:48:19 +0800 Subject: [PATCH 438/476] fmt --- .../com/github/kuangcp/serialize/Person.java | 34 ++-- .../kuangcp/serialize/SerializeTest.java | 181 +++++++++--------- .../kuangcp/serialize/json/GsonTest.java | 48 ++--- .../kuangcp/serialize/json/ReadJsonTest.java | 50 ++--- .../kuangcp/serialize/json/WriteJsonTest.java | 61 +++--- 5 files changed, 191 insertions(+), 183 deletions(-) diff --git a/class/src/main/java/com/github/kuangcp/serialize/Person.java b/class/src/main/java/com/github/kuangcp/serialize/Person.java index 61e5b588..774cf324 100644 --- a/class/src/main/java/com/github/kuangcp/serialize/Person.java +++ b/class/src/main/java/com/github/kuangcp/serialize/Person.java @@ -1,8 +1,9 @@ package com.github.kuangcp.serialize; -import java.io.Serializable; import lombok.Data; +import java.io.Serializable; + /** * Created by https://github.com/kuangcp on 17-10-24 下午2:26 * @@ -11,20 +12,23 @@ @Data public class Person implements Serializable { - private String name; - private String address; - private String phone; + private String name; + private String address; + private String phone; + + public Person() { + } - public Person(String name) { - this.name = name; - } + public Person(String name) { + this.name = name; + } - @Override - public String toString() { - return "Person{" + - "name='" + name + '\'' + - ", address='" + address + '\'' + - ", phone='" + phone + '\'' + - '}'; - } + @Override + public String toString() { + return "Person{" + + "name='" + name + '\'' + + ", address='" + address + '\'' + + ", phone='" + phone + '\'' + + '}'; + } } diff --git a/class/src/test/java/com/github/kuangcp/serialize/SerializeTest.java b/class/src/test/java/com/github/kuangcp/serialize/SerializeTest.java index 3c9fe664..bda960fd 100644 --- a/class/src/test/java/com/github/kuangcp/serialize/SerializeTest.java +++ b/class/src/test/java/com/github/kuangcp/serialize/SerializeTest.java @@ -1,7 +1,8 @@ package com.github.kuangcp.serialize; -import static org.hamcrest.CoreMatchers.equalTo; -import static org.junit.Assert.assertThat; +import lombok.extern.slf4j.Slf4j; +import org.junit.Assert; +import org.junit.Test; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -11,9 +12,9 @@ import java.io.NotSerializableException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; -import lombok.extern.slf4j.Slf4j; -import org.junit.Assert; -import org.junit.Test; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.junit.Assert.assertThat; /** * Created by https://github.com/kuangcp on 17-10-24 下午2:27 使用jdk自带的Serializable接口序列化对象 然后反序列化对象 @@ -23,90 +24,90 @@ @Slf4j public class SerializeTest { - // 字节数组流 - @Test - public void testSerializeWithByte() throws IOException, ClassNotFoundException { - Person person = new Person("name"); - - ByteArrayOutputStream byteOutput = new ByteArrayOutputStream(); - ObjectOutputStream output = new ObjectOutputStream(byteOutput); - output.writeObject(person); - - log.info("content={}", byteOutput.toString()); - - ByteArrayInputStream byteInput = new ByteArrayInputStream(byteOutput.toByteArray()); - - ObjectInputStream input = new ObjectInputStream(byteInput); - Person result = (Person) input.readObject(); - assertThat(result.getName(), equalTo("name")); - } - - /** - * 序列化对象上某个属性没有实现序列化接口,但该属性没有值 - */ - @Test - public void testSerializeWithNonSerializableField() throws IOException, ClassNotFoundException { - Address address = new Address(); - address.setCountry("country"); - - ByteArrayOutputStream byteOutput = new ByteArrayOutputStream(); - ObjectOutputStream output = new ObjectOutputStream(byteOutput); - output.writeObject(address); - - log.info("content={}", byteOutput.toString()); - - ByteArrayInputStream byteInput = new ByteArrayInputStream(byteOutput.toByteArray()); - - ObjectInputStream input = new ObjectInputStream(byteInput); - Address result = (Address) input.readObject(); - assertThat(result.getCountry(), equalTo("country")); - } - - /** - * 序列化对象上某个属性没有实现序列化接口,且该属性有值 - */ - @Test(expected = NotSerializableException.class) - public void testSerializeWithNonSerializableFieldAndValue() throws IOException { - Address address = new Address(); - address.setCountry("country"); - Street street = new Street(); - street.setStreet("street"); - address.setStreet(street); - - ByteArrayOutputStream byteOutput = new ByteArrayOutputStream(); - ObjectOutputStream output = new ObjectOutputStream(byteOutput); - output.writeObject(address); - } - - @Test - public void testSerializeWithFile() { - try { - writeFile(); - readFile(); - } catch (IOException | ClassNotFoundException e) { - log.error(e.getMessage(), e); - Assert.fail(); + // 字节数组流 + @Test + public void testSerializeWithByte() throws IOException, ClassNotFoundException { + Person person = new Person("name"); + + ByteArrayOutputStream byteOutput = new ByteArrayOutputStream(); + ObjectOutputStream output = new ObjectOutputStream(byteOutput); + output.writeObject(person); + + log.info("content={}", byteOutput.toString()); + + ByteArrayInputStream byteInput = new ByteArrayInputStream(byteOutput.toByteArray()); + + ObjectInputStream input = new ObjectInputStream(byteInput); + Person result = (Person) input.readObject(); + assertThat(result.getName(), equalTo("name")); + } + + /** + * 序列化对象上某个属性没有实现序列化接口,但该属性没有值 + */ + @Test + public void testSerializeWithNonSerializableField() throws IOException, ClassNotFoundException { + Address address = new Address(); + address.setCountry("country"); + + ByteArrayOutputStream byteOutput = new ByteArrayOutputStream(); + ObjectOutputStream output = new ObjectOutputStream(byteOutput); + output.writeObject(address); + + log.info("content={}", byteOutput.toString()); + + ByteArrayInputStream byteInput = new ByteArrayInputStream(byteOutput.toByteArray()); + + ObjectInputStream input = new ObjectInputStream(byteInput); + Address result = (Address) input.readObject(); + assertThat(result.getCountry(), equalTo("country")); + } + + /** + * 序列化对象上某个属性没有实现序列化接口,且该属性有值 + */ + @Test(expected = NotSerializableException.class) + public void testSerializeWithNonSerializableFieldAndValue() throws IOException { + Address address = new Address(); + address.setCountry("country"); + Street street = new Street(); + street.setStreet("street"); + address.setStreet(street); + + ByteArrayOutputStream byteOutput = new ByteArrayOutputStream(); + ObjectOutputStream output = new ObjectOutputStream(byteOutput); + output.writeObject(address); + } + + @Test + public void testSerializeWithFile() { + try { + writeFile(); + readFile(); + } catch (IOException | ClassNotFoundException e) { + log.error(e.getMessage(), e); + Assert.fail(); + } + } + + private void writeFile() throws IOException { + Person person = new Person("myth"); + + FileOutputStream fileOutputStream = new FileOutputStream("/tmp/person.log"); + ObjectOutputStream out = new ObjectOutputStream(fileOutputStream); + out.writeObject(person); + out.close(); + fileOutputStream.close(); + log.debug("序列化完成, person={}", person); + } + + private void readFile() throws IOException, ClassNotFoundException { + FileInputStream fileInputStream = new FileInputStream("/tmp/person.log"); + ObjectInputStream in = new ObjectInputStream(fileInputStream); + Person object = (Person) in.readObject(); + in.close(); + fileInputStream.close(); + System.out.println(object.toString()); + assertThat(object.getName(), equalTo("myth")); } - } - - private void writeFile() throws IOException { - Person person = new Person("myth"); - - FileOutputStream fileOutputStream = new FileOutputStream("/tmp/person.log"); - ObjectOutputStream out = new ObjectOutputStream(fileOutputStream); - out.writeObject(person); - out.close(); - fileOutputStream.close(); - log.debug("序列化完成, person={}", person); - } - - private void readFile() throws IOException, ClassNotFoundException { - FileInputStream fileInputStream = new FileInputStream("/tmp/person.log"); - ObjectInputStream in = new ObjectInputStream(fileInputStream); - Person object = (Person) in.readObject(); - in.close(); - fileInputStream.close(); - System.out.println(object.toString()); - assertThat(object.getName(), equalTo("myth")); - } } diff --git a/class/src/test/java/com/github/kuangcp/serialize/json/GsonTest.java b/class/src/test/java/com/github/kuangcp/serialize/json/GsonTest.java index 55007a14..4700c1cd 100644 --- a/class/src/test/java/com/github/kuangcp/serialize/json/GsonTest.java +++ b/class/src/test/java/com/github/kuangcp/serialize/json/GsonTest.java @@ -14,36 +14,36 @@ @Slf4j public class GsonTest { - private Gson gson = new Gson(); + private Gson gson = new Gson(); - @Test - public void testRead() { + @Test + public void testRead() { - Code code = gson.fromJson("{\"code\":12, \"name\":\"ui\"}", Code.class); - log.info(": code={}", code); - } + Code code = gson.fromJson("{\"code\":12, \"name\":\"ui\"}", Code.class); + log.info(": code={}", code); + } - /** - * fromJSON a standard json string - */ - @Test - public void testRead2() { - String origin = "{\"code\":3131,\"playerId\":216,\"title\":\"new email\"," - + "\"content\":\"send by admin platform\"," - + "\"attachment\":\"[{\\\"func\\\":\\\"addItem\\\", \\\"args\\\":[\\\"item_1\\\", 100]}]\"}"; + /** + * fromJSON a standard json string + */ + @Test + public void testRead2() { + String origin = "{\"code\":3131,\"playerId\":216,\"title\":\"new email\"," + + "\"content\":\"send by admin platform\"," + + "\"attachment\":\"[{\\\"func\\\":\\\"addItem\\\", \\\"args\\\":[\\\"item_1\\\", 100]}]\"}"; - JsonElement element = new JsonParser().parse(origin); - JsonObject rawJsonObject = element.getAsJsonObject(); - int code = rawJsonObject.get("code").getAsInt(); - log.info("result: code={}", code); - log.info(": jsonElement={}", element); - } + JsonElement element = new JsonParser().parse(origin); + JsonObject rawJsonObject = element.getAsJsonObject(); + int code = rawJsonObject.get("code").getAsInt(); + log.info("result: code={}", code); + log.info(": jsonElement={}", element); + } - @Data - private class Code { + @Data + private class Code { - private int code; + private int code; - } + } } diff --git a/class/src/test/java/com/github/kuangcp/serialize/json/ReadJsonTest.java b/class/src/test/java/com/github/kuangcp/serialize/json/ReadJsonTest.java index 5b7a31c5..cef0e6d8 100644 --- a/class/src/test/java/com/github/kuangcp/serialize/json/ReadJsonTest.java +++ b/class/src/test/java/com/github/kuangcp/serialize/json/ReadJsonTest.java @@ -4,8 +4,10 @@ import com.github.kuangcp.serialize.json.speed.FastJsonTool; import com.github.kuangcp.serialize.json.speed.GsonTool; import com.github.kuangcp.serialize.json.speed.JacksonTool; + import java.io.IOException; import java.util.concurrent.TimeUnit; + import org.junit.Test; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; @@ -30,28 +32,28 @@ @OutputTimeUnit(TimeUnit.MILLISECONDS) public class ReadJsonTest { - private static final String json = "{\"name\":\"one\",\"address\":\"any province\",\"phone\":null}"; - - @Benchmark - public void testGsonRead() throws IOException { - new GsonTool().fromJSON(json, Person.class); - } - - @Benchmark - public void testJacksonRead() throws IOException { - new JacksonTool().fromJSON(json, Person.class); - } - - @Benchmark - public void testFastJsonRead() throws IOException { - new FastJsonTool().fromJSON(json, Person.class); - } - - @Test - public void testCompareRead() throws Exception { - Options options = new OptionsBuilder() - .include(ReadJsonTest.class.getSimpleName()) - .output("/tmp/" + ReadJsonTest.class.getSimpleName() + ".log").build(); - new Runner(options).run(); - } + private static final String json = "{\"name\":\"one\",\"address\":\"any province\",\"phone\":null}"; + + @Benchmark + public void testGsonRead() throws IOException { + new GsonTool().fromJSON(json, Person.class); + } + + @Benchmark + public void testJacksonRead() throws IOException { + new JacksonTool().fromJSON(json, Person.class); + } + + @Benchmark + public void testFastJsonRead() throws IOException { + new FastJsonTool().fromJSON(json, Person.class); + } + + @Test + public void testCompareRead() throws Exception { + Options options = new OptionsBuilder() + .include(ReadJsonTest.class.getSimpleName()) + .output("/tmp/" + ReadJsonTest.class.getSimpleName() + ".log").build(); + new Runner(options).run(); + } } \ No newline at end of file diff --git a/class/src/test/java/com/github/kuangcp/serialize/json/WriteJsonTest.java b/class/src/test/java/com/github/kuangcp/serialize/json/WriteJsonTest.java index 4cbf2695..e86b1c44 100644 --- a/class/src/test/java/com/github/kuangcp/serialize/json/WriteJsonTest.java +++ b/class/src/test/java/com/github/kuangcp/serialize/json/WriteJsonTest.java @@ -5,10 +5,6 @@ import com.github.kuangcp.serialize.json.speed.FastJsonTool; import com.github.kuangcp.serialize.json.speed.GsonTool; import com.github.kuangcp.serialize.json.speed.JacksonTool; -import java.util.List; -import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; -import java.util.stream.IntStream; import org.junit.Test; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; @@ -21,6 +17,11 @@ import org.openjdk.jmh.runner.options.Options; import org.openjdk.jmh.runner.options.OptionsBuilder; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + /** * @author Kuangcp on 2020-05-06 01:12 */ @@ -31,30 +32,30 @@ @OutputTimeUnit(TimeUnit.MILLISECONDS) public class WriteJsonTest { - private static final int DATA_SIZE = 30; - private static final List personList = IntStream.range(1, DATA_SIZE) - .mapToObj(i -> new Person("name" + i)).collect(Collectors.toList()); - - @Benchmark - public void jacksonWrite() throws JsonProcessingException { - new JacksonTool().toJSON(personList); - } - - @Benchmark - public void fastJsonWrite() throws JsonProcessingException { - new FastJsonTool().toJSON(personList); - } - - @Benchmark - public void gsonWrite() throws JsonProcessingException { - new GsonTool().toJSON(personList); - } - - @Test - public void testCompareRead() throws Exception { - Options options = new OptionsBuilder() - .include(WriteJsonTest.class.getSimpleName()) - .output("/tmp/" + WriteJsonTest.class.getSimpleName() + ".log").build(); - new Runner(options).run(); - } + private static final int DATA_SIZE = 30; + private static final List personList = IntStream.range(1, DATA_SIZE) + .mapToObj(i -> new Person("name" + i)).collect(Collectors.toList()); + + @Benchmark + public void jacksonWrite() throws JsonProcessingException { + new JacksonTool().toJSON(personList); + } + + @Benchmark + public void fastJsonWrite() throws JsonProcessingException { + new FastJsonTool().toJSON(personList); + } + + @Benchmark + public void gsonWrite() throws JsonProcessingException { + new GsonTool().toJSON(personList); + } + + @Test + public void testCompareRead() throws Exception { + Options options = new OptionsBuilder() + .include(WriteJsonTest.class.getSimpleName()) + .output("/tmp/" + WriteJsonTest.class.getSimpleName() + ".log").build(); + new Runner(options).run(); + } } From 46367479795f6e7ac7a9f3fe44e11f91028d0000 Mon Sep 17 00:00:00 2001 From: kuangcp Date: Thu, 5 Dec 2024 17:51:58 +0800 Subject: [PATCH 439/476] fj first test --- .../java/thread/pool/ForkJoinUsePool.java | 15 ++ .../thread/pool/ForkJoinUsePoolLIFOTest.java | 177 ++++++++++++++++++ .../kuangcp/validation/BeanValidator.java | 141 +++++++------- 3 files changed, 263 insertions(+), 70 deletions(-) create mode 100644 concurrency/src/main/java/thread/pool/ForkJoinUsePool.java create mode 100644 concurrency/src/test/java/thread/pool/ForkJoinUsePoolLIFOTest.java diff --git a/concurrency/src/main/java/thread/pool/ForkJoinUsePool.java b/concurrency/src/main/java/thread/pool/ForkJoinUsePool.java new file mode 100644 index 00000000..680eb329 --- /dev/null +++ b/concurrency/src/main/java/thread/pool/ForkJoinUsePool.java @@ -0,0 +1,15 @@ +package thread.pool; + +import java.util.concurrent.ForkJoinPool; + +/** + * 探究Fork/Join 线程池使用场景 + * + * @author Kuangcp + * 2024-11-27 11:00 + * @see ForkJoinPool#makeCommonPool() + */ +public class ForkJoinUsePool { + + +} diff --git a/concurrency/src/test/java/thread/pool/ForkJoinUsePoolLIFOTest.java b/concurrency/src/test/java/thread/pool/ForkJoinUsePoolLIFOTest.java new file mode 100644 index 00000000..d8793f15 --- /dev/null +++ b/concurrency/src/test/java/thread/pool/ForkJoinUsePoolLIFOTest.java @@ -0,0 +1,177 @@ +package thread.pool; + +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; + +import java.util.concurrent.Executors; +import java.util.concurrent.ForkJoinPool; +import java.util.concurrent.ForkJoinTask; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +/** + * 测试 栈的默认 LIFO 策略 + * + * @author Kuangcp + * 2024-12-05 13:43 + */ +@Slf4j +public class ForkJoinUsePoolLIFOTest { + + // TODO 栈出现长阻塞时的响应情况 + + + // [Fork join并发框架与工作窃取算法剖析-腾讯云开发者社区-腾讯云](https://cloud.tencent.com/developer/article/1512982) + + /** + * taskset -c 0,1,2 shell(复制自IDE第一行,需要IDE编译成class才会生效新改动) + * + * @see ForkJoinPool#externalPush(ForkJoinTask) 为什么任务只堆积在了一个队列上,因为实现中的随机数是取的提交线程,因此所有任务都堆在一个队列上了 但是为什么发生了工作窃取,任务执行顺序还是没变 + */ + @Test + public void testLIFO() throws Exception { + ForkJoinPool pool = ForkJoinPool.commonPool(); + + for (int i = 0; i < 10000; i++) { + int finalI = i; + pool.submit(() -> { + try { + TimeUnit.SECONDS.sleep(2); + log.info("i={}", finalI); + } catch (Exception e) { + log.error("", e); + } + }); + } + + ScheduledExecutorService sche = Executors.newScheduledThreadPool(1); + sche.scheduleAtFixedRate(() -> { + int parallelism = pool.getParallelism(); + long queuedTaskCount = pool.getQueuedSubmissionCount(); + log.info("con={} wait={}", parallelism, queuedTaskCount); + }, 1, 1, TimeUnit.SECONDS); + + Thread.currentThread().join(); + } + + /** + * 加了第二个线程提交任务后,类似LIFO的效果出来了 第二批优先第一批消费 + */ + @Test + public void testLIFO2() throws Exception { + ForkJoinPool pool = ForkJoinPool.commonPool(); + + for (int i = 0; i < 10000; i++) { + int finalI = i; + pool.submit(() -> { + try { + TimeUnit.SECONDS.sleep(2); + log.info("first={}", finalI); + } catch (Exception e) { + log.error("", e); + } + }); + } + + TimeUnit.SECONDS.sleep(3); + new Thread(() -> { + for (int i = 0; i < 1000; i++) { + int finalI = i; + pool.submit(() -> { + try { + TimeUnit.SECONDS.sleep(2); + log.info("second={}", finalI); + } catch (Exception e) { + log.error("", e); + } + }); + } + }).start(); + + ScheduledExecutorService sche = Executors.newScheduledThreadPool(1); + sche.scheduleAtFixedRate(() -> { + int parallelism = pool.getParallelism(); + long queuedTaskCount = pool.getQueuedSubmissionCount(); + log.info("con={} wait={}", parallelism, queuedTaskCount); + }, 1, 1, TimeUnit.SECONDS); + + Thread.currentThread().join(); + } + + /** + * 加了更多线程提交任务后,类似LIFO的效果更明显了 + */ + @Test + public void testLIFO3() throws Exception { + ForkJoinPool pool = ForkJoinPool.commonPool(); + + for (int i = 0; i < 10000; i++) { + int finalI = i; + pool.submit(() -> { + try { + TimeUnit.SECONDS.sleep(2); + log.info("first={}", finalI); + } catch (Exception e) { + log.error("", e); + } + }); + } + + TimeUnit.SECONDS.sleep(3); + for (int i = 0; i < 1000; i++) { + int finalI = i; + new Thread(() -> pool.submit(() -> { + try { + TimeUnit.SECONDS.sleep(2); + log.info("{}={}", finalI, finalI); + } catch (Exception e) { + log.error("", e); + } + })).start(); + } + + ScheduledExecutorService sche = Executors.newScheduledThreadPool(1); + sche.scheduleAtFixedRate(() -> { + int parallelism = pool.getParallelism(); + long queuedTaskCount = pool.getQueuedSubmissionCount(); + log.info("con={} wait={}", parallelism, queuedTaskCount); + }, 1, 1, TimeUnit.SECONDS); + + Thread.currentThread().join(); + } + + /** + * 全部新线程提交任务后,总体规律是有序的,局部是无序的,没有LIFO特性 + */ + @Test + public void testLIFO4() throws Exception { + ForkJoinPool pool = ForkJoinPool.commonPool(); + + for (int i = 0; i < 1000; i++) { + int finalI = i; + new Thread(() -> pool.submit(() -> { + try { + TimeUnit.SECONDS.sleep(2); + log.info("{}", finalI); + } catch (Exception e) { + log.error("", e); + } + })).start(); + } + + ScheduledExecutorService sche = Executors.newScheduledThreadPool(1); + sche.scheduleAtFixedRate(() -> { + int parallelism = pool.getParallelism(); + long queuedTaskCount = pool.getQueuedSubmissionCount(); + log.info("con={} wait={}", parallelism, queuedTaskCount); + }, 1, 1, TimeUnit.SECONDS); + + Thread.currentThread().join(); + } + + + @Test + public void testSSSS() throws Exception { + System.out.println("xxxxx"); + } +} diff --git a/web/src/main/java/com/github/kuangcp/validation/BeanValidator.java b/web/src/main/java/com/github/kuangcp/validation/BeanValidator.java index c9fd58f5..6fa9d9a6 100644 --- a/web/src/main/java/com/github/kuangcp/validation/BeanValidator.java +++ b/web/src/main/java/com/github/kuangcp/validation/BeanValidator.java @@ -1,5 +1,11 @@ package com.github.kuangcp.validation; +import lombok.extern.slf4j.Slf4j; + +import javax.validation.ConstraintViolation; +import javax.validation.Validation; +import javax.validation.Validator; +import javax.validation.ValidatorFactory; import java.util.Arrays; import java.util.Collection; import java.util.Collections; @@ -8,11 +14,6 @@ import java.util.Map; import java.util.Objects; import java.util.Set; -import javax.validation.ConstraintViolation; -import javax.validation.Validation; -import javax.validation.Validator; -import javax.validation.ValidatorFactory; -import lombok.extern.slf4j.Slf4j; /** * 基于注解的校验, @NotBlank @NotEmpty.... @@ -21,76 +22,76 @@ @Slf4j public abstract class BeanValidator { - private static final ValidatorFactory validatorFactory = Validation - .buildDefaultValidatorFactory(); + private static final ValidatorFactory validatorFactory = Validation + .buildDefaultValidatorFactory(); - /** - * 校验多个字段 - * - * @param t 校验的对象 - * @param groups Class, 必须要传,如果没有就传入一个new Class[0] - * @param 校验的对象泛型 - * @return key: 字段,value:错误信息 - */ - public static Map validate(T t, Class... groups) { - Validator validator = validatorFactory.getValidator(); - Set> violationSet = validator.validate(t, groups); - if (violationSet.isEmpty()) { - return Collections.emptyMap(); - } else { - Map errors = new HashMap<>(); - for (ConstraintViolation violation : violationSet) { - errors.put(violation.getPropertyPath().toString(), violation.getMessage()); - } - return errors; + /** + * 校验多个字段 + * + * @param t 校验的对象 + * @param groups Class, 必须要传,如果没有就传入一个new Class[0] + * @param 校验的对象泛型 + * @return key: 字段,value:错误信息 + */ + public static Map validate(T t, Class... groups) { + Validator validator = validatorFactory.getValidator(); + Set> violationSet = validator.validate(t, groups); + if (violationSet.isEmpty()) { + return Collections.emptyMap(); + } else { + Map errors = new HashMap<>(); + for (ConstraintViolation violation : violationSet) { + errors.put(violation.getPropertyPath().toString(), violation.getMessage()); + } + return errors; + } } - } - /** - * 校验多个对象 - * - * @param collection 集合 - * @return key: 字段,value:错误信息 - */ - public static Map validateList(Collection collection) { - Iterator iterator = collection.iterator(); - Map errors; - do { - if (!iterator.hasNext()) { - return Collections.emptyMap(); - } - Object object = iterator.next(); - // new Class[0] 必须要传, 否则在determineGroupValidationOrder方法中,会抛出异常 - errors = validate(object, new Class[0]); - } while (errors.isEmpty()); - return errors; - } + /** + * 校验多个对象 + * + * @param collection 集合 + * @return key: 字段,value:错误信息 + */ + public static Map validateList(Collection collection) { + Iterator iterator = collection.iterator(); + Map errors; + do { + if (!iterator.hasNext()) { + return Collections.emptyMap(); + } + Object object = iterator.next(); + // new Class[0] 必须要传, 否则在determineGroupValidationOrder方法中,会抛出异常 + errors = validate(object, new Class[0]); + } while (errors.isEmpty()); + return errors; + } - /** - * 综合validateList 和 validate 方法, 任何校验只需要使用这个方法就可以 - * - * @param first 第一个对象 - * @param objects 对象数组 - * @return key: 字段,value:错误信息 - */ - public static Map validateObject(Object first, Object... objects) { - if (objects != null && objects.length > 0) { - return validateList(Arrays.asList(first, objects)); - } else { - // new Class[0] 必须要传, 否则在determineGroupValidationOrder方法中,会抛出异常 - return validate(first, new Class[0]); + /** + * 综合validateList 和 validate 方法, 任何校验只需要使用这个方法就可以 + * + * @param first 第一个对象 + * @param objects 对象数组 + * @return key: 字段,value:错误信息 + */ + public static Map validateObject(Object first, Object... objects) { + if (objects != null && objects.length > 0) { + return validateList(Arrays.asList(first, objects)); + } else { + // new Class[0] 必须要传, 否则在determineGroupValidationOrder方法中,会抛出异常 + return validate(first, new Class[0]); + } } - } - /** - * 再次封装校验,对于异常直接抛出 - * - * @param param 参数 - */ - public static void check(Object param) throws IllegalArgumentException { - Map validateMap = BeanValidator.validateObject(param); - if (Objects.nonNull(validateMap) && !validateMap.isEmpty()) { - throw new RuntimeException(validateMap.values().iterator().next()); + /** + * 再次封装校验,对于异常直接抛出 + * + * @param param 参数 + */ + public static void check(Object param) throws IllegalArgumentException { + Map validateMap = BeanValidator.validateObject(param); + if (Objects.nonNull(validateMap) && !validateMap.isEmpty()) { + throw new RuntimeException(validateMap.values().iterator().next()); + } } - } } From 1661edbe3dca8956a74f4c49e34a5abd605d7aa4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 5 Dec 2024 18:24:21 +0000 Subject: [PATCH 440/476] Bump mysql:mysql-connector-java from 8.0.15 to 8.0.28 Bumps [mysql:mysql-connector-java](https://github.com/mysql/mysql-connector-j) from 8.0.15 to 8.0.28. - [Changelog](https://github.com/mysql/mysql-connector-j/blob/release/9.x/CHANGES) - [Commits](https://github.com/mysql/mysql-connector-j/compare/8.0.15...8.0.28) --- updated-dependencies: - dependency-name: mysql:mysql-connector-java dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c9664645..89b5dce9 100644 --- a/pom.xml +++ b/pom.xml @@ -206,7 +206,7 @@ mysql mysql-connector-java - 8.0.15 + 8.0.28 com.google.code.gson From 64512e0751f01ea182d63780cf84da7508e894b1 Mon Sep 17 00:00:00 2001 From: kuangcp Date: Mon, 9 Dec 2024 13:41:28 +0800 Subject: [PATCH 441/476] mock request --- .../thread/pool/ForkJoinUsePoolLIFOTest.java | 68 +++++++++++++++++-- 1 file changed, 64 insertions(+), 4 deletions(-) diff --git a/concurrency/src/test/java/thread/pool/ForkJoinUsePoolLIFOTest.java b/concurrency/src/test/java/thread/pool/ForkJoinUsePoolLIFOTest.java index d8793f15..5314b81e 100644 --- a/concurrency/src/test/java/thread/pool/ForkJoinUsePoolLIFOTest.java +++ b/concurrency/src/test/java/thread/pool/ForkJoinUsePoolLIFOTest.java @@ -7,10 +7,14 @@ import java.util.concurrent.ForkJoinPool; import java.util.concurrent.ForkJoinTask; import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; +import java.util.stream.IntStream; /** * 测试 栈的默认 LIFO 策略 + * 初步测试,没有LIFO特性,普通排队等待,队列中大量任务等待时,后续的小任务都要等很久,偶现又很快 * * @author Kuangcp * 2024-12-05 13:43 @@ -163,15 +167,71 @@ public void testLIFO4() throws Exception { sche.scheduleAtFixedRate(() -> { int parallelism = pool.getParallelism(); long queuedTaskCount = pool.getQueuedSubmissionCount(); - log.info("con={} wait={}", parallelism, queuedTaskCount); +// log.info("con={} wait={}", parallelism, queuedTaskCount); }, 1, 1, TimeUnit.SECONDS); Thread.currentThread().join(); } - + /** + * 模拟使用并行流时,高并发小请求 伴随定时的大请求,小请求很快就响应,但是大请求积压时间越来越长,提交线程有时也会消费任务。 + *

+ * 应该能解释当系统打开页面,批量发起请求时,排队中的就会积压等待,但是为什么部分请求耗时不受影响。 + *

+ * https://blog.csdn.net/weixin_38308374/article/details/112735120 + * + * @see ForEachOps.ForEachTask#compute() rightSplit.trySplit()可以对数据源的数据进行拆分,将数据一分为二 如果剩余的数据量不足以进行再次拆分,则直接使用当前线程处理 + */ @Test - public void testSSSS() throws Exception { - System.out.println("xxxxx"); + public void testLIFO5() throws Exception { + ScheduledExecutorService sche = Executors.newScheduledThreadPool(4); + sche.scheduleAtFixedRate(() -> { + int parallelism = ForkJoinPool.commonPool().getParallelism(); + long queuedTaskCount = ForkJoinPool.commonPool().getQueuedSubmissionCount(); + log.info("con={} wait={}", parallelism, queuedTaskCount); + }, 1, 1, TimeUnit.SECONDS); + + // 模拟线程池提交重请求 + sche.scheduleAtFixedRate(() -> { + long start = System.currentTimeMillis(); + + IntStream.range(1, 10).parallel().mapToObj(v -> { + try { + TimeUnit.MILLISECONDS.sleep(2000); + } catch (Exception e) { + log.error("", e); + } + long val = System.currentTimeMillis(); + long all = val - start; + if (all > 110) { + log.error("X long wait {}", all); + } + return val; + }).collect(Collectors.toList()); + }, 5, 6, TimeUnit.SECONDS); + + // 模拟小请求 + for (int i = 0; i < 1000; i++) { + TimeUnit.MILLISECONDS.sleep(300); + new Thread(() -> { + long start = System.currentTimeMillis(); + IntStream.range(1, 10).parallel().mapToObj(v -> { + try { + int xr = ThreadLocalRandom.current().nextInt(100); + TimeUnit.MILLISECONDS.sleep(200 + xr); + } catch (Exception e) { + log.error("", e); + } + long val = System.currentTimeMillis(); + long all = val - start; + if (all > 110) { + log.error("long wait {}", all); + } + return val; + }).collect(Collectors.toList()); + }).start(); + } + + Thread.currentThread().join(); } } From be2eca8143f2dc55e28d652b4b0f696f12e7a693 Mon Sep 17 00:00:00 2001 From: kuangcp Date: Tue, 10 Dec 2024 22:33:05 +0800 Subject: [PATCH 442/476] fix error --- class/src/main/java/jvm/oom/DirectMemoryOOM.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/class/src/main/java/jvm/oom/DirectMemoryOOM.java b/class/src/main/java/jvm/oom/DirectMemoryOOM.java index 6d18749b..6f799ca7 100644 --- a/class/src/main/java/jvm/oom/DirectMemoryOOM.java +++ b/class/src/main/java/jvm/oom/DirectMemoryOOM.java @@ -24,7 +24,7 @@ public class DirectMemoryOOM { private static final int mib = 1024 * 1024; - // 注意: -XX:MaxDirectMemorySize参数只对由DirectByteBuffer分配的内存有效,对Unsafe直接分配的内存无效 + // 注意: -XX:MaxDirectMemorySize参数只对由DirectByteBuffer分配的内存有效限制,对Unsafe直接分配的内存无效 /** * C语言malloc申请的也是虚拟内存,没有设置值的话操作系统不会分配物理内存 @@ -42,9 +42,10 @@ static void byUnsafe() throws IllegalAccessException, InterruptedException { while (true) { Thread.sleep(100); delta += 2; - System.out.println("now " + delta); + // byte unsafe.allocateMemory(2 * mib); + System.out.println("now " + delta); } } @@ -54,9 +55,9 @@ static void byBuffer() throws InterruptedException { while (true) { Thread.sleep(100); delta += 2; - System.out.println("now " + delta); // byte ByteBuffer buf = ByteBuffer.allocateDirect(2 * mib); + System.out.println("now " + delta); buf.putLong(2L); buf.flip(); buffers.add(buf); From ddce76438f4439fadc2626add947bce71c43224a Mon Sep 17 00:00:00 2001 From: kuangcp Date: Thu, 12 Dec 2024 11:48:33 +0800 Subject: [PATCH 443/476] gc --- class/src/test/java/jvm/gc/MxBeanCallbackTest.java | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/class/src/test/java/jvm/gc/MxBeanCallbackTest.java b/class/src/test/java/jvm/gc/MxBeanCallbackTest.java index 2c356765..d70f0910 100644 --- a/class/src/test/java/jvm/gc/MxBeanCallbackTest.java +++ b/class/src/test/java/jvm/gc/MxBeanCallbackTest.java @@ -1,25 +1,29 @@ package jvm.gc; +import lombok.extern.slf4j.Slf4j; import org.junit.Test; import java.lang.management.ManagementFactory; +import java.lang.management.MemoryMXBean; import java.util.concurrent.TimeUnit; /** * @author Kuangcp on 2024-03-30 10:50 */ +@Slf4j public class MxBeanCallbackTest { + /** + * jcmd pid GC.run 触发GC,可以看到对应输出 + */ @Test public void testInstallGCMonitoring() throws Exception { - MxBeanCallback.installGCMonitoring(); + log.info("finish install"); - ManagementFactory.getMemoryMXBean(); - + MemoryMXBean bean = ManagementFactory.getMemoryMXBean(); + log.info("bean={}", bean); - System.out.println("xx"); TimeUnit.MINUTES.sleep(3); - } } From 4b7db6ef4bc3d669c3ffd21c7306a61f0786a3e2 Mon Sep 17 00:00:00 2001 From: kuangcp Date: Fri, 7 Feb 2025 11:31:43 +0800 Subject: [PATCH 444/476] sse test --- .../thread/schdule/SchedulerPoolTest.java | 17 ++ network/pom.xml | 14 ++ .../com/github/kuangcp/http/sse/ReadTest.java | 176 ++++++++++++++++++ pom.xml | 19 +- 4 files changed, 225 insertions(+), 1 deletion(-) create mode 100644 network/src/test/java/com/github/kuangcp/http/sse/ReadTest.java diff --git a/concurrency/src/test/java/thread/schdule/SchedulerPoolTest.java b/concurrency/src/test/java/thread/schdule/SchedulerPoolTest.java index 7cfe7d05..4ab50ea6 100644 --- a/concurrency/src/test/java/thread/schdule/SchedulerPoolTest.java +++ b/concurrency/src/test/java/thread/schdule/SchedulerPoolTest.java @@ -232,6 +232,23 @@ public void testSchedulerGetResult() throws Exception { Thread.currentThread().join(10000); } + /** + * scheduleWithFixedDelay 和 scheduleAtFixedRate 如果任务抛出了未处理的异常,会中断后续的调度计划 + */ + @Test + public void testBrokeFixedTask() throws Exception { + AtomicInteger cnt = new AtomicInteger(); + customScheduler.scheduleWithFixedDelay(() -> { + int index = cnt.incrementAndGet(); + log.info("index={}", index); + if (index > 4) { + throw new RuntimeException("Broke"); + } + + }, 2, 1, TimeUnit.SECONDS); + Thread.sleep(1000000); + } + @Test public void testScheduleDrop() throws Exception { for (int i = 0; i < 10; i++) { diff --git a/network/pom.xml b/network/pom.xml index 6d35dfea..2ab420b1 100644 --- a/network/pom.xml +++ b/network/pom.xml @@ -28,10 +28,24 @@ + com.squareup.okhttp3 okhttp + + com.squareup.okhttp3 + okhttp-sse + + + com.squareup.okhttp3 + logging-interceptor + + + com.squareup.okhttp3 + mockwebserver + + org.apache.commons commons-lang3 diff --git a/network/src/test/java/com/github/kuangcp/http/sse/ReadTest.java b/network/src/test/java/com/github/kuangcp/http/sse/ReadTest.java new file mode 100644 index 00000000..2615638b --- /dev/null +++ b/network/src/test/java/com/github/kuangcp/http/sse/ReadTest.java @@ -0,0 +1,176 @@ +package com.github.kuangcp.http.sse; + +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.extern.slf4j.Slf4j; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; +import okhttp3.mockwebserver.MockResponse; +import okhttp3.mockwebserver.MockWebServer; +import okhttp3.sse.EventSource; +import okhttp3.sse.EventSourceListener; +import okhttp3.sse.EventSources; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.net.SocketTimeoutException; +import java.util.concurrent.TimeUnit; + +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +/** + * @author Kuangcp + * 2025-02-07 10:21 + */ +@Slf4j +public class ReadTest { + private MockWebServer server; + private OkHttpClient client; + + ObjectMapper mapper = new ObjectMapper(); + + @Before + public void setUp() { + server = new MockWebServer(); + try { + server.start(); + } catch (IOException e) { + throw new RuntimeException(e); + } + + // 设置 OkHttpClient 超时参数 + client = new OkHttpClient.Builder() + .connectTimeout(5, TimeUnit.SECONDS) // 连接超时5秒 + .writeTimeout(5, TimeUnit.SECONDS) // 写入超时5秒 + .readTimeout(5, TimeUnit.SECONDS) // 读取超时5秒 + .build(); + } + + @After + public void tearDown() throws IOException { + server.shutdown(); + } + + @Test + public void testConnectTimeout() throws IOException { + // 模拟服务器延迟响应 + server.enqueue(new MockResponse().setBodyDelay(6, TimeUnit.SECONDS)); + + Request request = new Request.Builder() + .url(server.url("/")) + .build(); + + try { + client.newCall(request).execute(); + fail("Expected a SocketTimeoutException to be thrown for connect timeout"); + } catch (SocketTimeoutException e) { + // 连接超时,测试通过 + assertTrue(true); + } + } + + @Test + public void testWriteTimeout() throws IOException { + // 这个测试依赖于服务器的配置,确保服务器不会立即发送响应头 + server.enqueue(new MockResponse().setBodyDelay(6, TimeUnit.SECONDS)); + + Request request = new Request.Builder() + .url(server.url("/")) + .post(RequestBody.create(null, "This is the request body")) + .build(); + + try { + client.newCall(request).execute(); + fail("Expected a SocketTimeoutException to be thrown for write timeout"); + } catch (SocketTimeoutException e) { + // 写入超时,测试通过 + assertTrue(true); + } + } + + @Test + public void testReadTimeout() throws IOException { + // 模拟服务器立即发送响应头,但延迟发送响应体 + server.enqueue(new MockResponse() + .setBody("Response body") +// .setHeadersDelay(10,TimeUnit.SECONDS) + .setBodyDelay(10, TimeUnit.SECONDS)); + + Request request = new Request.Builder() + .url(server.url("/")) + .build(); + try { + Response response = client.newCall(request).execute(); + String responseBody = response.body().string(); +// System.out.println(responseBody); + // 读取超时,测试通过 + fail("Expected a SocketTimeoutException to be thrown for read timeout"); + } catch (SocketTimeoutException e) { + // 写入超时,测试通过 + assertTrue(true); + } + } + + @Test + public void testReadTimeoutWithEventSource() throws IOException, InterruptedException { + // 模拟服务器立即发送响应头,但延迟发送响应体 + server.enqueue(new MockResponse() + .setBody(": keep-alive") // SSE需要发送特定的响应头 + .addHeader("Content-Type", "text/event-stream") // 设置正确的MIME类型 + .setBodyDelay(5, TimeUnit.SECONDS) + ); // 延迟发送事件 + + Request request = new Request.Builder() + .url(server.url("/")) + .build(); + + EventSource.Factory factory = EventSources.createFactory(client); + + EventSourceListener eventSourceListener = new EventSourceListener() { + @Override + public void onClosed(@NotNull EventSource eventSource) { + log.info("closed {}", eventSource); + super.onClosed(eventSource); + } + + @Override + public void onEvent(@NotNull EventSource eventSource, @Nullable String id, @Nullable String type, @NotNull String data) { + log.info("event"); + super.onEvent(eventSource, id, type, data); + } + + @Override + public void onFailure(@NotNull EventSource eventSource, @Nullable Throwable t, @Nullable Response response) { + assert t != null; + log.error("返回异常信息:{}", t.getMessage()); + + try { + log.error("response:{}", mapper.writeValueAsString(response)); + } catch (Exception e) { + log.error("", e); + } +// super.onFailure(eventSource, t, response); + } + + @Override + public void onOpen(@NotNull EventSource eventSource, @NotNull Response response) { + log.info("open {}", response); + super.onOpen(eventSource, response); + } + }; + + log.info("request"); + final EventSource eventSource = factory.newEventSource(request, eventSourceListener); + + // 等待足够的时间以触发超时 + Thread.sleep(15000); // 等待时间应大于服务器延迟时间 + + // 测试结束,关闭EventSource + } +} diff --git a/pom.xml b/pom.xml index c9664645..7e0e9613 100644 --- a/pom.xml +++ b/pom.xml @@ -173,11 +173,28 @@ 6.14.3 test + com.squareup.okhttp3 okhttp - 4.11.0 + 4.12.0 + + + com.squareup.okhttp3 + okhttp-sse + 4.12.0 + + com.squareup.okhttp3 + logging-interceptor + 4.12.0 + + + com.squareup.okhttp3 + mockwebserver + 4.12.0 + + io.netty netty-all From 569116180f87f509acd2dddb44e348aeee6eaf61 Mon Sep 17 00:00:00 2001 From: kuangcp Date: Tue, 4 Mar 2025 18:46:06 +0800 Subject: [PATCH 445/476] aes --- class/src/main/java/security/aes/AESUtil.java | 56 +++++++++++++++---- .../test/java/security/aes/AESUtilTest.java | 41 +++++++++----- 2 files changed, 73 insertions(+), 24 deletions(-) diff --git a/class/src/main/java/security/aes/AESUtil.java b/class/src/main/java/security/aes/AESUtil.java index 0a0db26f..527e6be2 100644 --- a/class/src/main/java/security/aes/AESUtil.java +++ b/class/src/main/java/security/aes/AESUtil.java @@ -1,13 +1,6 @@ package security.aes; -import javax.crypto.BadPaddingException; -import javax.crypto.Cipher; -import javax.crypto.IllegalBlockSizeException; -import javax.crypto.KeyGenerator; -import javax.crypto.NoSuchPaddingException; -import javax.crypto.SealedObject; -import javax.crypto.SecretKey; -import javax.crypto.SecretKeyFactory; +import javax.crypto.*; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.PBEKeySpec; import javax.crypto.spec.SecretKeySpec; @@ -16,6 +9,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.Serializable; +import java.nio.charset.StandardCharsets; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; @@ -45,9 +39,9 @@ public static String decrypt(String algorithm, String cipherText, SecretKey key, } /** - * @see com.sun.crypto.provider.AESCrypt#isKeySizeValid 16 24 32 字节 + * @see com.sun.crypto.provider.AESCrypt#isKeySizeValid 16 24 32 字节 */ - public static SecretKey generateKey(int bit) throws NoSuchAlgorithmException { + public static SecretKey randomKey(int bit) throws NoSuchAlgorithmException { KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); keyGenerator.init(bit); return keyGenerator.generateKey(); @@ -162,4 +156,46 @@ public static String decryptPasswordBased(String cipherText, SecretKey key, IvPa return new String(cipher.doFinal(Base64.getDecoder().decode(cipherText))); } + public static String encryptPass(String text, String passwd, String salt, String iv) throws Exception { + return AESUtil.encryptPasswordBased(text, AESUtil.getKeyFromPassword(passwd, salt), AESUtil.generateIv(iv)); + } + + public static String decryptPass(String text, String passwd, String salt, String iv) throws Exception { + return AESUtil.decryptPasswordBased(text, AESUtil.getKeyFromPassword(passwd, salt), AESUtil.generateIv(iv)); + } + + + // 密钥长度(AES支持128、192、256位) + private static final int KEY_SIZE = 256; + + // 固定IV 0值 + private static final byte[] FIXED_IV = new byte[16]; // AES块大小为16字节 + + // 将字符串密钥转换为SecretKeySpec + + /** + * @param key 字符串长度对应 KEY_SIZE 长度/8 + */ + public static SecretKeySpec generateKey(String key) throws Exception { + byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8); + byte[] keyArray = new byte[KEY_SIZE / 8]; + System.arraycopy(keyBytes, 0, keyArray, 0, Math.min(keyBytes.length, keyArray.length)); + return new SecretKeySpec(keyArray, "AES"); + } + + // 加密方法 + public static String encrypt(String plainText, SecretKeySpec key) throws Exception { + Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); + cipher.init(Cipher.ENCRYPT_MODE, key, new javax.crypto.spec.IvParameterSpec(FIXED_IV)); + byte[] encryptedBytes = cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8)); + return Base64.getEncoder().encodeToString(encryptedBytes); + } + + // 解密方法 + public static String decrypt(String encryptedText, SecretKeySpec key) throws Exception { + Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); + cipher.init(Cipher.DECRYPT_MODE, key, new javax.crypto.spec.IvParameterSpec(FIXED_IV)); + byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(encryptedText)); + return new String(decryptedBytes, StandardCharsets.UTF_8); + } } diff --git a/class/src/test/java/security/aes/AESUtilTest.java b/class/src/test/java/security/aes/AESUtilTest.java index 22324694..fe61cca0 100644 --- a/class/src/test/java/security/aes/AESUtilTest.java +++ b/class/src/test/java/security/aes/AESUtilTest.java @@ -31,7 +31,7 @@ public void testStringEncrypt() throws Exception { new SecureRandom().nextBytes(var2); new SecretKeySpec(var2, "AES"); - SecretKey key = AESUtil.generateKey(128); + SecretKey key = AESUtil.randomKey(128); final String base64 = AESUtil.generateIvByte(); // 固定iv密钥 @@ -49,10 +49,11 @@ public void testStringEncrypt() throws Exception { Assert.assertEquals(input, plainText); } + @Test public void testGivenFile_whenEncrypt_thenSuccess() throws Exception { // given - SecretKey key = AESUtil.generateKey(128); + SecretKey key = AESUtil.randomKey(128); String algorithm = "AES/CBC/PKCS5Padding"; IvParameterSpec ivParameterSpec = AESUtil.generateIv(); Path originPath = Paths.get("src/test/resources/origin.txt"); @@ -79,7 +80,7 @@ public void testGivenFile_whenEncrypt_thenSuccess() throws Exception { public void givenObject_whenEncrypt_thenSuccess() throws Exception { // given Student student = new Student("Baeldung", 20); - SecretKey key = AESUtil.generateKey(128); + SecretKey key = AESUtil.randomKey(128); IvParameterSpec ivParameterSpec = AESUtil.generateIv(); String algorithm = "AES/CBC/PKCS5Padding"; @@ -116,23 +117,35 @@ public void givenPassword_whenEncrypt_thenSuccess() throws Exception { @Test public void testConfigWay() throws Exception { - String plainText = "www.baeldung.com"; + String plainText = "" + System.currentTimeMillis(); String password = "baeldung"; String salt = "12345678"; - final String iv = AESUtil.generateIvByte(); - log.info("iv={}", iv); - final String result = encryptPass(plainText, password, salt, iv); - final String origin = decryptPass(result, password, salt, iv); +// final String iv = AESUtil.generateIvByte(); + String iv = "D+OER4Pzbc+CtaXeVKlTEQ=="; + log.info("iv [{}] salt [{}]", iv, salt); + // 即使固定了iv 初始化向量,在跨系统传输仍需要双方约定三个 变量比较麻烦 + final String result = AESUtil.encryptPass(plainText, password, salt, iv); + final String origin = AESUtil.decryptPass(result, password, salt, iv); + log.info("{} {}", result, origin); Assert.assertEquals(origin, plainText); - } - public String encryptPass(String text, String passwd, String salt, String iv) throws Exception { - return AESUtil.encryptPasswordBased(text, AESUtil.getKeyFromPassword(passwd, salt), AESUtil.generateIv(iv)); + @Test + public void testSimple() throws Exception { + try { + String key = "b423eb489f47dbe933f7e761946ec216"; + SecretKeySpec secretKey = AESUtil.generateKey(key); + + String plainText = "Hello, AES Encryption!"; + String encryptedText = AESUtil.encrypt(plainText, secretKey); + System.out.println("加密后的密文 (Base64): " + encryptedText); + + String decryptedText = AESUtil.decrypt(encryptedText, secretKey); + System.out.println("解密后的明文: " + decryptedText); + } catch (Exception e) { + log.error("", e); + } } - public String decryptPass(String text, String passwd, String salt, String iv) throws Exception { - return AESUtil.decryptPasswordBased(text, AESUtil.getKeyFromPassword(passwd, salt), AESUtil.generateIv(iv)); - } } From dbb5bfcdd3a7acc99b03303d1aa70c05071b8433 Mon Sep 17 00:00:00 2001 From: kuangcp Date: Tue, 4 Mar 2025 19:13:44 +0800 Subject: [PATCH 446/476] doc --- class/src/test/java/security/aes/AESUtilTest.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/class/src/test/java/security/aes/AESUtilTest.java b/class/src/test/java/security/aes/AESUtilTest.java index fe61cca0..ea58c342 100644 --- a/class/src/test/java/security/aes/AESUtilTest.java +++ b/class/src/test/java/security/aes/AESUtilTest.java @@ -131,6 +131,9 @@ public void testConfigWay() throws Exception { Assert.assertEquals(origin, plainText); } + /** + * 只保留一个密钥,iv使用0值,安全性降低,但是更方便 + */ @Test public void testSimple() throws Exception { try { From 809a6fd350b7366c2a14d5c1fac4d923c267b610 Mon Sep 17 00:00:00 2001 From: kuangcp Date: Wed, 12 Mar 2025 18:30:43 +0800 Subject: [PATCH 447/476] fmt --- .../java/syntax/tryblock/FinallyTest.java | 24 +++++++ .../syntax/tryblock/FinallyWithReturn.java | 65 ------------------ .../tryblock/FinallyWithReturnTest.java | 66 +++++++++++++++++++ class/src/test/java/syntax/tryblock/TWR.java | 34 ---------- .../syntax/tryblock/TryWithResourceTest.java | 34 ++++++++++ 5 files changed, 124 insertions(+), 99 deletions(-) create mode 100644 class/src/test/java/syntax/tryblock/FinallyTest.java delete mode 100644 class/src/test/java/syntax/tryblock/FinallyWithReturn.java create mode 100644 class/src/test/java/syntax/tryblock/FinallyWithReturnTest.java delete mode 100644 class/src/test/java/syntax/tryblock/TWR.java create mode 100644 class/src/test/java/syntax/tryblock/TryWithResourceTest.java diff --git a/class/src/test/java/syntax/tryblock/FinallyTest.java b/class/src/test/java/syntax/tryblock/FinallyTest.java new file mode 100644 index 00000000..a1a9c107 --- /dev/null +++ b/class/src/test/java/syntax/tryblock/FinallyTest.java @@ -0,0 +1,24 @@ +package syntax.tryblock; + +import org.junit.Test; + +/** + * @author Kuangcp + * 2025-03-12 18:22 + */ +public class FinallyTest { + + + /** + * 将 输出 try 和 finally + */ + @Test + public void testExceptionFinally() throws Exception { + try { + System.out.println("try"); + throw new RuntimeException("error"); + } finally { + System.out.println("finally"); + } + } +} diff --git a/class/src/test/java/syntax/tryblock/FinallyWithReturn.java b/class/src/test/java/syntax/tryblock/FinallyWithReturn.java deleted file mode 100644 index 2e42584f..00000000 --- a/class/src/test/java/syntax/tryblock/FinallyWithReturn.java +++ /dev/null @@ -1,65 +0,0 @@ -package syntax.tryblock; - -import java.io.IOException; -import java.util.concurrent.ThreadLocalRandom; -import lombok.extern.slf4j.Slf4j; -import org.junit.Test; - -/** - * created by https://gitee.com/gin9 - * - * finally block should not contain return, the ide warn you as same - * execution sequence: expression in return then cache in stack, execution finally, return(the return on finally cover the try) - * - * @author kuangcp on 2/17/19-9:24 AM - */ -@Slf4j -public class FinallyWithReturn { - - @Test - public void testFinallyWithReturn() { - int value = doSomethingWithCoverException(); - log.info("actual value: value={}", value); - } - - @Test - public void testFinallyWithoutReturn() { - int value = doSomething(); - log.info("actual value: value={}", value); - } - - // 1. finally block will override try block return value - // 2. may cover Exception, if not catch corresponding exception - private int doSomethingWithCoverException() { - try { - return alwaysZero(); - } finally { - log.info("finally block"); - // if use return, even the compile-time exception will ignored - // this return will cover the return on try block - return 1; - } - } - - // correct way - private int doSomething() { - try { - return alwaysZero(); - } catch (IOException e) { - log.warn("catch exception ", e); - } finally { - log.info("finally block"); - } - return 1; - } - - private int alwaysZero() throws IOException { - log.info("invoke alwaysZero method"); - if (ThreadLocalRandom.current().nextBoolean()) { - log.warn("throw Exception"); - throw new IOException("io exception"); - } - - return 0; - } -} diff --git a/class/src/test/java/syntax/tryblock/FinallyWithReturnTest.java b/class/src/test/java/syntax/tryblock/FinallyWithReturnTest.java new file mode 100644 index 00000000..3e1cafd3 --- /dev/null +++ b/class/src/test/java/syntax/tryblock/FinallyWithReturnTest.java @@ -0,0 +1,66 @@ +package syntax.tryblock; + +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; + +import java.io.IOException; +import java.util.concurrent.ThreadLocalRandom; + +/** + * created by https://gitee.com/gin9 + *

+ * finally block should not contain return, the ide warn you as same + * execution sequence: expression in return then cache in stack, execution finally, return(the return on finally cover the try) + * + * @author kuangcp on 2/17/19-9:24 AM + */ +@Slf4j +public class FinallyWithReturnTest { + + @Test + public void testFinallyWithReturn() { + int value = doSomethingWithCoverException(); + log.info("actual value: value={}", value); + } + + @Test + public void testFinallyWithoutReturn() { + int value = doSomething(); + log.info("actual value: value={}", value); + } + + // 1. finally block will override try block return value + // 2. may cover Exception, if not catch corresponding exception + private int doSomethingWithCoverException() { + try { + return alwaysZero(); + } finally { + log.info("finally block"); + // if use return, even the compile-time exception will ignored + // this return will cover the return on try block + return 1; + } + } + + // correct way + private int doSomething() { + try { + return alwaysZero(); + } catch (IOException e) { + log.warn("catch exception ", e); + } finally { + log.info("finally block"); + } + return 1; + } + + private int alwaysZero() throws IOException { + log.info("invoke alwaysZero method"); + if (ThreadLocalRandom.current().nextBoolean()) { + log.warn("throw Exception"); + throw new IOException("io exception"); + } + + return 0; + } +} diff --git a/class/src/test/java/syntax/tryblock/TWR.java b/class/src/test/java/syntax/tryblock/TWR.java deleted file mode 100644 index 9de8f9da..00000000 --- a/class/src/test/java/syntax/tryblock/TWR.java +++ /dev/null @@ -1,34 +0,0 @@ -package syntax.tryblock; - -import java.io.BufferedReader; -import java.io.FileReader; -import java.io.IOException; - -/** - * created by https://gitee.com/gin9 - * - * try-with-resources - * as long as the object implements the AutoCloseable and Closeable interface - * - * @author kuangcp on 18-10-1-下午9:59 - */ -public class TWR { - - // primitive way - static String ReadFile(String file) throws IOException { - BufferedReader br = new BufferedReader(new FileReader(file)); - try { - return br.readLine(); - } finally { - br.close(); - } - } - - // use TWR - static String ReadFileWithTWR(String file) throws IOException { - try (BufferedReader br = new BufferedReader(new FileReader(file))) { - return br.readLine(); - } - } - -} diff --git a/class/src/test/java/syntax/tryblock/TryWithResourceTest.java b/class/src/test/java/syntax/tryblock/TryWithResourceTest.java new file mode 100644 index 00000000..f4845f86 --- /dev/null +++ b/class/src/test/java/syntax/tryblock/TryWithResourceTest.java @@ -0,0 +1,34 @@ +package syntax.tryblock; + +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; + +/** + * created by https://gitee.com/gin9 + *

+ * try-with-resources + * as long as the object implements the AutoCloseable and Closeable interface + * + * @author kuangcp on 18-10-1-下午9:59 + */ +public class TryWithResourceTest { + + // primitive way + static String ReadFile(String file) throws IOException { + BufferedReader br = new BufferedReader(new FileReader(file)); + try { + return br.readLine(); + } finally { + br.close(); + } + } + + // use TWR + static String ReadFileWithTWR(String file) throws IOException { + try (BufferedReader br = new BufferedReader(new FileReader(file))) { + return br.readLine(); + } + } + +} From 2d321d561d81702134adfc21f6227d3118fa79fb Mon Sep 17 00:00:00 2001 From: kuangcp Date: Tue, 25 Mar 2025 13:45:23 +0800 Subject: [PATCH 448/476] Stream limit --- .../kuangcp/stream/CreateStreamTest.java | 156 ++++++++++-------- .../kuangcp/stream/bug/StreamGenericTest.java | 4 +- 2 files changed, 89 insertions(+), 71 deletions(-) diff --git a/java8/src/test/java/com/github/kuangcp/stream/CreateStreamTest.java b/java8/src/test/java/com/github/kuangcp/stream/CreateStreamTest.java index 9fc48de5..01cce65b 100644 --- a/java8/src/test/java/com/github/kuangcp/stream/CreateStreamTest.java +++ b/java8/src/test/java/com/github/kuangcp/stream/CreateStreamTest.java @@ -1,86 +1,104 @@ package com.github.kuangcp.stream; -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.Objects; +import java.util.Random; import java.util.stream.Collectors; import java.util.stream.Stream; import java.util.stream.StreamSupport; -import org.junit.Test; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; /** * @author Kuangcp on 2020-01-08 16:41 */ +@Slf4j public class CreateStreamTest { - @Test - public void testOfWithNull() { - List list = Stream.of(null, "test", "name").filter(Objects::nonNull) - .collect(Collectors.toList()); - System.out.println(list); - } - - /** - * 判断是否无限流, 理论上是无法实现的,但是这里可以估计 - */ - @Test - public void testJudgeFinite() { - assertFalse(isFinite(Stream.iterate(1, x -> x))); - assertTrue(isFinite(Stream.of(1))); - assertTrue(isFinite(Stream.of(1, 2, 3, 4).limit(1))); - } - - boolean isFinite(Stream stream) { - return !Objects.equals(stream.spliterator().estimateSize(), Long.MAX_VALUE); - } - - @Test - public void testZip() throws Exception { - - Integer[] result = new Integer[5]; - Stream zip = zip(Stream.of(1, 2, 4, 5).parallel(), Stream.of(2, 3,10).parallel()); - zip.collect(Collectors.toList()).toArray(result); - - List excepts = Arrays.asList(1, 2, 2, 3, 4, 10, 5); - Integer[] exceptArray = new Integer[5]; - excepts.toArray(exceptArray); - assertArrayEquals(result, exceptArray); - } - - Stream zip(Stream left, Stream right) { - Iterator leftIterator = left.iterator(); - Iterator rightIterator = right.iterator(); - - Iterator iterator = new Iterator() { - private boolean left = false; - - @Override - public boolean hasNext() { - return leftIterator.hasNext() || rightIterator.hasNext(); - } - - @Override - public T next() { - left = !left; - if (left && !leftIterator.hasNext()) { - return rightIterator.next(); - } - if (!left && !rightIterator.hasNext()) { - return leftIterator.next(); - } - return left - ? leftIterator.next() - : rightIterator.next(); - } - }; - - Iterable iterable = () -> iterator; - boolean parallel = left.isParallel() || right.isParallel(); - return StreamSupport.stream(iterable.spliterator(), parallel); - } + @Test + public void testOfWithNull() { + List list = Stream.of(null, "test", "name").filter(Objects::nonNull) + .collect(Collectors.toList()); + System.out.println(list); + } + + /** + * 判断是否无限流, 理论上是无法实现的,但是这里可以估计 + */ + @Test + public void testJudgeFinite() { + assertFalse(isFinite(Stream.iterate(1, x -> x))); + assertTrue(isFinite(Stream.of(1))); + assertTrue(isFinite(Stream.of(1, 2, 3, 4).limit(1))); + } + + @Test + public void testLimitBlocked() throws Exception { + // 从一个集合中随机选取一部分数据出来。 limit的目标值大于集合大小时,就会阻塞 + List ids = Arrays.asList("1", "2", "3"); + int size = ids.size(); + List pick = new Random() + .ints(0, size) + .distinct() + .limit(5) + .mapToObj(ids::get) + .collect(Collectors.toList()); + log.info("pick={}", pick); + } + + boolean isFinite(Stream stream) { + return !Objects.equals(stream.spliterator().estimateSize(), Long.MAX_VALUE); + } + + @Test + public void testZip() throws Exception { + + Integer[] result = new Integer[5]; + Stream zip = zip(Stream.of(1, 2, 4, 5).parallel(), Stream.of(2, 3, 10).parallel()); + zip.collect(Collectors.toList()).toArray(result); + + List excepts = Arrays.asList(1, 2, 2, 3, 4, 10, 5); + Integer[] exceptArray = new Integer[5]; + excepts.toArray(exceptArray); + assertArrayEquals(result, exceptArray); + } + + Stream zip(Stream left, Stream right) { + Iterator leftIterator = left.iterator(); + Iterator rightIterator = right.iterator(); + + Iterator iterator = new Iterator() { + private boolean left = false; + + @Override + public boolean hasNext() { + return leftIterator.hasNext() || rightIterator.hasNext(); + } + + @Override + public T next() { + left = !left; + if (left && !leftIterator.hasNext()) { + return rightIterator.next(); + } + if (!left && !rightIterator.hasNext()) { + return leftIterator.next(); + } + return left + ? leftIterator.next() + : rightIterator.next(); + } + }; + + Iterable iterable = () -> iterator; + boolean parallel = left.isParallel() || right.isParallel(); + return StreamSupport.stream(iterable.spliterator(), parallel); + } } diff --git a/java8/src/test/java/com/github/kuangcp/stream/bug/StreamGenericTest.java b/java8/src/test/java/com/github/kuangcp/stream/bug/StreamGenericTest.java index a815c19e..1959bd32 100644 --- a/java8/src/test/java/com/github/kuangcp/stream/bug/StreamGenericTest.java +++ b/java8/src/test/java/com/github/kuangcp/stream/bug/StreamGenericTest.java @@ -22,8 +22,8 @@ public void testList() throws Exception { .map(v -> (Integer) v) // 即使是硬编码转换类型 同样会只推断为Object类型 .map(v -> Integer.parseInt(v.toString())) - // 甚至换成任意对象值,都不符合Optional.map方法的签名(泛型推断),永远推断为Object - .map(v -> new HashMap<>()) + // 甚至换成任意对象值,都不符合Optional.map方法的签名(泛型推断),result 永远推断为Object +// .map(v -> new HashMap<>()) .orElse(null); // 只能在lambda表达式结束后做强转 From 32d82e8b95f597e5d0828cd45e31392b184330c2 Mon Sep 17 00:00:00 2001 From: kuangcp Date: Fri, 4 Apr 2025 14:30:49 +0800 Subject: [PATCH 449/476] limit --- .../github/kuangcp/stream/CreateStreamTest.java | 16 ++++++++++++++++ .../proxy/salary/jdkproxy/Interceptor.java | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/java8/src/test/java/com/github/kuangcp/stream/CreateStreamTest.java b/java8/src/test/java/com/github/kuangcp/stream/CreateStreamTest.java index 01cce65b..e9c68004 100644 --- a/java8/src/test/java/com/github/kuangcp/stream/CreateStreamTest.java +++ b/java8/src/test/java/com/github/kuangcp/stream/CreateStreamTest.java @@ -39,18 +39,34 @@ public void testJudgeFinite() { assertTrue(isFinite(Stream.of(1, 2, 3, 4).limit(1))); } + /** + * @see java.util.stream.SliceOps#makeInt(java.util.stream.AbstractPipeline.AbstractPipeline, long, long) + */ @Test public void testLimitBlocked() throws Exception { + // 从一个集合中随机选取一部分数据出来。 limit的目标值大于集合大小时,就会阻塞 List ids = Arrays.asList("1", "2", "3"); + + // 有限集合产生的流不会阻塞 +// List xl = ids.stream().limit(5).collect(Collectors.toList()); +// log.info("xl={}", xl); + + + Stream.iterate(0, n -> n + 2).distinct().limit(10).forEach(System.out::println); +// Random.RandomIntsSpliterator.tryAdvance + int size = ids.size(); List pick = new Random() .ints(0, size) +// .peek(v -> System.out.println(v)) .distinct() .limit(5) .mapToObj(ids::get) .collect(Collectors.toList()); log.info("pick={}", pick); + + } boolean isFinite(Stream stream) { diff --git a/spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/Interceptor.java b/spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/Interceptor.java index 0c01757b..6a2e922b 100644 --- a/spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/Interceptor.java +++ b/spring/src/test/java/com/github/kuangcp/proxy/salary/jdkproxy/Interceptor.java @@ -5,5 +5,5 @@ */ public interface Interceptor { - void interceptor(); + void interceptor(); } From b13414106618851e60f93cf0c7336e24ade5dc80 Mon Sep 17 00:00:00 2001 From: kuangcp Date: Thu, 10 Apr 2025 21:20:30 +0800 Subject: [PATCH 450/476] hex --- class/src/test/java/syntax/bit/HexTest.java | 24 +++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 class/src/test/java/syntax/bit/HexTest.java diff --git a/class/src/test/java/syntax/bit/HexTest.java b/class/src/test/java/syntax/bit/HexTest.java new file mode 100644 index 00000000..e72debb9 --- /dev/null +++ b/class/src/test/java/syntax/bit/HexTest.java @@ -0,0 +1,24 @@ +package syntax.bit; + +import org.junit.Test; + +import java.math.BigInteger; +import java.security.MessageDigest; + +/** + * @author Kuangcp + * 2025-04-10 16:14 + */ +public class HexTest { + + /** + * printf hi | md5sum + */ + @Test + public void testHex() throws Exception { + String txt = "hi"; + byte[] hash = MessageDigest.getInstance("MD5").digest(txt.getBytes()); + String checksum = new BigInteger(1, hash).toString(16); + System.out.println(checksum); + } +} From 9e30b5324fbcdd25ff9d1532d5a8d307980f1230 Mon Sep 17 00:00:00 2001 From: kuangcp Date: Tue, 15 Apr 2025 13:47:35 +0800 Subject: [PATCH 451/476] byte hex convert --- .../com/github/kuangcp/util/ShowBinary.java | 56 +++++++++++++++---- class/src/main/java/security/aes/AESUtil.java | 39 +++++++++++++ .../test/java/security/aes/AESUtilTest.java | 55 ++++++++++++++++++ 3 files changed, 138 insertions(+), 12 deletions(-) diff --git a/class/src/main/java/com/github/kuangcp/util/ShowBinary.java b/class/src/main/java/com/github/kuangcp/util/ShowBinary.java index 3f490a05..f4a1d170 100644 --- a/class/src/main/java/com/github/kuangcp/util/ShowBinary.java +++ b/class/src/main/java/com/github/kuangcp/util/ShowBinary.java @@ -1,23 +1,55 @@ package com.github.kuangcp.util; +import java.math.BigInteger; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + /** * @author kuangcp on 3/14/19-2:26 PM */ public class ShowBinary { - public static String toBinary(Byte value) { - return Integer.toBinaryString(value); - } + public static String toBinary(Byte value) { + return Integer.toBinaryString(value); + } + + public static String toBinary(Integer value) { + return Integer.toBinaryString(value); + } + + public static String toBinary(Double value) { + return Long.toBinaryString(Double.doubleToRawLongBits(value)); + } + + public static String toBinary(Float value) { + return Float.toHexString(value); + } - public static String toBinary(Integer value) { - return Integer.toBinaryString(value); - } + /** + * https://stackoverflow.com/questions/9655181/java-convert-a-byte-array-to-a-hex-string + * 风险:当转整数后前缀出现0,会丢失字节 + */ + public static String byteToHex2(byte[] value) { + return new BigInteger(1, value).toString(16); + } - public static String toBinary(Double value) { - return Long.toBinaryString(Double.doubleToRawLongBits(value)); - } + public static String byteToHex(byte[] value) { + return IntStream.range(0, value.length) + .mapToObj(i -> String.format("%02X", value[i])) + .collect(Collectors.joining()); + } - public static String toBinary(Float value) { - return Float.toHexString(value); - } + public static byte[] hexToByte(String hex) { + byte[] ans = new byte[hex.length() / 2]; + for (int i = 0; i < ans.length; i++) { + int index = i * 2; + int val = Integer.parseInt(hex.substring(index, index + 2), 16); + ans[i] = (byte) val; + } +// for (byte b : ans) { +// System.out.print(b + " "); +// } +// System.out.println(hex.length() + " " + ans.length); + return ans; + } } diff --git a/class/src/main/java/security/aes/AESUtil.java b/class/src/main/java/security/aes/AESUtil.java index 527e6be2..17194d53 100644 --- a/class/src/main/java/security/aes/AESUtil.java +++ b/class/src/main/java/security/aes/AESUtil.java @@ -1,5 +1,7 @@ package security.aes; +import com.github.kuangcp.util.ShowBinary; + import javax.crypto.*; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.PBEKeySpec; @@ -20,6 +22,43 @@ public class AESUtil { + + public static byte[] encrypt(String hexKey, byte[] data) throws Exception { + return encrypt(ShowBinary.hexToByte(hexKey), data); + } + + public static byte[] decrypt(String hexKey, byte[] data) throws Exception { + return decrypt(ShowBinary.hexToByte(hexKey), data); + } + + /** + * 加密 + * + * @param key 密钥 + * @param data 加密数据 + * @return 密文 + */ + public static byte[] encrypt(byte[] key, byte[] data) throws Exception { + SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES"); + Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); + cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec); + return cipher.doFinal(data); + } + + /** + * 解密 + * + * @param key 密钥 + * @param data 密文 + * @return 解密后的数据 + */ + public static byte[] decrypt(byte[] key, byte[] data) throws Exception { + SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES"); + Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); + cipher.init(Cipher.DECRYPT_MODE, secretKeySpec); + return cipher.doFinal(data); + } + public static String encrypt(String algorithm, String input, SecretKey key, IvParameterSpec iv) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException { diff --git a/class/src/test/java/security/aes/AESUtilTest.java b/class/src/test/java/security/aes/AESUtilTest.java index ea58c342..28d43fd7 100644 --- a/class/src/test/java/security/aes/AESUtilTest.java +++ b/class/src/test/java/security/aes/AESUtilTest.java @@ -1,10 +1,12 @@ package security.aes; +import com.github.kuangcp.util.ShowBinary; import lombok.extern.slf4j.Slf4j; import org.junit.Assert; import org.junit.Test; +import javax.crypto.KeyGenerator; import javax.crypto.SealedObject; import javax.crypto.SecretKey; import javax.crypto.spec.IvParameterSpec; @@ -14,6 +16,7 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.security.SecureRandom; +import java.util.Base64; import static org.hamcrest.MatcherAssert.assertThat; @@ -21,6 +24,58 @@ public class AESUtilTest { + @Test + public void testFlow() throws Exception { + // 生成随机密钥 + KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); + // 128 192 256 + keyGenerator.init(128, new SecureRandom()); + SecretKey secretKey = keyGenerator.generateKey(); + + byte[] key = secretKey.getEncoded(); + System.out.println("AES 密钥:" + Base64.getEncoder().encodeToString(key)); + System.out.println("AES 密钥:" + ShowBinary.byteToHex(key)); + + String content = "KeyGenerator keyGenerator = KeyGenerator.getInstance(\"AES\");"; + System.out.println("原文:" + content); + + byte[] ret = AESUtil.encrypt(key, content.getBytes()); + System.out.println("密文:" + Base64.getEncoder().encodeToString(ret)); + System.out.println("密文:" + ShowBinary.byteToHex(ret)); + + byte[] raw = AESUtil.decrypt(key, ret); + System.out.println("原文:" + new String(raw)); + Assert.assertEquals(content, new String(raw)); + } + + @Test + public void testHexKeyFlow() throws Exception { + // 生成随机密钥 + KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); + // 128 192 256 + keyGenerator.init(192, new SecureRandom()); + SecretKey secretKey = keyGenerator.generateKey(); + byte[] key = secretKey.getEncoded(); + +// System.out.println("Origin: " + key.length); + String hexKey = ShowBinary.byteToHex(key); + + System.out.println("AES 密钥:" + hexKey); + + String content = "KeyGenerator keyGenerator = KeyGenerator.getInstance(\"AES\");"; + System.out.println("原文:" + content); + + System.out.println("------"); + byte[] ret = AESUtil.encrypt(hexKey, content.getBytes()); +// System.out.println("密文:" + Base64.getEncoder().encodeToString(ret)); + System.out.println("密文:" + ShowBinary.byteToHex(ret)); + + byte[] raw = AESUtil.decrypt(hexKey, ret); + System.out.println("原文:" + new String(raw)); + Assert.assertEquals(content, new String(raw)); + } + + @Test public void testStringEncrypt() throws Exception { // given From 2453070ce57b30fac89ddbd3581c7da2b5ce1d65 Mon Sep 17 00:00:00 2001 From: kuangcp Date: Tue, 15 Apr 2025 21:33:41 +0800 Subject: [PATCH 452/476] repeat --- class/pom.xml | 5 +++++ .../src/test/java/security/aes/AESUtilTest.java | 2 ++ pom.xml | 7 +++++++ test/pom.xml | 11 +++++++++++ test/src/test/java/junit5/RepeatTest.java | 16 ++++++++++++++++ 5 files changed, 41 insertions(+) create mode 100644 test/src/test/java/junit5/RepeatTest.java diff --git a/class/pom.xml b/class/pom.xml index 30f4179e..6a9e1c5b 100644 --- a/class/pom.xml +++ b/class/pom.xml @@ -106,6 +106,11 @@ org.openjdk.jmh jmh-generator-annprocess + + org.junit.jupiter + junit-jupiter + test + diff --git a/class/src/test/java/security/aes/AESUtilTest.java b/class/src/test/java/security/aes/AESUtilTest.java index 28d43fd7..856ea8b5 100644 --- a/class/src/test/java/security/aes/AESUtilTest.java +++ b/class/src/test/java/security/aes/AESUtilTest.java @@ -5,6 +5,7 @@ import lombok.extern.slf4j.Slf4j; import org.junit.Assert; import org.junit.Test; +import org.junit.jupiter.api.RepeatedTest; import javax.crypto.KeyGenerator; import javax.crypto.SealedObject; @@ -49,6 +50,7 @@ public void testFlow() throws Exception { } @Test + @RepeatedTest(6) public void testHexKeyFlow() throws Exception { // 生成随机密钥 KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); diff --git a/pom.xml b/pom.xml index 6d0af306..ffe630ef 100644 --- a/pom.xml +++ b/pom.xml @@ -162,6 +162,13 @@ junit 4.13.2 + + org.junit + junit-bom + 5.12.2 + pom + import + org.mockito mockito-core diff --git a/test/pom.xml b/test/pom.xml index f85713d2..7b00227a 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -31,6 +31,17 @@ org.openjdk.jmh jmh-generator-annprocess + + org.testng + testng + test + + + + org.junit.jupiter + junit-jupiter + test + diff --git a/test/src/test/java/junit5/RepeatTest.java b/test/src/test/java/junit5/RepeatTest.java new file mode 100644 index 00000000..bc7d49d3 --- /dev/null +++ b/test/src/test/java/junit5/RepeatTest.java @@ -0,0 +1,16 @@ +package junit5; + + +import org.junit.jupiter.api.RepeatedTest; + +/** + * @author Kuangcp + * 2025-04-15 21:24 + */ +public class RepeatTest { + + @RepeatedTest(5) + public void testRepeat() throws Exception { + System.out.println("xxxxxx"); + } +} From 55ec10a80dd3c8837ad037bfee70152b93dd5e54 Mon Sep 17 00:00:00 2001 From: kuangcp Date: Tue, 6 May 2025 19:38:40 +0800 Subject: [PATCH 453/476] fmt --- .../com/github/kuangcp/serialize/Person.java | 2 + .../customserialize/MythSerialize.java | 127 +++++++++--------- .../kuangcp/serialize/SerializeTest.java | 29 ++-- .../customserialize/MythSerializeTest.java | 33 ++--- 4 files changed, 106 insertions(+), 85 deletions(-) diff --git a/class/src/main/java/com/github/kuangcp/serialize/Person.java b/class/src/main/java/com/github/kuangcp/serialize/Person.java index 774cf324..3ffa0a25 100644 --- a/class/src/main/java/com/github/kuangcp/serialize/Person.java +++ b/class/src/main/java/com/github/kuangcp/serialize/Person.java @@ -12,6 +12,8 @@ @Data public class Person implements Serializable { + private static final long serialVersionUID = 1L; + private String name; private String address; private String phone; diff --git a/class/src/main/java/com/github/kuangcp/serialize/customserialize/MythSerialize.java b/class/src/main/java/com/github/kuangcp/serialize/customserialize/MythSerialize.java index 5906cb4a..15202313 100644 --- a/class/src/main/java/com/github/kuangcp/serialize/customserialize/MythSerialize.java +++ b/class/src/main/java/com/github/kuangcp/serialize/customserialize/MythSerialize.java @@ -1,9 +1,15 @@ package com.github.kuangcp.serialize.customserialize; -import java.io.*; +import lombok.extern.slf4j.Slf4j; + +import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import lombok.extern.slf4j.Slf4j; /** * Created by https://github.com/kuangcp on 17-10-24 下午2:25 @@ -14,70 +20,71 @@ @Slf4j class MythSerialize { - T in(Class target, InputStream inputStream) { - T object = null; - try { - Reader reader = new InputStreamReader(inputStream); - BufferedReader bufferedReader = new BufferedReader(reader); - String line = bufferedReader.readLine(); - line = line.substring(1, line.length() - 1); - String[] result = line.split("[,:]"); - object = target.getDeclaredConstructor().newInstance(); - Method[] methods = target.getDeclaredMethods(); - for (int i = 0; i < result.length; i += 3) { - for (Method method : methods) { - if (method.getName().startsWith("set") && method.getName() - .equals("set" + result[i + 1])) { - if ("null".equals(result[i + 2])) { - continue; - } - if ("java.lang.String".equals(result[i])) { - method.invoke(object, result[i + 2]); - } else if ("java.lang.Long".equals(result[i])) { - method.invoke(object, Long.parseLong(result[i + 2])); - } else if ("java.lang.Integer".equals(result[i])) { - method.invoke(object, Integer.parseInt(result[i + 2])); - } else if ("java.lang.Float".equals(result[i])) { - method.invoke(object, Float.parseFloat(result[i + 2])); - } - // 递归序列化??? 发现有自定义的类(一定要继承自定义的序列化接口)就进行序列化然后嵌套 - // 所以序列化的成功与否在于所有的属性是否都序列化 + T in(Class target, InputStream inputStream) { + T object = null; + try { + Reader reader = new InputStreamReader(inputStream); + BufferedReader bufferedReader = new BufferedReader(reader); + String line = bufferedReader.readLine(); + line = line.substring(1, line.length() - 1); + String[] result = line.split("[,:]"); + object = target.getDeclaredConstructor().newInstance(); + Method[] methods = target.getDeclaredMethods(); + for (int i = 0; i < result.length; i += 3) { + for (Method method : methods) { + if (method.getName().startsWith("set") && method.getName() + .equals("set" + result[i + 1])) { + if ("null".equals(result[i + 2])) { + continue; + } + if ("java.lang.String".equals(result[i])) { + method.invoke(object, result[i + 2]); + } else if ("java.lang.Long".equals(result[i])) { + method.invoke(object, Long.parseLong(result[i + 2])); + } else if ("java.lang.Integer".equals(result[i])) { + method.invoke(object, Integer.parseInt(result[i + 2])); + } else if ("java.lang.Float".equals(result[i])) { + method.invoke(object, Float.parseFloat(result[i + 2])); + } + // 递归序列化??? 发现有自定义的类(一定要继承自定义的序列化接口)就进行序列化然后嵌套 + // 所以序列化的成功与否在于所有的属性是否都序列化 // method.invoke(object,filedType.cast(result[i+2])); - } + } + } + } + } catch (InstantiationException | IOException | IllegalAccessException | InvocationTargetException | + NoSuchMethodException e) { + log.error(e.getMessage(), e); } - } - } catch (InstantiationException | IOException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { - log.error(e.getMessage(), e); + return object; } - return object; - } - public ByteArrayOutputStream out(T object) { - try { - Class domain = object.getClass(); - Method[] methods = domain.getDeclaredMethods(); - StringBuilder builder = new StringBuilder(); - builder.append("["); - for (Method method : methods) { + public ByteArrayOutputStream out(T object) { + try { + Class domain = object.getClass(); + Method[] methods = domain.getDeclaredMethods(); + StringBuilder builder = new StringBuilder(); + builder.append("["); + for (Method method : methods) { - if (method.getName().startsWith("get")) { - Class returnType = method.getReturnType(); - builder.append(returnType.getName()) - .append(":").append(method.getName().substring(3)) - .append(":").append(method.invoke(object)) - .append(","); - } - } - String result = builder.toString().substring(0, builder.length() - 1) + "]"; + if (method.getName().startsWith("get")) { + Class returnType = method.getReturnType(); + builder.append(returnType.getName()) + .append(":").append(method.getName().substring(3)) + .append(":").append(method.invoke(object)) + .append(","); + } + } + String result = builder.toString().substring(0, builder.length() - 1) + "]"; - log.info("content={}", result); + log.info("content={}", result); - ByteArrayOutputStream byteOutput = new ByteArrayOutputStream(); - byteOutput.write(result.getBytes()); - return byteOutput; - } catch (IOException | IllegalAccessException | InvocationTargetException e) { - log.error("", e); + ByteArrayOutputStream byteOutput = new ByteArrayOutputStream(); + byteOutput.write(result.getBytes()); + return byteOutput; + } catch (IOException | IllegalAccessException | InvocationTargetException e) { + log.error("", e); + } + return null; } - return null; - } } diff --git a/class/src/test/java/com/github/kuangcp/serialize/SerializeTest.java b/class/src/test/java/com/github/kuangcp/serialize/SerializeTest.java index bda960fd..8a199351 100644 --- a/class/src/test/java/com/github/kuangcp/serialize/SerializeTest.java +++ b/class/src/test/java/com/github/kuangcp/serialize/SerializeTest.java @@ -4,14 +4,7 @@ import org.junit.Assert; import org.junit.Test; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.NotSerializableException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; +import java.io.*; import static org.hamcrest.CoreMatchers.equalTo; import static org.junit.Assert.assertThat; @@ -33,7 +26,7 @@ public void testSerializeWithByte() throws IOException, ClassNotFoundException { ObjectOutputStream output = new ObjectOutputStream(byteOutput); output.writeObject(person); - log.info("content={}", byteOutput.toString()); + log.info("content={}", byteOutput); ByteArrayInputStream byteInput = new ByteArrayInputStream(byteOutput.toByteArray()); @@ -42,6 +35,24 @@ public void testSerializeWithByte() throws IOException, ClassNotFoundException { assertThat(result.getName(), equalTo("name")); } + /** + * [为什么serialVersionUID不能随便改](https://hollischuang.github.io/toBeTopJavaer/#/basics/java-basic/serialVersionUID-modify?id=%e5%a6%82%e6%9e%9cserialversionuid%e5%8f%98%e4%ba%86%e4%bc%9a%e6%80%8e%e6%a0%b7) + */ + @Test + public void testChangeNo() { + try { + // Person serialVersionUID 设置为1 + writeFile(); + + // Person serialVersionUID 设置为2 再执行 +// readFile(); + // 报错: java.io.InvalidClassException: com.github.kuangcp.serialize.Person; local class incompatible: stream classdesc serialVersionUID = 1, local class serialVersionUID = 2 + } catch (Exception e) { + log.error(e.getMessage(), e); + Assert.fail(); + } + } + /** * 序列化对象上某个属性没有实现序列化接口,但该属性没有值 */ diff --git a/class/src/test/java/com/github/kuangcp/serialize/customserialize/MythSerializeTest.java b/class/src/test/java/com/github/kuangcp/serialize/customserialize/MythSerializeTest.java index df7cde3a..db6108b6 100644 --- a/class/src/test/java/com/github/kuangcp/serialize/customserialize/MythSerializeTest.java +++ b/class/src/test/java/com/github/kuangcp/serialize/customserialize/MythSerializeTest.java @@ -1,12 +1,13 @@ package com.github.kuangcp.serialize.customserialize; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.core.IsEqual.equalTo; +import org.junit.Test; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.util.Objects; -import org.junit.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.IsEqual.equalTo; /** * @author kuangcp on 2019-04-21 11:31 AM @@ -14,20 +15,20 @@ public class MythSerializeTest { - @Test - public void test() { - MythSerialize mythSerialize = new MythSerialize<>(); - Myth domain = new Myth(); - domain.setName("myth"); - domain.setPhone("121212121"); - domain.setTest(90909090L); - ByteArrayOutputStream out = mythSerialize.out(domain); + @Test + public void test() { + MythSerialize mythSerialize = new MythSerialize<>(); + Myth domain = new Myth(); + domain.setName("myth"); + domain.setPhone("121212121"); + domain.setTest(90909090L); + ByteArrayOutputStream out = mythSerialize.out(domain); - assert Objects.nonNull(out); + assert Objects.nonNull(out); - Myth result = mythSerialize.in(Myth.class, new ByteArrayInputStream(out.toByteArray())); - System.out.println(result.toString()); + Myth result = mythSerialize.in(Myth.class, new ByteArrayInputStream(out.toByteArray())); + System.out.println(result.toString()); - assertThat(result.getName(), equalTo(domain.getName())); - } + assertThat(result.getName(), equalTo(domain.getName())); + } } From 078281e4cb35a7d36d115c97feca4fbfbd8e3f33 Mon Sep 17 00:00:00 2001 From: kuangcp Date: Fri, 9 May 2025 18:11:04 +0800 Subject: [PATCH 454/476] hex --- .../src/main/java/security/hash/Md5Util.java | 82 +++++++++++++++++++ .../test/java/security/hash/Md5UtilTest.java | 23 ++++++ class/src/test/java/syntax/bit/HexTest.java | 23 ++++++ .../kuangcp/future/CompletableFutureTest.java | 6 +- 4 files changed, 131 insertions(+), 3 deletions(-) create mode 100644 class/src/main/java/security/hash/Md5Util.java create mode 100644 class/src/test/java/security/hash/Md5UtilTest.java diff --git a/class/src/main/java/security/hash/Md5Util.java b/class/src/main/java/security/hash/Md5Util.java new file mode 100644 index 00000000..e581d713 --- /dev/null +++ b/class/src/main/java/security/hash/Md5Util.java @@ -0,0 +1,82 @@ +package security.hash; + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; + +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.security.MessageDigest; +import java.util.Optional; + +/** + * @author Kuangcp + * 2025-05-09 17:18 + */ +@Slf4j +public class Md5Util { + + private static final char[] HEX_ARRAY = "0123456789abcdef".toCharArray(); + + /** + * 性能更好 + */ + public static String bytesToHex(byte[] bytes) { + char[] hexChars = new char[bytes.length * 2]; + for (int i = 0; i < bytes.length; i++) { + int v = bytes[i] & 0xFF; + hexChars[i * 2] = HEX_ARRAY[v >>> 4]; + hexChars[i * 2 + 1] = HEX_ARRAY[v & 0x0F]; + } + return new String(hexChars); + } + + public static String bytesToHexBuilder(byte[] bytes) { + StringBuilder hexString = new StringBuilder(); + for (byte b : bytes) { + String hex = Integer.toHexString(0xff & b); + if (hex.length() == 1) { + hexString.append('0'); + } + hexString.append(hex); + } + return hexString.toString(); + } + + /** + * 逐块计算MD5值, + */ + public static Optional calculateFileMd5(String urlString) { + if (StringUtils.isBlank(urlString)) { + return Optional.empty(); + } + + try { + URL url = new URL(urlString); + // 获取MD5摘要算法实例 + MessageDigest digest = MessageDigest.getInstance("MD5"); + + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setConnectTimeout(5000); + connection.setRequestProperty("User-Agent", "Mozilla/5.0"); + + // 打开URL连接并获取输入流 + try (InputStream is = connection.getInputStream()) { + byte[] buffer = new byte[8192]; + int bytesRead; + // 逐块读取内容并更新摘要 + while ((bytesRead = is.read(buffer)) != -1) { + digest.update(buffer, 0, bytesRead); + } + } + + // 获取最终的摘要值(字节数组) + byte[] hashBytes = digest.digest(); + return Optional.of(bytesToHex(hashBytes)); + } catch (Exception e) { + log.error("计算文件md5异常,url={}", urlString, e); + } + + return Optional.empty(); + } +} diff --git a/class/src/test/java/security/hash/Md5UtilTest.java b/class/src/test/java/security/hash/Md5UtilTest.java new file mode 100644 index 00000000..e3b60631 --- /dev/null +++ b/class/src/test/java/security/hash/Md5UtilTest.java @@ -0,0 +1,23 @@ +package security.hash; + +import org.junit.Assert; +import org.junit.Test; + +import java.util.Optional; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; + +/** + * @author Kuangcp + * 2025-05-09 18:02 + */ +public class Md5UtilTest { + + @Test + public void testCalculateFileMd5() throws Exception { + Optional result = Md5Util.calculateFileMd5("https://docs.github.com/assets/cb-345/images/site/favicon.png"); + assertThat(result.isPresent(), equalTo(true)); + Assert.assertEquals("3f0e5c3208cc4bfcb07223777cfbca25", result.get()); + } +} diff --git a/class/src/test/java/syntax/bit/HexTest.java b/class/src/test/java/syntax/bit/HexTest.java index e72debb9..d41b90bd 100644 --- a/class/src/test/java/syntax/bit/HexTest.java +++ b/class/src/test/java/syntax/bit/HexTest.java @@ -1,10 +1,14 @@ package syntax.bit; import org.junit.Test; +import security.hash.Md5Util; import java.math.BigInteger; import java.security.MessageDigest; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.MatcherAssert.assertThat; + /** * @author Kuangcp * 2025-04-10 16:14 @@ -15,10 +19,29 @@ public class HexTest { * printf hi | md5sum */ @Test + @Deprecated public void testHex() throws Exception { String txt = "hi"; byte[] hash = MessageDigest.getInstance("MD5").digest(txt.getBytes()); String checksum = new BigInteger(1, hash).toString(16); System.out.println(checksum); + System.out.println(Md5Util.bytesToHexBuilder(hash)); + System.out.println(Md5Util.bytesToHex(hash)); + } + + @Test + public void testLossHigh() throws Exception { + byte[] hash = new byte[]{ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 + }; + // 简单但是有隐患,例如最高位是0时,会丢失高位的所有0 + String checksum = new BigInteger(1, hash).toString(16); + + System.out.println(checksum); + assertThat(checksum, equalTo("0")); + + System.out.println(Md5Util.bytesToHexBuilder(hash)); + assertThat(Md5Util.bytesToHexBuilder(hash), equalTo("000000000000000000")); + assertThat(Md5Util.bytesToHex(hash), equalTo("000000000000000000")); } } diff --git a/java8/src/test/java/com/github/kuangcp/future/CompletableFutureTest.java b/java8/src/test/java/com/github/kuangcp/future/CompletableFutureTest.java index 62b91293..67d40bf2 100644 --- a/java8/src/test/java/com/github/kuangcp/future/CompletableFutureTest.java +++ b/java8/src/test/java/com/github/kuangcp/future/CompletableFutureTest.java @@ -38,6 +38,8 @@ public void testAsync() throws Exception { } /** + * 提交一批任务,并行执行,等待结果 + * * @see java.util.concurrent.CompletableFuture.asyncPool 默认线程池 */ @Test @@ -58,9 +60,7 @@ public void testAsyncPoolSize() throws Exception { } for (Future future : wait) { future.get(); - if (future.isDone()) { - log.info("complete"); - } + log.info("complete"); } } From a3cce1d32e2129ed410b7f3e6645ad9c64b34409 Mon Sep 17 00:00:00 2001 From: kuangcp Date: Fri, 9 May 2025 18:28:50 +0800 Subject: [PATCH 455/476] md5 --- .../src/main/java/security/hash/Md5Util.java | 36 +++++++++++++++++-- .../test/java/security/hash/Md5UtilTest.java | 9 ++++- 2 files changed, 42 insertions(+), 3 deletions(-) diff --git a/class/src/main/java/security/hash/Md5Util.java b/class/src/main/java/security/hash/Md5Util.java index e581d713..5d7646d4 100644 --- a/class/src/main/java/security/hash/Md5Util.java +++ b/class/src/main/java/security/hash/Md5Util.java @@ -6,6 +6,8 @@ import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Paths; import java.security.MessageDigest; import java.util.Optional; @@ -44,9 +46,9 @@ public static String bytesToHexBuilder(byte[] bytes) { } /** - * 逐块计算MD5值, + * 逐块计算MD5值,缓存区是512整数倍即可 */ - public static Optional calculateFileMd5(String urlString) { + public static Optional urlMd5(String urlString) { if (StringUtils.isBlank(urlString)) { return Optional.empty(); } @@ -79,4 +81,34 @@ public static Optional calculateFileMd5(String urlString) { return Optional.empty(); } + + /** + * 本地文件完整计算MD5 + */ + public static Optional fileMd5(String path) { + try { + byte[] data = Files.readAllBytes(Paths.get(path)); + MessageDigest digest = MessageDigest.getInstance("MD5"); + byte[] hash = digest.digest(data); + return Optional.of(bytesToHex(hash)); + } catch (Exception e) { + log.error("", e); + } + return Optional.empty(); + } + + /** + * 字符串计算MD5 + */ + public static Optional stringMd5(String raw) { + try { + byte[] data = raw.getBytes(); + MessageDigest digest = MessageDigest.getInstance("MD5"); + byte[] hash = digest.digest(data); + return Optional.of(bytesToHex(hash)); + } catch (Exception e) { + log.error("", e); + } + return Optional.empty(); + } } diff --git a/class/src/test/java/security/hash/Md5UtilTest.java b/class/src/test/java/security/hash/Md5UtilTest.java index e3b60631..076ddc15 100644 --- a/class/src/test/java/security/hash/Md5UtilTest.java +++ b/class/src/test/java/security/hash/Md5UtilTest.java @@ -16,8 +16,15 @@ public class Md5UtilTest { @Test public void testCalculateFileMd5() throws Exception { - Optional result = Md5Util.calculateFileMd5("https://docs.github.com/assets/cb-345/images/site/favicon.png"); + Optional result = Md5Util.urlMd5("https://docs.github.com/assets/cb-345/images/site/favicon.png"); assertThat(result.isPresent(), equalTo(true)); Assert.assertEquals("3f0e5c3208cc4bfcb07223777cfbca25", result.get()); } + + @Test + public void testString() throws Exception { + Optional hash = Md5Util.stringMd5("hi"); + System.out.println(hash.get()); + } + } From ae8baca3720991cbcd3d8b98a62c2b4dce39cab1 Mon Sep 17 00:00:00 2001 From: kuangcp Date: Tue, 13 May 2025 14:59:44 +0800 Subject: [PATCH 456/476] bench --- .../main/java/thread/pool/CusBenchPool.java | 106 ++++++++++++++++++ .../java/thread/pool/CusBenchPoolTest.java | 45 ++++++++ 2 files changed, 151 insertions(+) create mode 100644 concurrency/src/main/java/thread/pool/CusBenchPool.java create mode 100644 concurrency/src/test/java/thread/pool/CusBenchPoolTest.java diff --git a/concurrency/src/main/java/thread/pool/CusBenchPool.java b/concurrency/src/main/java/thread/pool/CusBenchPool.java new file mode 100644 index 00000000..5b5ff297 --- /dev/null +++ b/concurrency/src/main/java/thread/pool/CusBenchPool.java @@ -0,0 +1,106 @@ +package thread.pool; + +import lombok.Data; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.RejectedExecutionHandler; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; + +/** + * @author Kuangcp + * 2025-05-13 14:05 + */ +public class CusBenchPool extends ThreadPoolExecutor { + private final static ConcurrentLinkedQueue queue = new ConcurrentLinkedQueue<>(); + + private final static AtomicInteger cnt = new AtomicInteger(); + + @Data + public static class Task { + private long startAt; + private long endAt; + private String name; + + public Task(String name) { + this.name = name; + this.startAt = System.currentTimeMillis(); + } + + public void end() { + this.endAt = System.currentTimeMillis(); + } + + public boolean isEnd() { + return endAt > 0; + } + + public String toString() { + return name + ": " + startAt + " " + endAt; + } + } + + public CusBenchPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, + TimeUnit unit, BlockingQueue workQueue) { + super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue); + } + + public CusBenchPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, + BlockingQueue workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) { + super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler); + } + + @Override + public void execute(Runnable command) { + if (Objects.isNull(command)) { + return; + } + super.execute(() -> { + Task task = new Task(cnt.incrementAndGet() + ""); + queue.add(task); + + command.run(); + task.end(); + }); + } + + public String showTask() { + StringBuilder res = new StringBuilder(); + for (Task task : queue) { + res.append(task.toString()).append("\n"); + } + return res.toString(); + } + + public String statisticsTask() { + StringBuilder res = new StringBuilder(); + long total = 0; + long cnt = 0; + List all = new ArrayList<>(); + for (Task task : queue) { + if (!task.isEnd()) { + continue; + } + cnt++; + long run = task.endAt - task.startAt; + total += run; + all.add(run); + } + List sorted = all.stream().sorted().collect(Collectors.toList()); + int size = sorted.size(); + Long P30 = sorted.get((int) (size * 0.3)); + Long P50 = sorted.get((int) (size * 0.5)); + Long P90 = sorted.get((int) (size * 0.9)); + Long P99 = sorted.get((int) (size * 0.99)); + + return "cnt: " + cnt + " total:" + total + " avg:" + total / cnt + + " P30 " + P30 + " P50 " + P50 + " P90 " + P90 + " P99 " + P99; + } +} diff --git a/concurrency/src/test/java/thread/pool/CusBenchPoolTest.java b/concurrency/src/test/java/thread/pool/CusBenchPoolTest.java new file mode 100644 index 00000000..b2c431d0 --- /dev/null +++ b/concurrency/src/test/java/thread/pool/CusBenchPoolTest.java @@ -0,0 +1,45 @@ +package thread.pool; + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.concurrent.BasicThreadFactory; +import org.junit.Test; + +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +/** + * @author Kuangcp + * 2025-05-13 14:44 + */ +@Slf4j +public class CusBenchPoolTest { + + @Test + public void testShow() throws Exception { + CusBenchPool pool = new CusBenchPool(10, 20, + 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(1000), + new BasicThreadFactory.Builder().namingPattern("limit-%d").build(), + new ThreadPoolExecutor.AbortPolicy()); + + CusSchedulePool sche = new CusSchedulePool(1); + sche.scheduleAtFixedRate(() -> { + log.info("Run {}", pool.statisticsTask()); + }, 2, 3, TimeUnit.SECONDS); + + int total = 1000; + for (int i = 0; i < total; i++) { + pool.execute(() -> { + try { + TimeUnit.MILLISECONDS.sleep(500 + ThreadLocalRandom.current().nextInt(600)); + } catch (Exception e) { + log.error("", e); + } + }); + } + + + Thread.currentThread().join(); + } +} From 2b0fb6b44bbdeec1af27cb829fa2ac67b6b20a69 Mon Sep 17 00:00:00 2001 From: kuangcp Date: Tue, 13 May 2025 15:40:11 +0800 Subject: [PATCH 457/476] pool --- .../main/java/thread/pool/CusBenchPool.java | 32 ++++++++++++++++--- .../java/thread/pool/CusBenchPoolTest.java | 2 +- spring/pom.xml | 4 +++ 3 files changed, 33 insertions(+), 5 deletions(-) diff --git a/concurrency/src/main/java/thread/pool/CusBenchPool.java b/concurrency/src/main/java/thread/pool/CusBenchPool.java index 5b5ff297..353d83ed 100644 --- a/concurrency/src/main/java/thread/pool/CusBenchPool.java +++ b/concurrency/src/main/java/thread/pool/CusBenchPool.java @@ -1,5 +1,6 @@ package thread.pool; +import lombok.AllArgsConstructor; import lombok.Data; import java.util.ArrayList; @@ -47,6 +48,31 @@ public String toString() { } } + @Data + @AllArgsConstructor + public static class ReportVO { + private int core; + private long cnt; + private long total; + private long min; + private long max; + private long avg; + private long p30; + private long p50; + private long p90; + private long p99; + + public String toString() { + return "cnt: " + cnt + " total:" + total + " avg:" + avg + " min:" + min + " max:" + max + + " P: " + p30 + ", " + p50 + ", " + p90 + ", " + p99; + } + + public String format() { + return String.format("core:%2d cnt:%4d total:%8d avg:%5d min:%5d max:%5d P: %5d,%5d,%5d,%5d", + core, cnt, total, avg, min, max, p30, p50, p90, p99); + } + } + public CusBenchPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue) { super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue); @@ -79,8 +105,7 @@ public String showTask() { return res.toString(); } - public String statisticsTask() { - StringBuilder res = new StringBuilder(); + public ReportVO statisticsTask() { long total = 0; long cnt = 0; List all = new ArrayList<>(); @@ -100,7 +125,6 @@ public String statisticsTask() { Long P90 = sorted.get((int) (size * 0.9)); Long P99 = sorted.get((int) (size * 0.99)); - return "cnt: " + cnt + " total:" + total + " avg:" + total / cnt - + " P30 " + P30 + " P50 " + P50 + " P90 " + P90 + " P99 " + P99; + return new ReportVO(getCorePoolSize(), cnt, total, sorted.get(0), sorted.get(size - 1), total / cnt, P30, P50, P90, P99); } } diff --git a/concurrency/src/test/java/thread/pool/CusBenchPoolTest.java b/concurrency/src/test/java/thread/pool/CusBenchPoolTest.java index b2c431d0..ed06cdfc 100644 --- a/concurrency/src/test/java/thread/pool/CusBenchPoolTest.java +++ b/concurrency/src/test/java/thread/pool/CusBenchPoolTest.java @@ -25,7 +25,7 @@ public void testShow() throws Exception { CusSchedulePool sche = new CusSchedulePool(1); sche.scheduleAtFixedRate(() -> { - log.info("Run {}", pool.statisticsTask()); + log.info("Run {}", pool.statisticsTask().format()); }, 2, 3, TimeUnit.SECONDS); int total = 1000; diff --git a/spring/pom.xml b/spring/pom.xml index d2db862f..33510437 100644 --- a/spring/pom.xml +++ b/spring/pom.xml @@ -86,6 +86,10 @@ io.projectreactor.netty reactor-netty-http + + org.apache.commons + commons-lang3 + From 4a108dd0333e66bca33e90caf410b1368c697f16 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Thu, 15 May 2025 22:05:35 +0800 Subject: [PATCH 458/476] todo --- class/src/main/java/jvm/MallocBench.java | 9 +++ .../java/jvm/oom/DirectMemoryGlibcGrow.java | 12 +++ .../github/kuangcp/pool/SpringThreadPool.java | 73 +++++++++++++++++++ 3 files changed, 94 insertions(+) create mode 100644 class/src/main/java/jvm/MallocBench.java create mode 100644 class/src/main/java/jvm/oom/DirectMemoryGlibcGrow.java create mode 100644 spring/src/main/java/com/github/kuangcp/pool/SpringThreadPool.java diff --git a/class/src/main/java/jvm/MallocBench.java b/class/src/main/java/jvm/MallocBench.java new file mode 100644 index 00000000..549eed77 --- /dev/null +++ b/class/src/main/java/jvm/MallocBench.java @@ -0,0 +1,9 @@ +package jvm; + +/** + * @author Kuangcp + * 2025-05-15 22:02 + */ +public class MallocBench { + // TODO 对比 glibc jemalloc tcmalloc musl-malloc 在同样的并发场景下的性能表现 +} diff --git a/class/src/main/java/jvm/oom/DirectMemoryGlibcGrow.java b/class/src/main/java/jvm/oom/DirectMemoryGlibcGrow.java new file mode 100644 index 00000000..561c9d1e --- /dev/null +++ b/class/src/main/java/jvm/oom/DirectMemoryGlibcGrow.java @@ -0,0 +1,12 @@ +package jvm.oom; + +/** + * https://github.com/Kuangcp/Note/blob/master/Linux/Base/LinuxBase.md#glibc-ptmalloc2 + * + * @author Kuangcp + * 2025-05-15 21:59 + */ +public class DirectMemoryGlibcGrow { + //TODO 模拟出Glibc thread arena 占用大的情况 + //TODO 替换 jemalloc 后查看是否有问题 +} diff --git a/spring/src/main/java/com/github/kuangcp/pool/SpringThreadPool.java b/spring/src/main/java/com/github/kuangcp/pool/SpringThreadPool.java new file mode 100644 index 00000000..19e0d4b4 --- /dev/null +++ b/spring/src/main/java/com/github/kuangcp/pool/SpringThreadPool.java @@ -0,0 +1,73 @@ +package com.github.kuangcp.pool; + +import org.apache.commons.lang3.concurrent.BasicThreadFactory; +import org.springframework.context.annotation.Bean; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; + +import java.util.concurrent.ThreadPoolExecutor; + +/** + * @author Kuangcp + * 2025-05-13 15:12 + */ +public class SpringThreadPool { + + /** + * 核心线程数 - 保持较小以减少资源消耗 + */ + private int corePoolSize = 10; + + /** + * 最大线程数 - 考虑到LLM调用的IO密集特性 + */ + private int maxPoolSize = 20; + + /** + * 队列容量 - 较大的队列用于缓冲请求 + */ + private int queueCapacity = 1000; + + /** + * 线程空闲超时时间(秒) + */ + private int keepAliveSeconds = 300; + + /** + * 是否等待所有任务完成后再关闭线程池 + */ + private boolean waitForTasksToCompleteOnShutdown = true; + + /** + * 线程池关闭时等待任务完成的最长时间(秒) + */ + private int awaitTerminationSeconds = 60; + + @Bean(name = "llmThreadPool") + public ThreadPoolTaskExecutor llmThreadPool() { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + + // 核心配置 + executor.setCorePoolSize(corePoolSize); + executor.setMaxPoolSize(maxPoolSize); + executor.setQueueCapacity(queueCapacity); + executor.setKeepAliveSeconds(keepAliveSeconds); + + // 使用自定义的线程工厂,设置有意义的线程名称前缀 + executor.setThreadFactory(new BasicThreadFactory.Builder() + .namingPattern("llm-worker-%d") + .daemon(true) // 设置为守护线程 + .build()); + + // 设置拒绝策略为CallerRunsPolicy,防止任务丢失 + executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); + + // 优雅关闭配置 + executor.setWaitForTasksToCompleteOnShutdown(waitForTasksToCompleteOnShutdown); + executor.setAwaitTerminationSeconds(awaitTerminationSeconds); + + // 初始化线程池 + executor.initialize(); + + return executor; + } +} From b9b0cd6b7291816c7a9cd6b66fbb480eb77afce1 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Mon, 19 May 2025 22:11:33 +0800 Subject: [PATCH 459/476] thread arena --- .../java/jvm/oom/DirectMemoryGlibcGrow.java | 50 +++++++++++++++++++ .../main/java/jvm/oom/DirectMemoryOOM.java | 2 +- .../jvm/oom/DirectMemoryGlibcGrowTest.java | 25 ++++++++++ 3 files changed, 76 insertions(+), 1 deletion(-) create mode 100644 class/src/test/java/jvm/oom/DirectMemoryGlibcGrowTest.java diff --git a/class/src/main/java/jvm/oom/DirectMemoryGlibcGrow.java b/class/src/main/java/jvm/oom/DirectMemoryGlibcGrow.java index 561c9d1e..b5bd7b7e 100644 --- a/class/src/main/java/jvm/oom/DirectMemoryGlibcGrow.java +++ b/class/src/main/java/jvm/oom/DirectMemoryGlibcGrow.java @@ -1,12 +1,62 @@ package jvm.oom; +import jvm.MallocBench; +import lombok.extern.slf4j.Slf4j; + +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.TimeUnit; + /** * https://github.com/Kuangcp/Note/blob/master/Linux/Base/LinuxBase.md#glibc-ptmalloc2 * * @author Kuangcp * 2025-05-15 21:59 + * @see MallocBench */ +@Slf4j public class DirectMemoryGlibcGrow { //TODO 模拟出Glibc thread arena 占用大的情况 //TODO 替换 jemalloc 后查看是否有问题 + + + // https://medium.com/@daniyal.hass/how-glibc-memory-handling-affects-java-applications-the-hidden-cost-of-fragmentation-8e666ee6e000 + public static void fragment() throws InterruptedException { + List buffers = new ArrayList<>(); + // Allocate large buffers and release them to see memory impact + for (int i = 0; i < 50; i++) { + int delta = ThreadLocalRandom.current().nextInt(100) + 33; + System.out.print(delta + ", "); + buffers.add(ByteBuffer.allocateDirect(delta * 1024 * 1024)); // 100 MB each + + buffers.remove(0); +// if (i % 2 == 0) buffers.remove(0); // Simulate variable memory usage + } + + System.out.println("Memory allocations complete. Check OS memory usage."); + } + + public static void loopFragment() throws InterruptedException { + TimeUnit.SECONDS.sleep(10); + ExecutorService xo = Executors.newFixedThreadPool(3); + Runnable run = () -> { + try { + for (int i = 0; i < 1000; i++) { + TimeUnit.SECONDS.sleep(3); + fragment(); + System.out.println("loop " + i); + } + } catch (Exception e) { + log.error("", e); + } + }; + xo.submit(run); + xo.submit(run); + + } + } diff --git a/class/src/main/java/jvm/oom/DirectMemoryOOM.java b/class/src/main/java/jvm/oom/DirectMemoryOOM.java index 6f799ca7..4c4cbfa1 100644 --- a/class/src/main/java/jvm/oom/DirectMemoryOOM.java +++ b/class/src/main/java/jvm/oom/DirectMemoryOOM.java @@ -27,7 +27,7 @@ public class DirectMemoryOOM { // 注意: -XX:MaxDirectMemorySize参数只对由DirectByteBuffer分配的内存有效限制,对Unsafe直接分配的内存无效 /** - * C语言malloc申请的也是虚拟内存,没有设置值的话操作系统不会分配物理内存 + * C语言malloc申请的是虚拟内存,没有设置值的话操作系统不会分配物理内存 * * @see java.nio.DirectByteBuffer#DirectByteBuffer(int) *

diff --git a/class/src/test/java/jvm/oom/DirectMemoryGlibcGrowTest.java b/class/src/test/java/jvm/oom/DirectMemoryGlibcGrowTest.java new file mode 100644 index 00000000..50268f0f --- /dev/null +++ b/class/src/test/java/jvm/oom/DirectMemoryGlibcGrowTest.java @@ -0,0 +1,25 @@ +package jvm.oom; + +import org.junit.Test; + +/** + * @author Kuangcp + * 2025-05-19 21:36 + */ +public class DirectMemoryGlibcGrowTest { + + @Test + public void testFragment() throws Exception { + DirectMemoryGlibcGrow.fragment(); + Thread.currentThread().join(); + } + + /** + * pmap -x 259296 | jvm-tool -s 查看块数量 + */ + @Test + public void testLoopFragment() throws Exception { + DirectMemoryGlibcGrow.loopFragment(); + Thread.currentThread().join(); + } +} From 10720a3a04b71113bacae4c0fef08925259e3ca4 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Mon, 19 May 2025 22:21:38 +0800 Subject: [PATCH 460/476] verify glibc ptmalloc arena --- class/src/main/java/jvm/oom/DirectMemoryGlibcGrow.java | 2 +- class/src/test/java/jvm/oom/DirectMemoryGlibcGrowTest.java | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/class/src/main/java/jvm/oom/DirectMemoryGlibcGrow.java b/class/src/main/java/jvm/oom/DirectMemoryGlibcGrow.java index b5bd7b7e..b502514a 100644 --- a/class/src/main/java/jvm/oom/DirectMemoryGlibcGrow.java +++ b/class/src/main/java/jvm/oom/DirectMemoryGlibcGrow.java @@ -46,7 +46,7 @@ public static void loopFragment() throws InterruptedException { Runnable run = () -> { try { for (int i = 0; i < 1000; i++) { - TimeUnit.SECONDS.sleep(3); + TimeUnit.MILLISECONDS.sleep(2000 + ThreadLocalRandom.current().nextInt(1000)); fragment(); System.out.println("loop " + i); } diff --git a/class/src/test/java/jvm/oom/DirectMemoryGlibcGrowTest.java b/class/src/test/java/jvm/oom/DirectMemoryGlibcGrowTest.java index 50268f0f..72eee50b 100644 --- a/class/src/test/java/jvm/oom/DirectMemoryGlibcGrowTest.java +++ b/class/src/test/java/jvm/oom/DirectMemoryGlibcGrowTest.java @@ -16,6 +16,8 @@ public void testFragment() throws Exception { /** * pmap -x 259296 | jvm-tool -s 查看块数量 + * 注意,IDE启动,则是使用全部的核心,最大thread arena数量是 cpu核数*8 + * 可复制IDE第一行命令,创建shell脚本,删除-agentlib:jdwp的参数,命令前添加 taskset -c 0,1 来限制CPU核心数,可以看到数量就变成17了 1+8*2, 1是 main arena */ @Test public void testLoopFragment() throws Exception { From 341a992a425f228ea1ed8dc91c089b27f137d49c Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Tue, 20 May 2025 12:23:44 +0800 Subject: [PATCH 461/476] test --- class/src/main/java/jvm/oom/DirectMemoryGlibcGrow.java | 6 ++++-- class/src/test/java/jvm/oom/DirectMemoryGlibcGrowTest.java | 3 +++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/class/src/main/java/jvm/oom/DirectMemoryGlibcGrow.java b/class/src/main/java/jvm/oom/DirectMemoryGlibcGrow.java index b502514a..e19c5693 100644 --- a/class/src/main/java/jvm/oom/DirectMemoryGlibcGrow.java +++ b/class/src/main/java/jvm/oom/DirectMemoryGlibcGrow.java @@ -29,7 +29,8 @@ public static void fragment() throws InterruptedException { List buffers = new ArrayList<>(); // Allocate large buffers and release them to see memory impact for (int i = 0; i < 50; i++) { - int delta = ThreadLocalRandom.current().nextInt(100) + 33; +// int delta = ThreadLocalRandom.current().nextInt(40) + 33; + int delta = 37; System.out.print(delta + ", "); buffers.add(ByteBuffer.allocateDirect(delta * 1024 * 1024)); // 100 MB each @@ -46,7 +47,7 @@ public static void loopFragment() throws InterruptedException { Runnable run = () -> { try { for (int i = 0; i < 1000; i++) { - TimeUnit.MILLISECONDS.sleep(2000 + ThreadLocalRandom.current().nextInt(1000)); + TimeUnit.MILLISECONDS.sleep(1000 + ThreadLocalRandom.current().nextInt(1000)); fragment(); System.out.println("loop " + i); } @@ -56,6 +57,7 @@ public static void loopFragment() throws InterruptedException { }; xo.submit(run); xo.submit(run); + xo.submit(run); } diff --git a/class/src/test/java/jvm/oom/DirectMemoryGlibcGrowTest.java b/class/src/test/java/jvm/oom/DirectMemoryGlibcGrowTest.java index 72eee50b..e691efa7 100644 --- a/class/src/test/java/jvm/oom/DirectMemoryGlibcGrowTest.java +++ b/class/src/test/java/jvm/oom/DirectMemoryGlibcGrowTest.java @@ -18,6 +18,9 @@ public void testFragment() throws Exception { * pmap -x 259296 | jvm-tool -s 查看块数量 * 注意,IDE启动,则是使用全部的核心,最大thread arena数量是 cpu核数*8 * 可复制IDE第一行命令,创建shell脚本,删除-agentlib:jdwp的参数,命令前添加 taskset -c 0,1 来限制CPU核心数,可以看到数量就变成17了 1+8*2, 1是 main arena + *

+ * 替换为jemalloc export LD_PRELOAD=/usr/lib/libjemalloc.so,固定37M块并行分配的情况 两种malloc都是RSS内存在1-7G内存浮动,glibc可以看到17个arena + * 所以这个测试场景不能充分展示 大对象导致 大量thread arena 占用 */ @Test public void testLoopFragment() throws Exception { From c5d2e462c1c1636df12314f544959d61c89a5a56 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Thu, 19 Jun 2025 21:49:51 +0800 Subject: [PATCH 462/476] cs rebuild --- .../CalculateBtnBeginActionAdapter.java | 22 - .../CalculateBtnCancelActionAdapter.java | 22 - .../CalculateBtnEqualActionAdapter.java | 22 - .../CalculateBtnIncreaseActionAdapter.java | 22 - .../CalculateBtnMinusActionAdapter.java | 22 - .../CalculateBtnPointActionAdapter.java | 22 - .../CalculateBtnZeroActionAdapter.java | 22 - .../github/kuangcp/caculator/Calculator.java | 450 ++++++------------ .../caculator/CalculatorButtonFactory.java | 39 ++ .../caculator/CalculatorException.java | 11 + .../kuangcp/caculator/CalculatorModel.java | 102 ++++ .../github/kuangcp/caculator/Operation.java | 54 +++ 12 files changed, 348 insertions(+), 462 deletions(-) delete mode 100644 gui/src/main/java/com/github/kuangcp/caculator/CalculateBtnBeginActionAdapter.java delete mode 100644 gui/src/main/java/com/github/kuangcp/caculator/CalculateBtnCancelActionAdapter.java delete mode 100644 gui/src/main/java/com/github/kuangcp/caculator/CalculateBtnEqualActionAdapter.java delete mode 100644 gui/src/main/java/com/github/kuangcp/caculator/CalculateBtnIncreaseActionAdapter.java delete mode 100644 gui/src/main/java/com/github/kuangcp/caculator/CalculateBtnMinusActionAdapter.java delete mode 100644 gui/src/main/java/com/github/kuangcp/caculator/CalculateBtnPointActionAdapter.java delete mode 100644 gui/src/main/java/com/github/kuangcp/caculator/CalculateBtnZeroActionAdapter.java create mode 100644 gui/src/main/java/com/github/kuangcp/caculator/CalculatorButtonFactory.java create mode 100644 gui/src/main/java/com/github/kuangcp/caculator/CalculatorException.java create mode 100644 gui/src/main/java/com/github/kuangcp/caculator/CalculatorModel.java create mode 100644 gui/src/main/java/com/github/kuangcp/caculator/Operation.java diff --git a/gui/src/main/java/com/github/kuangcp/caculator/CalculateBtnBeginActionAdapter.java b/gui/src/main/java/com/github/kuangcp/caculator/CalculateBtnBeginActionAdapter.java deleted file mode 100644 index 34637f0d..00000000 --- a/gui/src/main/java/com/github/kuangcp/caculator/CalculateBtnBeginActionAdapter.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.github.kuangcp.caculator; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -/** - * created by https://gitee.com/gin9 - * - * @author kuangcp on 3/19/19-1:02 AM - */ -class CalculateBtnBeginActionAdapter implements ActionListener { - - private Calculator adapter; - - CalculateBtnBeginActionAdapter(Calculator adapter) { - this.adapter = adapter; - } - - public void actionPerformed(ActionEvent e) { - adapter.btnBeginActionPerformed(); - } -} diff --git a/gui/src/main/java/com/github/kuangcp/caculator/CalculateBtnCancelActionAdapter.java b/gui/src/main/java/com/github/kuangcp/caculator/CalculateBtnCancelActionAdapter.java deleted file mode 100644 index 062abf41..00000000 --- a/gui/src/main/java/com/github/kuangcp/caculator/CalculateBtnCancelActionAdapter.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.github.kuangcp.caculator; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -/** - * created by https://gitee.com/gin9 - * - * @author kuangcp on 3/19/19-1:02 AM - */ -class CalculateBtnCancelActionAdapter implements ActionListener { - - private Calculator adapter; - - CalculateBtnCancelActionAdapter(Calculator adapter) { - this.adapter = adapter; - } - - public void actionPerformed(ActionEvent e) { - adapter.btnCancel_actionPerformed(); - } -} diff --git a/gui/src/main/java/com/github/kuangcp/caculator/CalculateBtnEqualActionAdapter.java b/gui/src/main/java/com/github/kuangcp/caculator/CalculateBtnEqualActionAdapter.java deleted file mode 100644 index 0461836a..00000000 --- a/gui/src/main/java/com/github/kuangcp/caculator/CalculateBtnEqualActionAdapter.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.github.kuangcp.caculator; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -/** - * created by https://gitee.com/gin9 - * - * @author kuangcp on 3/19/19-1:02 AM - */ -class CalculateBtnEqualActionAdapter implements ActionListener { - - private Calculator adapter; - - CalculateBtnEqualActionAdapter(Calculator adapter) { - this.adapter = adapter; - } - - public void actionPerformed(ActionEvent e) { - adapter.btnEqualActionPerformed(); - } -} diff --git a/gui/src/main/java/com/github/kuangcp/caculator/CalculateBtnIncreaseActionAdapter.java b/gui/src/main/java/com/github/kuangcp/caculator/CalculateBtnIncreaseActionAdapter.java deleted file mode 100644 index f09bbcfd..00000000 --- a/gui/src/main/java/com/github/kuangcp/caculator/CalculateBtnIncreaseActionAdapter.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.github.kuangcp.caculator; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -/** - * created by https://gitee.com/gin9 - * - * @author kuangcp on 3/19/19-1:02 AM - */ -class CalculateBtnIncreaseActionAdapter implements ActionListener { - - private final Calculator adapter; - - CalculateBtnIncreaseActionAdapter(Calculator adapter) { - this.adapter = adapter; - } - - public void actionPerformed(ActionEvent e) { - adapter.btnIncreaseActionPerformed(e); - } -} diff --git a/gui/src/main/java/com/github/kuangcp/caculator/CalculateBtnMinusActionAdapter.java b/gui/src/main/java/com/github/kuangcp/caculator/CalculateBtnMinusActionAdapter.java deleted file mode 100644 index cf860aae..00000000 --- a/gui/src/main/java/com/github/kuangcp/caculator/CalculateBtnMinusActionAdapter.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.github.kuangcp.caculator; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -/** - * created by https://gitee.com/gin9 - * - * @author kuangcp on 3/19/19-1:02 AM - */ -class CalculateBtnMinusActionAdapter implements ActionListener { - - private Calculator adapter; - - CalculateBtnMinusActionAdapter(Calculator adapter) { - this.adapter = adapter; - } - - public void actionPerformed(ActionEvent e) { - adapter.btnMinusActionPerformed(); - } -} diff --git a/gui/src/main/java/com/github/kuangcp/caculator/CalculateBtnPointActionAdapter.java b/gui/src/main/java/com/github/kuangcp/caculator/CalculateBtnPointActionAdapter.java deleted file mode 100644 index 0e6c1fda..00000000 --- a/gui/src/main/java/com/github/kuangcp/caculator/CalculateBtnPointActionAdapter.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.github.kuangcp.caculator; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -/** - * created by https://gitee.com/gin9 - * - * @author kuangcp on 3/19/19-1:02 AM - */ -class CalculateBtnPointActionAdapter implements ActionListener { - - private Calculator adapter; - - CalculateBtnPointActionAdapter(Calculator adapter) { - this.adapter = adapter; - } - - public void actionPerformed(ActionEvent e) { - adapter.btnPointActionPerformed(e); - } -} diff --git a/gui/src/main/java/com/github/kuangcp/caculator/CalculateBtnZeroActionAdapter.java b/gui/src/main/java/com/github/kuangcp/caculator/CalculateBtnZeroActionAdapter.java deleted file mode 100644 index 22c54e90..00000000 --- a/gui/src/main/java/com/github/kuangcp/caculator/CalculateBtnZeroActionAdapter.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.github.kuangcp.caculator; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -/** - * created by https://gitee.com/gin9 - * - * @author kuangcp on 3/19/19-1:02 AM - */ -class CalculateBtnZeroActionAdapter implements ActionListener { - - private Calculator adapter; - - CalculateBtnZeroActionAdapter(Calculator adapter) { - this.adapter = adapter; - } - - public void actionPerformed(ActionEvent e) { - adapter.btnZeroActionPerformed(e); - } -} diff --git a/gui/src/main/java/com/github/kuangcp/caculator/Calculator.java b/gui/src/main/java/com/github/kuangcp/caculator/Calculator.java index c2d42bc5..76bbe165 100644 --- a/gui/src/main/java/com/github/kuangcp/caculator/Calculator.java +++ b/gui/src/main/java/com/github/kuangcp/caculator/Calculator.java @@ -1,317 +1,151 @@ package com.github.kuangcp.caculator; - -import java.awt.Dimension; -import java.awt.Font; -import java.awt.Rectangle; -import java.awt.event.ActionEvent; -import java.util.Objects; -import javax.swing.JButton; -import javax.swing.JComponent; -import javax.swing.JFrame; -import javax.swing.JTextField; -import javax.swing.SwingConstants; -import javax.swing.WindowConstants; import lombok.extern.slf4j.Slf4j; -/** - * 搜到的一个计算器实现代码 TODO 思考如何重构 - */ +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionListener; + @Slf4j public class Calculator extends JFrame { - - private static final short width = 47; - private static final short height = 38; - private static final Font dialogFont = new Font("Dialog", Font.PLAIN, 16); - - private String front = ""; - private String behind = ""; //分别用于记录加减乘除运算符之前,之后输入的内容 - private String operator; //用于记录运算符 - private String result;//用于存储运算结果的字符串格式 - private boolean flag = false; //用于记录是否按下了运算符 - private boolean dotFlag = false;//用于判断是否输入了点运算符 - private boolean numFlag = false;//用于判断是否输入了数字 - private boolean calculateFlag = false;//用于判断是否按下了等号运算符 - - private final JTextField txtResult = new JTextField("0"); - private final JTextField inputCache = new JTextField(""); - private final JButton btnNull = new JButton(" "); - - private final JButton btnDecrease = new JButton("-"); - private final JButton btnBegin = new JButton("C"); - - private final JButton btnMultiply = new JButton("*"); - private final JButton btnCancel = new JButton("←"); - - private final JButton btnMinus = new JButton("+/-"); - private final JButton btnPoint = new JButton("."); - private final JButton btnDivide = new JButton("/"); - private final JButton btnEqual = new JButton("="); - private final JButton btnIncrease = new JButton("+"); - - private Calculator() { - try { - rootPane.setLayout(null); - this.setResizable(false); - setSize(new Dimension(400, 300)); - setTitle("计算器"); - - setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); - initLogicButton(); - initNumberButton(); - } catch (Exception e) { - log.error(e.getMessage(), e); - } - } - - /** - * 初始化数字按钮 - */ - private void initNumberButton() { - int startX = 30; - int startY = 170; - int deltaW = 70; - int deltaH = 50; - JButton[] numberButtons = new JButton[10]; - for (int i = 1; i < 10; i++) { - JButton button = new JButton(String.valueOf(i)); - int x = startX + ((i - 1) % 3) * deltaW; - int y = startY - ((i - 1) / 3) * deltaH; - button.setBounds(new Rectangle(x, y, width, height)); - numberButtons[i] = button; + private final CalculatorModel model; + private final JTextField display; + private final JTextField inputCache; + + public Calculator() { + model = new CalculatorModel(); + display = new JTextField("0"); + inputCache = new JTextField(""); + + try { + initializeFrame(); + initializeDisplay(); + initializeButtons(); + } catch (Exception e) { + log.error(e.getMessage(), e); + } + } + + private void initializeFrame() { + setTitle("计算器"); + setSize(new Dimension(400, 300)); + setResizable(false); + setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); + getContentPane().setLayout(null); + } + + private void initializeDisplay() { + display.setEnabled(false); + display.setEditable(false); + display.setHorizontalAlignment(SwingConstants.RIGHT); + display.setBounds(33, 19, 310, 34); + + inputCache.setEnabled(false); + inputCache.setEditable(false); + inputCache.setHorizontalAlignment(SwingConstants.RIGHT); + inputCache.setBounds(27, 19, 310, 8); + + getContentPane().add(display); + getContentPane().add(inputCache); + } + + private void initializeButtons() { + // 数字按钮 + ActionListener numberListener = e -> { + try { + String number = e.getActionCommand(); + model.setNumber(number); + updateDisplay(); + } catch (Exception ex) { + log.error("数字输入错误", ex); + display.setText("错误"); + } + }; + + int startX = 30; + int startY = 170; + int deltaW = 70; + int deltaH = 50; + + // 创建数字按钮 1-9 + for (int i = 1; i <= 9; i++) { + int x = startX + ((i - 1) % 3) * deltaW; + int y = startY - ((i - 1) / 3) * deltaH; + getContentPane().add(CalculatorButtonFactory.createNumberButton( + String.valueOf(i), x, y, numberListener)); + } + + // 创建数字0按钮 + getContentPane().add(CalculatorButtonFactory.createNumberButton( + "0", startX, startY + deltaH, numberListener)); + + // 运算符按钮 + ActionListener operatorListener = e -> { + try { + Operation op = Operation.fromSymbol(e.getActionCommand()); + model.setOperation(op); + updateDisplay(); + } catch (Exception ex) { + log.error("运算符输入错误", ex); + display.setText("错误"); + } + }; + + // 添加运算符按钮 + getContentPane().add(CalculatorButtonFactory.createOperationButton( + Operation.ADD, 234, 70, operatorListener)); + getContentPane().add(CalculatorButtonFactory.createOperationButton( + Operation.SUBTRACT, 234, 120, operatorListener)); + getContentPane().add(CalculatorButtonFactory.createOperationButton( + Operation.MULTIPLY, 234, 172, operatorListener)); + getContentPane().add(CalculatorButtonFactory.createOperationButton( + Operation.DIVIDE, 234, 222, operatorListener)); + + // 特殊按钮 + getContentPane().add(CalculatorButtonFactory.createSpecialButton( + "=", 298, 222, e -> { + try { + model.calculate(); + updateDisplay(); + } catch (Exception ex) { + log.error("计算错误", ex); + display.setText(ex.getMessage()); + } + })); + + getContentPane().add(CalculatorButtonFactory.createSpecialButton( + "C", 298, 121, e -> { + model.clear(); + updateDisplay(); + })); + + getContentPane().add(CalculatorButtonFactory.createSpecialButton( + ".", 167, 222, e -> { + model.addDecimalPoint(); + updateDisplay(); + })); + + getContentPane().add(CalculatorButtonFactory.createSpecialButton( + "+/-", 101, 222, e -> { + model.negate(); + updateDisplay(); + })); + + getContentPane().add(CalculatorButtonFactory.createSpecialButton( + "←", 298, 172, e -> { + model.backspace(); + updateDisplay(); + })); + } + + private void updateDisplay() { + display.setText(model.getCurrentValue().stripTrailingZeros().toPlainString()); + } + + public static void main(String[] args) { + Calculator calculator = new Calculator(); + calculator.setLocation(200, 150); + calculator.setVisible(true); } - - JButton btnZero = new JButton("0"); - btnZero.setBounds(new Rectangle(startX, startY + deltaH, width, height)); - numberButtons[0] = btnZero; - - //加载数字0-9的监听事件 - bindListener(numberButtons); - configFont(numberButtons); - showButton(numberButtons); - } - - private void initLogicButton() { - txtResult.setEnabled(false); - txtResult.setEditable(false); - txtResult.setHorizontalAlignment(SwingConstants.RIGHT); - txtResult.setBounds(new Rectangle(33, 19, 310, 34)); - - inputCache.setEnabled(false); - inputCache.setEditable(false); - inputCache.setHorizontalAlignment(SwingConstants.RIGHT); - inputCache.setBounds(new Rectangle(27, 19, 310, 8)); - - btnNull.setBounds(new Rectangle(298, 70, width, height)); - btnNull.setFont(new Font("Dialog", Font.PLAIN, 12)); - - btnDecrease.setBounds(new Rectangle(234, 120, width, height)); - - btnBegin.setBounds(new Rectangle(298, 121, width, height)); - - btnBegin.addActionListener(new CalculateBtnBeginActionAdapter(this)); - - btnMultiply.setBounds(new Rectangle(234, 172, width, height)); - - btnCancel.setBounds(new Rectangle(298, 172, width, height)); - btnCancel.setFont(new Font("Dialog", Font.PLAIN, 12)); - - btnCancel.addActionListener(new CalculateBtnCancelActionAdapter(this)); - - configFont(txtResult, btnDecrease, btnBegin, btnMultiply, btnDivide, btnIncrease, btnEqual); - - btnMinus.setBounds(new Rectangle(101, 222, width, height)); - btnMinus.setFont(new Font("Dialog", Font.PLAIN, 10)); - - btnMinus.addActionListener(new CalculateBtnMinusActionAdapter(this)); - btnPoint.setBounds(new Rectangle(167, 222, width, height)); - btnPoint.setFont(new Font("Dialog", Font.PLAIN, 30)); - btnPoint.setHorizontalTextPosition(SwingConstants.CENTER); - - btnPoint.addActionListener(new CalculateBtnPointActionAdapter(this)); - btnDivide.setBounds(new Rectangle(234, 222, width, height)); - - btnEqual.setBounds(new Rectangle(298, 222, width, height)); - - btnEqual.addActionListener(new CalculateBtnEqualActionAdapter(this)); - btnIncrease.setBounds(new Rectangle(234, 70, width, height)); - - //加载加减乘除运算符的监听事件 - btnIncrease.addActionListener(new CalculateBtnIncreaseActionAdapter(this)); - btnDecrease.addActionListener(new CalculateBtnIncreaseActionAdapter(this)); - btnMultiply.addActionListener(new CalculateBtnIncreaseActionAdapter(this)); - btnDivide.addActionListener(new CalculateBtnIncreaseActionAdapter(this)); - - showButton(btnDecrease, btnBegin, btnMultiply, btnCancel, - btnMinus, btnPoint, btnDivide, btnEqual, btnIncrease, btnNull); - - rootPane.add(txtResult); - } - - private void configFont(JComponent... components) { - if (Objects.isNull(components)) { - return; - } - for (JComponent component : components) { - component.setFont(dialogFont); - } - } - - private void bindListener(JButton... buttons) { - if (Objects.isNull(buttons)) { - return; - } - for (JButton button : buttons) { - if (Objects.isNull(button)) { - continue; - } - button.addActionListener(new CalculateBtnZeroActionAdapter(this)); - } - } - - private void showButton(JButton... buttons) { - if (Objects.isNull(buttons)) { - return; - } - if (Objects.isNull(rootPane)) { - return; - } - for (JButton button : buttons) { - if (Objects.isNull(button)) { - continue; - } - - rootPane.add(button); - } - } - - void btnZeroActionPerformed(ActionEvent e) { - if (flag) { //如果刚刚按下了运算符 - txtResult.setText(""); - if (dotFlag) {//判断之前是否输入了点运算符 - txtResult.setText("0." + e.getActionCommand()); - dotFlag = false; - } else { - txtResult.setText(e.getActionCommand()); - } - numFlag = true; - } else { - int num = txtResult.getText().indexOf("."); - if (num < 0 && !txtResult.getText().equals("0")) { - txtResult.setText(txtResult.getText() + e.getActionCommand()); - } else if (num < 0 && txtResult.getText().equals("0")) { - txtResult.setText(e.getActionCommand()); - } else if (num >= 0 && txtResult.getText().equals("0")) { - txtResult.setText("0." + e.getActionCommand()); - } else if (num >= 0 && !txtResult.getText().equals("0")) { - txtResult.setText(txtResult.getText() + e.getActionCommand()); - } - } - flag = false; - calculateFlag = false; - } - - void btnIncreaseActionPerformed(ActionEvent e) { - if (calculateFlag) { - txtResult.setText(txtResult.getText()); - operator = e.getActionCommand(); //得到刚刚按下的运算符 - front = txtResult.getText(); //记录加减乘除运算符之前输入的内容 - } else if (numFlag) { -// ActionEvent ee = new ActionEvent("qq", 1, "pp"); - btnEqualActionPerformed(); - operator = e.getActionCommand(); //得到刚刚按下的运算符 - front = result; - numFlag = false; - } else { - front = txtResult.getText(); //记录加减乘除运算符之前输入的内容 - operator = e.getActionCommand(); //得到刚刚按下的运算符 - } - calculateFlag = false; - flag = true; //记录已经按下了加减乘除运算符的其中一个 - } - - void btnEqualActionPerformed() { - if (!calculateFlag) { //未曾按下等于运算符 - behind = txtResult.getText(); - } else { - front = result; - } - try { - if (Objects.isNull(front) || front.isEmpty() || Objects.isNull(behind) || behind.isEmpty()) { - return; - } - double a1 = Double.parseDouble(front); - double b1 = Double.parseDouble(behind); - double result; - if (Objects.equals(operator, "+")) { - result = a1 + b1; - } else if (Objects.equals(operator, "-")) { - result = a1 - b1; - } else if (Objects.equals(operator, "*")) { - result = a1 * b1; - } else { - result = a1 / b1; - } - this.result = Double.toString(result); - txtResult.setText(this.result); - } catch (ArithmeticException ce) { - txtResult.setText("除数不能为零"); - } - if (!calculateFlag) { - calculateFlag = true; - } - } - - void btnPointActionPerformed(ActionEvent e) { - int num = txtResult.getText().indexOf("."); - if (num < 0 && !flag) { - txtResult.setText(txtResult.getText() + e.getActionCommand()); - } - if (flag) { - dotFlag = true; - } - } - - void btnBeginActionPerformed() {//清零运算符事件处理 - flag = false; - dotFlag = false; - numFlag = false; - calculateFlag = false; - front = ""; - behind = ""; - result = ""; - txtResult.setText("0"); - } - - void btnMinusActionPerformed() {//取反运算符事件处理 - if (txtResult.getText().equals("0")) {//如果文本框内容为0 - txtResult.setText(txtResult.getText()); - } else if (txtResult.getText().contains("-")) {//若文本框中含有负号 - String a = txtResult.getText().replaceAll("-", ""); - txtResult.setText(a); - } else if (flag) { - txtResult.setText("0"); - } else { - txtResult.setText("-" + txtResult.getText()); - } - } - - void btnCancel_actionPerformed() {//退格事件处理方法 - String str = txtResult.getText(); - if (str.length() == 1) {//如文本框中只剩下最后一个字符,将文本框内容置为0 - txtResult.setText("0"); - } - if (str.length() > 1) { - str = str.substring(0, str.length() - 1); - txtResult.setText(str); - } - } - - public static void main(String[] args) { - Calculator calculator = new Calculator(); - calculator.setSize(400, 310); - calculator.setLocation(200, 150); - calculator.setVisible(true); - } } diff --git a/gui/src/main/java/com/github/kuangcp/caculator/CalculatorButtonFactory.java b/gui/src/main/java/com/github/kuangcp/caculator/CalculatorButtonFactory.java new file mode 100644 index 00000000..785e2a0a --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/caculator/CalculatorButtonFactory.java @@ -0,0 +1,39 @@ +package com.github.kuangcp.caculator; + +import java.awt.Dimension; +import java.awt.Font; +import java.awt.Rectangle; +import java.awt.event.ActionListener; +import javax.swing.JButton; + +public class CalculatorButtonFactory { + private static final Font STANDARD_FONT = new Font("Dialog", Font.PLAIN, 16); + private static final Dimension BUTTON_SIZE = new Dimension(47, 38); + + public static JButton createNumberButton(String number, int x, int y, ActionListener listener) { + JButton button = new JButton(number); + configureButton(button, x, y); + button.addActionListener(listener); + return button; + } + + public static JButton createOperationButton(Operation op, int x, int y, ActionListener listener) { + JButton button = new JButton(op.getSymbol()); + configureButton(button, x, y); + button.addActionListener(listener); + return button; + } + + public static JButton createSpecialButton(String text, int x, int y, ActionListener listener) { + JButton button = new JButton(text); + configureButton(button, x, y); + button.addActionListener(listener); + return button; + } + + private static void configureButton(JButton button, int x, int y) { + button.setFont(STANDARD_FONT); + button.setPreferredSize(BUTTON_SIZE); + button.setBounds(new Rectangle(x, y, BUTTON_SIZE.width, BUTTON_SIZE.height)); + } +} \ No newline at end of file diff --git a/gui/src/main/java/com/github/kuangcp/caculator/CalculatorException.java b/gui/src/main/java/com/github/kuangcp/caculator/CalculatorException.java new file mode 100644 index 00000000..26dcfce8 --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/caculator/CalculatorException.java @@ -0,0 +1,11 @@ +package com.github.kuangcp.caculator; + +public class CalculatorException extends RuntimeException { + public CalculatorException(String message) { + super(message); + } + + public CalculatorException(String message, Throwable cause) { + super(message, cause); + } +} \ No newline at end of file diff --git a/gui/src/main/java/com/github/kuangcp/caculator/CalculatorModel.java b/gui/src/main/java/com/github/kuangcp/caculator/CalculatorModel.java new file mode 100644 index 00000000..b6387001 --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/caculator/CalculatorModel.java @@ -0,0 +1,102 @@ +package com.github.kuangcp.caculator; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import lombok.Getter; + +public class CalculatorModel { + private static final int SCALE = 10; + private static final RoundingMode ROUNDING_MODE = RoundingMode.HALF_UP; + + @Getter + private BigDecimal currentValue = BigDecimal.ZERO; + private BigDecimal storedValue = null; + private Operation currentOperation = null; + private boolean newNumber = true; + private boolean hasDecimalPoint = false; + + public void setNumber(String num) { + if (newNumber) { + currentValue = new BigDecimal(num); + newNumber = false; + } else { + String current = currentValue.toString(); + if (hasDecimalPoint) { + current = current + num; + } else { + current = current + num; + } + currentValue = new BigDecimal(current); + } + } + + public void setOperation(Operation op) { + if (storedValue == null) { + storedValue = currentValue; + } else if (currentOperation != null) { + storedValue = calculate(); + } + currentOperation = op; + newNumber = true; + hasDecimalPoint = false; + } + + public void addDecimalPoint() { + if (!hasDecimalPoint) { + hasDecimalPoint = true; + if (newNumber) { + currentValue = BigDecimal.ZERO; + newNumber = false; + } + } + } + + public void negate() { + currentValue = currentValue.negate(); + } + + public void clear() { + currentValue = BigDecimal.ZERO; + storedValue = null; + currentOperation = null; + newNumber = true; + hasDecimalPoint = false; + } + + public BigDecimal calculate() { + if (currentOperation == null || storedValue == null) { + return currentValue; + } + + try { + BigDecimal result = currentOperation.apply(storedValue, currentValue); + result = result.setScale(SCALE, ROUNDING_MODE); + currentValue = result; + storedValue = null; + currentOperation = null; + newNumber = true; + hasDecimalPoint = false; + return result; + } catch (ArithmeticException e) { + throw new CalculatorException("计算错误: " + e.getMessage()); + } + } + + public void backspace() { + if (newNumber) { + return; + } + String current = currentValue.toString(); + if (current.length() <= 1) { + currentValue = BigDecimal.ZERO; + newNumber = true; + } else { + current = current.substring(0, current.length() - 1); + if (current.endsWith(".")) { + current = current.substring(0, current.length() - 1); + hasDecimalPoint = false; + } + currentValue = new BigDecimal(current); + } + } +} \ No newline at end of file diff --git a/gui/src/main/java/com/github/kuangcp/caculator/Operation.java b/gui/src/main/java/com/github/kuangcp/caculator/Operation.java new file mode 100644 index 00000000..9517c74f --- /dev/null +++ b/gui/src/main/java/com/github/kuangcp/caculator/Operation.java @@ -0,0 +1,54 @@ +package com.github.kuangcp.caculator; + +import java.math.BigDecimal; + +public enum Operation { + ADD("+") { + @Override + BigDecimal apply(BigDecimal x, BigDecimal y) { + return x.add(y); + } + }, + SUBTRACT("-") { + @Override + BigDecimal apply(BigDecimal x, BigDecimal y) { + return x.subtract(y); + } + }, + MULTIPLY("*") { + @Override + BigDecimal apply(BigDecimal x, BigDecimal y) { + return x.multiply(y); + } + }, + DIVIDE("/") { + @Override + BigDecimal apply(BigDecimal x, BigDecimal y) { + if (y.compareTo(BigDecimal.ZERO) == 0) { + throw new CalculatorException("除数不能为零"); + } + return x.divide(y, 10, BigDecimal.ROUND_HALF_UP); + } + }; + + private final String symbol; + + Operation(String symbol) { + this.symbol = symbol; + } + + public String getSymbol() { + return symbol; + } + + abstract BigDecimal apply(BigDecimal x, BigDecimal y); + + public static Operation fromSymbol(String symbol) { + for (Operation op : values()) { + if (op.symbol.equals(symbol)) { + return op; + } + } + throw new CalculatorException("未知的运算符: " + symbol); + } +} \ No newline at end of file From e1bca7362178791e537612d69699b68db72d53be Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Mon, 23 Jun 2025 21:34:38 +0800 Subject: [PATCH 463/476] fmt --- class/src/test/java/time/InstantTest.java | 70 +++++++++++ class/src/test/java/time/LocalDateTest.java | 90 ++++++++++++++ .../kuangcp/validation/ActiveState.java | 4 +- .../validation/EnumContainValidator.java | 112 +++++++++--------- .../kuangcp/validation/ErrorMsgConstant.java | 4 +- .../github/kuangcp/validation/StrState.java | 4 +- .../github/kuangcp/validation/TestParam.java | 8 +- 7 files changed, 226 insertions(+), 66 deletions(-) diff --git a/class/src/test/java/time/InstantTest.java b/class/src/test/java/time/InstantTest.java index 64d2c61e..459b47f8 100644 --- a/class/src/test/java/time/InstantTest.java +++ b/class/src/test/java/time/InstantTest.java @@ -2,8 +2,11 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.IsEqual.equalTo; +import static org.junit.Assert.*; import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.time.Duration; import lombok.extern.slf4j.Slf4j; import org.junit.Test; @@ -24,4 +27,71 @@ public void testCurrentTimeMillis() { assertThat(epochMilli, equalTo(now)); } + @Test + public void testCreateInstant() { + // 从纪元时间创建 + Instant epoch = Instant.EPOCH; // 1970-01-01T00:00:00Z + assertEquals(0, epoch.getEpochSecond()); + + // 从秒创建 + Instant fromSeconds = Instant.ofEpochSecond(1000); + assertEquals(1000, fromSeconds.getEpochSecond()); + + // 从毫秒创建 + Instant fromMillis = Instant.ofEpochMilli(1000); + assertEquals(1, fromMillis.getEpochSecond()); + + // 解析字符串 + Instant parsed = Instant.parse("2023-01-01T00:00:00Z"); + assertTrue(parsed.toString().startsWith("2023")); + } + + @Test + public void testCompareInstants() { + Instant now = Instant.now(); + Instant future = now.plusSeconds(10); + Instant past = now.minusSeconds(10); + + assertTrue(now.isBefore(future)); + assertTrue(now.isAfter(past)); + assertTrue(now.equals(now)); + + // 比较两个时间点的差异 + Duration duration = Duration.between(past, future); + assertEquals(20, duration.getSeconds()); + } + + @Test + public void testModifyInstant() { + Instant base = Instant.parse("2023-01-01T00:00:00Z"); + + // 增加时间 + Instant plus1Hour = base.plus(1, ChronoUnit.HOURS); + assertEquals("2023-01-01T01:00:00Z", plus1Hour.toString()); + + // 减少时间 + Instant minus30Mins = base.minus(30, ChronoUnit.MINUTES); + assertEquals("2022-12-31T23:30:00Z", minus30Mins.toString()); + + // 使用Duration进行修改 + Instant plusDuration = base.plus(Duration.ofDays(1)); + assertEquals("2023-01-02T00:00:00Z", plusDuration.toString()); + } + + @Test + public void testTruncateInstant() { + Instant now = Instant.parse("2023-01-01T12:34:56.789Z"); + + // 截断到秒 + Instant truncatedToSeconds = now.truncatedTo(ChronoUnit.SECONDS); + assertEquals("2023-01-01T12:34:56Z", truncatedToSeconds.toString()); + + // 截断到分钟 + Instant truncatedToMinutes = now.truncatedTo(ChronoUnit.MINUTES); + assertEquals("2023-01-01T12:34:00Z", truncatedToMinutes.toString()); + + // 截断到小时 + Instant truncatedToHours = now.truncatedTo(ChronoUnit.HOURS); + assertEquals("2023-01-01T12:00:00Z", truncatedToHours.toString()); + } } diff --git a/class/src/test/java/time/LocalDateTest.java b/class/src/test/java/time/LocalDateTest.java index ae1833cb..265956fc 100644 --- a/class/src/test/java/time/LocalDateTest.java +++ b/class/src/test/java/time/LocalDateTest.java @@ -7,10 +7,14 @@ import java.text.SimpleDateFormat; import java.time.LocalDate; import java.time.LocalDateTime; +import java.time.Period; +import java.time.DayOfWeek; +import java.time.temporal.TemporalAdjusters; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatterBuilder; import java.time.format.DateTimeParseException; import java.time.temporal.ChronoField; +import java.time.temporal.ChronoUnit; import java.time.temporal.TemporalAccessor; import java.util.Date; @@ -99,4 +103,90 @@ public void testParseYearDay() throws Exception { LocalDate parse = LocalDate.parse("2017-21", format); System.out.println(parse); } + + @Test + public void testDateCalculation() { + LocalDate today = LocalDate.of(2024, 3, 15); + + // 日期加减 + LocalDate tomorrow = today.plusDays(1); + Assert.assertEquals("2024-03-16", tomorrow.toString()); + + LocalDate nextWeek = today.plusWeeks(1); + Assert.assertEquals("2024-03-22", nextWeek.toString()); + + LocalDate lastMonth = today.minusMonths(1); + Assert.assertEquals("2024-02-15", lastMonth.toString()); + + LocalDate nextYear = today.plusYears(1); + Assert.assertEquals("2025-03-15", nextYear.toString()); + } + + @Test + public void testDateComparison() { + LocalDate date1 = LocalDate.of(2024, 3, 15); + LocalDate date2 = LocalDate.of(2024, 3, 16); + + Assert.assertTrue(date1.isBefore(date2)); + Assert.assertTrue(date2.isAfter(date1)); + Assert.assertFalse(date1.isEqual(date2)); + + // 计算日期间隔 + Period period = Period.between(date1, date2); + Assert.assertEquals(1, period.getDays()); + + long daysBetween = ChronoUnit.DAYS.between(date1, date2); + Assert.assertEquals(1, daysBetween); + } + + @Test + public void testSpecialDates() { + LocalDate date = LocalDate.of(2024, 3, 15); + + // 获取当月第一天 + LocalDate firstDayOfMonth = date.with(TemporalAdjusters.firstDayOfMonth()); + Assert.assertEquals("2024-03-01", firstDayOfMonth.toString()); + + // 获取当月最后一天 + LocalDate lastDayOfMonth = date.with(TemporalAdjusters.lastDayOfMonth()); + Assert.assertEquals("2024-03-31", lastDayOfMonth.toString()); + + // 获取下一个周一 + LocalDate nextMonday = date.with(TemporalAdjusters.next(DayOfWeek.MONDAY)); + Assert.assertEquals("2024-03-18", nextMonday.toString()); + + // 获取本月第一个周日 + LocalDate firstSundayOfMonth = date.with(TemporalAdjusters.firstInMonth(DayOfWeek.SUNDAY)); + Assert.assertEquals("2024-03-03", firstSundayOfMonth.toString()); + } + + @Test + public void testDateProperties() { + LocalDate date = LocalDate.of(2024, 3, 15); + + Assert.assertEquals(2024, date.getYear()); + Assert.assertEquals(3, date.getMonthValue()); + Assert.assertEquals(15, date.getDayOfMonth()); + Assert.assertEquals(DayOfWeek.FRIDAY, date.getDayOfWeek()); + Assert.assertEquals(75, date.getDayOfYear()); + + Assert.assertTrue(date.isLeapYear()); // 2024是闰年 + Assert.assertEquals(31, date.lengthOfMonth()); + Assert.assertEquals(366, date.lengthOfYear()); // 闰年 + } + + @Test + public void testDateCreation() { + // 从年月日创建 + LocalDate date1 = LocalDate.of(2024, 3, 15); + Assert.assertEquals("2024-03-15", date1.toString()); + + // 从纪元日创建 + LocalDate date2 = LocalDate.ofEpochDay(19797); // 2024-03-15 + Assert.assertEquals("2024-03-15", date2.toString()); + + // 从年份中的日子创建 + LocalDate date3 = LocalDate.ofYearDay(2024, 75); // 2024年第75天 + Assert.assertEquals("2024-03-15", date3.toString()); + } } diff --git a/web/src/main/java/com/github/kuangcp/validation/ActiveState.java b/web/src/main/java/com/github/kuangcp/validation/ActiveState.java index 88502acc..5e1f9b19 100644 --- a/web/src/main/java/com/github/kuangcp/validation/ActiveState.java +++ b/web/src/main/java/com/github/kuangcp/validation/ActiveState.java @@ -5,8 +5,8 @@ */ public interface ActiveState { - int ACTIVE = 1; + int ACTIVE = 1; - int NONE = 0; + int NONE = 0; } diff --git a/web/src/main/java/com/github/kuangcp/validation/EnumContainValidator.java b/web/src/main/java/com/github/kuangcp/validation/EnumContainValidator.java index 6dd61837..bcf4846b 100644 --- a/web/src/main/java/com/github/kuangcp/validation/EnumContainValidator.java +++ b/web/src/main/java/com/github/kuangcp/validation/EnumContainValidator.java @@ -12,71 +12,71 @@ @Slf4j public class EnumContainValidator implements ConstraintValidator { - private Class enumClass; + private Class enumClass; - @Override - public void initialize(Contain constraintAnnotation) { - this.enumClass = constraintAnnotation.value(); - } - - @Override - public boolean isValid(Object judgeValue, ConstraintValidatorContext constraintValidatorContext) { - if (Objects.isNull(judgeValue)) { - return false; - } - try { - if (!enumClass.isInterface()) { - log.warn("只能使用接口对入参进行约束: class:{}", enumClass.getSimpleName()); - return false; - } - - // TODO 实际使用需要缓存反射的字段 - return Arrays.stream(enumClass.getFields()) - .filter(v -> Modifier.isFinal(v.getModifiers()) && Modifier.isStatic(v.getModifiers())) - .anyMatch(v -> Objects.equals(getStaticFieldValue(v), judgeValue)); - } catch (Exception e) { - log.error("", e); + @Override + public void initialize(Contain constraintAnnotation) { + this.enumClass = constraintAnnotation.value(); } - return false; - } + @Override + public boolean isValid(Object judgeValue, ConstraintValidatorContext constraintValidatorContext) { + if (Objects.isNull(judgeValue)) { + return false; + } + try { + if (!enumClass.isInterface()) { + log.warn("只能使用接口对入参进行约束: class:{}", enumClass.getSimpleName()); + return false; + } - public static Object getStaticFieldValue(Field field) { - return getFieldValue(null, field); - } + // TODO 实际使用需要缓存反射的字段 + return Arrays.stream(enumClass.getFields()) + .filter(v -> Modifier.isFinal(v.getModifiers()) && Modifier.isStatic(v.getModifiers())) + .anyMatch(v -> Objects.equals(getStaticFieldValue(v), judgeValue)); + } catch (Exception e) { + log.error("", e); + } - public static Object getFieldValue(Object obj, Field field) { - if (null == field) { - return null; + return false; } - if (obj instanceof Class) { - // 静态字段获取时对象为null - obj = null; + + public static Object getStaticFieldValue(Field field) { + return getFieldValue(null, field); } - setAccessible(field); - Object result; - try { - result = field.get(obj); - } catch (IllegalAccessException e) { - throw new RuntimeException("IllegalAccess for " + field.getDeclaringClass() + field.getName(), - e); + public static Object getFieldValue(Object obj, Field field) { + if (null == field) { + return null; + } + if (obj instanceof Class) { + // 静态字段获取时对象为null + obj = null; + } + + setAccessible(field); + Object result; + try { + result = field.get(obj); + } catch (IllegalAccessException e) { + throw new RuntimeException("IllegalAccess for " + field.getDeclaringClass() + field.getName(), + e); + } + return result; } - return result; - } - /** - * 设置方法为可访问(私有方法可以被外部调用) - * - * @param AccessibleObject的子类,比如Class、Method、Field等 - * @param accessibleObject 可设置访问权限的对象,比如Class、Method、Field等 - * @return 被设置可访问的对象 - * @since 4.6.8 - */ - public static T setAccessible(T accessibleObject) { - if (null != accessibleObject && !accessibleObject.isAccessible()) { - accessibleObject.setAccessible(true); + /** + * 设置方法为可访问(私有方法可以被外部调用) + * + * @param AccessibleObject的子类,比如Class、Method、Field等 + * @param accessibleObject 可设置访问权限的对象,比如Class、Method、Field等 + * @return 被设置可访问的对象 + * @since 4.6.8 + */ + public static T setAccessible(T accessibleObject) { + if (null != accessibleObject && !accessibleObject.isAccessible()) { + accessibleObject.setAccessible(true); + } + return accessibleObject; } - return accessibleObject; - } } diff --git a/web/src/main/java/com/github/kuangcp/validation/ErrorMsgConstant.java b/web/src/main/java/com/github/kuangcp/validation/ErrorMsgConstant.java index d0c51128..42731e4b 100644 --- a/web/src/main/java/com/github/kuangcp/validation/ErrorMsgConstant.java +++ b/web/src/main/java/com/github/kuangcp/validation/ErrorMsgConstant.java @@ -5,7 +5,7 @@ */ public interface ErrorMsgConstant { - String STATE_ERROR = "状态不合法"; + String STATE_ERROR = "状态不合法"; - String STR_STATE_ERROR = "string状态不合法"; + String STR_STATE_ERROR = "string状态不合法"; } diff --git a/web/src/main/java/com/github/kuangcp/validation/StrState.java b/web/src/main/java/com/github/kuangcp/validation/StrState.java index c81dfb7e..4014f7f8 100644 --- a/web/src/main/java/com/github/kuangcp/validation/StrState.java +++ b/web/src/main/java/com/github/kuangcp/validation/StrState.java @@ -2,8 +2,8 @@ public interface StrState { - String NONE = null; + String NONE = null; - String SECOND = "2"; + String SECOND = "2"; } diff --git a/web/src/main/java/com/github/kuangcp/validation/TestParam.java b/web/src/main/java/com/github/kuangcp/validation/TestParam.java index 5ee0fcb9..336c438a 100644 --- a/web/src/main/java/com/github/kuangcp/validation/TestParam.java +++ b/web/src/main/java/com/github/kuangcp/validation/TestParam.java @@ -9,9 +9,9 @@ @Data class TestParam { - @Contain(value = ActiveState.class, message = ErrorMsgConstant.STATE_ERROR) - private Integer state; + @Contain(value = ActiveState.class, message = ErrorMsgConstant.STATE_ERROR) + private Integer state; - @Contain(value = StrState.class, message = ErrorMsgConstant.STR_STATE_ERROR) - private String str; + @Contain(value = StrState.class, message = ErrorMsgConstant.STR_STATE_ERROR) + private String str; } From b9cbf32d10eedd3f3e8563fee0ea8e695a169877 Mon Sep 17 00:00:00 2001 From: Kcp Date: Fri, 25 Jul 2025 10:51:34 +0800 Subject: [PATCH 464/476] vector --- .../kuangcp/vector/AdvancedVectorMatcher.java | 251 ++++++++++++++++ .../java/com/github/kuangcp/vector/Demo.java | 138 +++++++++ .../github/kuangcp/vector/MatchResult.java | 66 +++++ .../java/com/github/kuangcp/vector/README.md | 269 ++++++++++++++++++ .../kuangcp/vector/SimilarityCalculator.java | 165 +++++++++++ .../github/kuangcp/vector/VectorMatcher.java | 241 ++++++++++++++++ .../kuangcp/vector/VectorMatchingExample.java | 264 +++++++++++++++++ .../com/github/kuangcp/vector/Vectorizer.java | 184 ++++++++++++ .../com/github/kuangcp/vector/VectorTest.java | 229 +++++++++++++++ .../test/java/security/aes/AESUtilTest.java | 2 +- 10 files changed, 1808 insertions(+), 1 deletion(-) create mode 100644 algorithms/src/main/java/com/github/kuangcp/vector/AdvancedVectorMatcher.java create mode 100644 algorithms/src/main/java/com/github/kuangcp/vector/Demo.java create mode 100644 algorithms/src/main/java/com/github/kuangcp/vector/MatchResult.java create mode 100644 algorithms/src/main/java/com/github/kuangcp/vector/README.md create mode 100644 algorithms/src/main/java/com/github/kuangcp/vector/SimilarityCalculator.java create mode 100644 algorithms/src/main/java/com/github/kuangcp/vector/VectorMatcher.java create mode 100644 algorithms/src/main/java/com/github/kuangcp/vector/VectorMatchingExample.java create mode 100644 algorithms/src/main/java/com/github/kuangcp/vector/Vectorizer.java create mode 100644 algorithms/src/test/java/com/github/kuangcp/vector/VectorTest.java diff --git a/algorithms/src/main/java/com/github/kuangcp/vector/AdvancedVectorMatcher.java b/algorithms/src/main/java/com/github/kuangcp/vector/AdvancedVectorMatcher.java new file mode 100644 index 00000000..79ff60f7 --- /dev/null +++ b/algorithms/src/main/java/com/github/kuangcp/vector/AdvancedVectorMatcher.java @@ -0,0 +1,251 @@ +package com.github.kuangcp.vector; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; + +/** + * 高级向量匹配器 + * 支持多种匹配策略、缓存优化、并行计算等 + */ +public class AdvancedVectorMatcher { + + private final VectorMatcher[] matchers; + private final Map cachedVectors; + private final List terms; + private final MatchingStrategy strategy; + private boolean isInitialized = false; + + public AdvancedVectorMatcher(MatchingStrategy strategy) { + this.strategy = strategy; + this.matchers = new VectorMatcher[Vectorizer.VectorizationStrategy.values().length]; + this.cachedVectors = new ConcurrentHashMap<>(); + this.terms = new ArrayList<>(); + + // 初始化所有向量化策略的匹配器 + int i = 0; + for (Vectorizer.VectorizationStrategy vs : Vectorizer.VectorizationStrategy.values()) { + matchers[i++] = new VectorMatcher(vs); + } + } + + /** + * 初始化匹配器 + * @param terms 专业术语列表 + */ + public void initialize(List terms) { + if (terms == null || terms.isEmpty()) { + throw new IllegalArgumentException("术语列表不能为空"); + } + + this.terms.clear(); + this.terms.addAll(terms); + this.cachedVectors.clear(); + + // 初始化所有匹配器 + for (VectorMatcher matcher : matchers) { + matcher.initialize(terms); + } + + isInitialized = true; + } + + /** + * 查找相似术语(使用多种策略) + * @param input 输入字符串 + * @param topK 返回前K个结果 + * @param minSimilarity 最小相似度阈值 + * @return 匹配结果列表 + */ + public List findSimilarTerms(String input, int topK, double minSimilarity) { + if (!isInitialized) { + throw new IllegalStateException("匹配器尚未初始化"); + } + + if (input == null || input.trim().isEmpty()) { + return Collections.emptyList(); + } + + switch (strategy) { + case ENSEMBLE: + return findSimilarTermsEnsemble(input, topK, minSimilarity); + case BEST_STRATEGY: + return findSimilarTermsBestStrategy(input, topK, minSimilarity); + case WEIGHTED_ENSEMBLE: + return findSimilarTermsWeightedEnsemble(input, topK, minSimilarity); + default: + return matchers[0].findSimilarTerms(input, topK, minSimilarity); + } + } + + /** + * 集成学习策略:综合多种向量化方法的结果 + */ + private List findSimilarTermsEnsemble(String input, int topK, double minSimilarity) { + Map> termScores = new HashMap<>(); + + // 收集所有匹配器的结果 + for (VectorMatcher matcher : matchers) { + List results = matcher.findSimilarTerms(input, topK * 2, minSimilarity); + for (MatchResult result : results) { + termScores.computeIfAbsent(result.getTerm(), k -> new ArrayList<>()) + .add(result.getSimilarity()); + } + } + + // 计算平均分数 + List ensembleResults = new ArrayList<>(); + for (Map.Entry> entry : termScores.entrySet()) { + double avgScore = entry.getValue().stream() + .mapToDouble(Double::doubleValue) + .average() + .orElse(0.0); + + if (avgScore >= minSimilarity) { + ensembleResults.add(new MatchResult(entry.getKey(), avgScore)); + } + } + + return ensembleResults.stream() + .sorted() + .limit(topK) + .collect(Collectors.toList()); + } + + /** + * 最佳策略:选择表现最好的向量化方法 + */ + private List findSimilarTermsBestStrategy(String input, int topK, double minSimilarity) { + List bestResults = Collections.emptyList(); + double bestScore = 0.0; + + for (VectorMatcher matcher : matchers) { + List results = matcher.findSimilarTerms(input, topK, minSimilarity); + if (!results.isEmpty()) { + double avgScore = results.stream() + .mapToDouble(MatchResult::getSimilarity) + .average() + .orElse(0.0); + + if (avgScore > bestScore) { + bestScore = avgScore; + bestResults = results; + } + } + } + + return bestResults; + } + + /** + * 加权集成:根据策略的权重综合结果 + */ + private List findSimilarTermsWeightedEnsemble(String input, int topK, double minSimilarity) { + Map termScores = new HashMap<>(); + double[] weights = {0.3, 0.2, 0.3, 0.2}; // TF-IDF, CharFreq, WordFreq, SimpleHash + + // 收集所有匹配器的结果并加权 + for (int i = 0; i < matchers.length; i++) { + List results = matchers[i].findSimilarTerms(input, topK * 2, minSimilarity); + for (MatchResult result : results) { + termScores.merge(result.getTerm(), + result.getSimilarity() * weights[i], + Double::sum); + } + } + + // 转换为结果列表 + List weightedResults = termScores.entrySet().stream() + .filter(entry -> entry.getValue() >= minSimilarity) + .map(entry -> new MatchResult(entry.getKey(), entry.getValue())) + .sorted() + .limit(topK) + .collect(Collectors.toList()); + + return weightedResults; + } + + /** + * 批量查找相似术语 + * @param inputs 输入字符串列表 + * @param topK 每个输入返回前K个结果 + * @param minSimilarity 最小相似度阈值 + * @return 每个输入对应的匹配结果 + */ + public Map> batchFindSimilarTerms(List inputs, int topK, double minSimilarity) { + Map> results = new HashMap<>(); + + // 并行处理批量查询 + inputs.parallelStream().forEach(input -> { + List matches = findSimilarTerms(input, topK, minSimilarity); + synchronized (results) { + results.put(input, matches); + } + }); + + return results; + } + + /** + * 获取指定策略的匹配器 + * @param strategy 向量化策略 + * @return 对应的匹配器 + */ + public VectorMatcher getMatcher(Vectorizer.VectorizationStrategy strategy) { + int index = strategy.ordinal(); + return matchers[index]; + } + + /** + * 获取所有术语 + * @return 术语列表 + */ + public List getAllTerms() { + return new ArrayList<>(terms); + } + + /** + * 获取术语数量 + * @return 术语数量 + */ + public int getTermCount() { + return terms.size(); + } + + /** + * 检查是否已初始化 + * @return 是否已初始化 + */ + public boolean isInitialized() { + return isInitialized; + } + + /** + * 清除所有数据 + */ + public void clear() { + terms.clear(); + cachedVectors.clear(); + for (VectorMatcher matcher : matchers) { + matcher.clear(); + } + isInitialized = false; + } + + /** + * 获取匹配策略 + * @return 当前使用的匹配策略 + */ + public MatchingStrategy getStrategy() { + return strategy; + } + + /** + * 匹配策略枚举 + */ + public enum MatchingStrategy { + ENSEMBLE, // 集成学习 + BEST_STRATEGY, // 最佳策略 + WEIGHTED_ENSEMBLE // 加权集成 + } +} \ No newline at end of file diff --git a/algorithms/src/main/java/com/github/kuangcp/vector/Demo.java b/algorithms/src/main/java/com/github/kuangcp/vector/Demo.java new file mode 100644 index 00000000..649517fa --- /dev/null +++ b/algorithms/src/main/java/com/github/kuangcp/vector/Demo.java @@ -0,0 +1,138 @@ +package com.github.kuangcp.vector; + +import java.util.Arrays; +import java.util.List; + +/** + * 向量匹配系统演示程序 + */ +public class Demo { + + public static void main(String[] args) { + System.out.println("=== Java向量匹配系统演示 ===\n"); + + // 1. 准备专业术语数据 + List technicalTerms = Arrays.asList( + "机器学习", "深度学习", "神经网络", "卷积神经网络", "循环神经网络", + "自然语言处理", "计算机视觉", "强化学习", "监督学习", "无监督学习", + "半监督学习", "迁移学习", "集成学习", "随机森林", "支持向量机", + "决策树", "逻辑回归", "线性回归", "聚类算法", "降维算法", + "特征工程", "数据预处理", "模型评估", "交叉验证", "过拟合", + "欠拟合", "正则化", "梯度下降", "反向传播", "激活函数", + "损失函数", "优化器", "批量归一化", "Dropout", "注意力机制", + "Transformer", "BERT", "GPT", "ResNet", "VGG", "AlexNet", + "YOLO", "R-CNN", "Fast R-CNN", "Faster R-CNN", "Mask R-CNN", + "目标检测", "图像分割", "语义分割", "实例分割", "姿态估计" + ); + + System.out.println("术语库大小: " + technicalTerms.size() + " 个专业术语"); + + // 2. 创建向量匹配器 + System.out.println("\n正在初始化向量匹配器..."); + VectorMatcher matcher = new VectorMatcher(Vectorizer.VectorizationStrategy.TF_IDF); + matcher.initialize(technicalTerms); + System.out.println("初始化完成!向量维度: " + matcher.getVectorDimension()); + + // 3. 演示相似度匹配 + System.out.println("\n=== 相似度匹配演示 ==="); + + String[] testQueries = { + "机器学习算法", + "深度学习模型", + "神经网络训练", + "计算机视觉技术", + "自然语言处理系统" + }; + + for (String query : testQueries) { + System.out.println("\n查询: " + query); + List results = matcher.findSimilarTerms(query, 5, 0.1); + + if (results.isEmpty()) { + System.out.println(" 无匹配结果"); + } else { + System.out.println(" 匹配结果:"); + for (int i = 0; i < results.size(); i++) { + MatchResult result = results.get(i); + System.out.printf(" %d. %s (相似度: %.4f, 置信度: %.2f%%)\n", + i + 1, result.getTerm(), result.getSimilarity(), + result.getConfidence() * 100); + } + } + } + + // 4. 演示高级匹配器 + System.out.println("\n=== 高级匹配器演示 ==="); + AdvancedVectorMatcher advancedMatcher = new AdvancedVectorMatcher( + AdvancedVectorMatcher.MatchingStrategy.WEIGHTED_ENSEMBLE + ); + advancedMatcher.initialize(technicalTerms); + + String advancedQuery = "深度学习网络"; + List advancedResults = advancedMatcher.findSimilarTerms(advancedQuery, 3, 0.2); + + System.out.println("查询: " + advancedQuery); + System.out.println("高级匹配结果:"); + for (int i = 0; i < advancedResults.size(); i++) { + MatchResult result = advancedResults.get(i); + System.out.printf(" %d. %s (置信度: %.2f%%)\n", + i + 1, result.getTerm(), result.getConfidence() * 100); + } + + // 5. 演示不同策略的效果 + System.out.println("\n=== 不同向量化策略对比 ==="); + String compareQuery = "神经网络"; + + Vectorizer.VectorizationStrategy[] strategies = { + Vectorizer.VectorizationStrategy.TF_IDF, + Vectorizer.VectorizationStrategy.CHARACTER_FREQUENCY, + Vectorizer.VectorizationStrategy.WORD_FREQUENCY, + Vectorizer.VectorizationStrategy.SIMPLE_HASH + }; + + for (Vectorizer.VectorizationStrategy strategy : strategies) { + VectorMatcher strategyMatcher = new VectorMatcher(strategy); + strategyMatcher.initialize(technicalTerms); + + List strategyResults = strategyMatcher.findSimilarTerms(compareQuery, 3, 0.1); + + System.out.println("\n策略: " + strategy); + for (int i = 0; i < strategyResults.size(); i++) { + MatchResult result = strategyResults.get(i); + System.out.printf(" %d. %s (相似度: %.4f)\n", + i + 1, result.getTerm(), result.getSimilarity()); + } + } + + // 6. 性能演示 + System.out.println("\n=== 性能演示 ==="); + String performanceQuery = "机器学习算法"; + int iterations = 1000; + + long startTime = System.currentTimeMillis(); + for (int i = 0; i < iterations; i++) { + matcher.findSimilarTerms(performanceQuery, 5, 0.1); + } + long endTime = System.currentTimeMillis(); + + long duration = endTime - startTime; + double avgTime = (double) duration / iterations; + double qps = 1000.0 / avgTime; + + System.out.println("性能测试结果:"); + System.out.println(" 术语数量: " + technicalTerms.size()); + System.out.println(" 测试查询: " + performanceQuery); + System.out.println(" 迭代次数: " + iterations); + System.out.println(" 总耗时: " + duration + "ms"); + System.out.println(" 平均耗时: " + String.format("%.2f", avgTime) + "ms"); + System.out.println(" QPS: " + String.format("%.2f", qps)); + + System.out.println("\n=== 演示完成 ==="); + System.out.println("这个向量匹配系统可以用于:"); + System.out.println("1. 智能客服系统的术语匹配"); + System.out.println("2. 搜索引擎的相关词推荐"); + System.out.println("3. 知识图谱的实体链接"); + System.out.println("4. 文本分类和聚类"); + System.out.println("5. 推荐系统的内容匹配"); + } +} \ No newline at end of file diff --git a/algorithms/src/main/java/com/github/kuangcp/vector/MatchResult.java b/algorithms/src/main/java/com/github/kuangcp/vector/MatchResult.java new file mode 100644 index 00000000..41a8b66d --- /dev/null +++ b/algorithms/src/main/java/com/github/kuangcp/vector/MatchResult.java @@ -0,0 +1,66 @@ +package com.github.kuangcp.vector; + +/** + * 向量匹配结果 + */ +public class MatchResult implements Comparable { + + private final String term; + private final double similarity; + private final double[] vector; + + public MatchResult(String term, double similarity, double[] vector) { + this.term = term; + this.similarity = similarity; + this.vector = vector != null ? vector.clone() : null; + } + + public MatchResult(String term, double similarity) { + this(term, similarity, null); + } + + public String getTerm() { + return term; + } + + public double getSimilarity() { + return similarity; + } + + public double getConfidence() { + return similarity; // 置信度就是相似度 + } + + public double[] getVector() { + return vector != null ? vector.clone() : null; + } + + @Override + public int compareTo(MatchResult other) { + // 按相似度降序排列 + return Double.compare(other.similarity, this.similarity); + } + + @Override + public String toString() { + return String.format("MatchResult{term='%s', similarity=%.4f, confidence=%.2f%%}", + term, similarity, similarity * 100); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null || getClass() != obj.getClass()) return false; + + MatchResult that = (MatchResult) obj; + return Double.compare(that.similarity, similarity) == 0 && + term.equals(that.term); + } + + @Override + public int hashCode() { + int result = term.hashCode(); + result = 31 * result + Double.hashCode(similarity); + return result; + } +} \ No newline at end of file diff --git a/algorithms/src/main/java/com/github/kuangcp/vector/README.md b/algorithms/src/main/java/com/github/kuangcp/vector/README.md new file mode 100644 index 00000000..33161f01 --- /dev/null +++ b/algorithms/src/main/java/com/github/kuangcp/vector/README.md @@ -0,0 +1,269 @@ +# Java向量匹配系统 + +这是一个基于Java实现的多维度向量数据匹配系统,专门用于专业术语的向量化和相似度匹配。 + +## 功能特性 + +- **多种向量化策略**: 支持TF-IDF、字符频率、词频、简单哈希等多种向量化方法 +- **余弦相似度计算**: 使用余弦相似度进行向量匹配 +- **高级匹配策略**: 支持集成学习、最佳策略、加权集成等高级匹配方法 +- **高性能**: 支持批量查询和并行处理 +- **易于使用**: 提供简洁的API接口 +- **可扩展**: 支持自定义向量化策略和相似度计算方法 + +## 核心组件 + +### 1. Vectorizer (向量化工具类) +负责将文本转换为向量表示,支持多种向量化策略: + +- `TF_IDF`: 基于词频-逆文档频率的向量化 +- `CHARACTER_FREQUENCY`: 基于字符频率的向量化 +- `WORD_FREQUENCY`: 基于词频的向量化 +- `SIMPLE_HASH`: 基于简单哈希的向量化 + +### 2. SimilarityCalculator (相似度计算工具类) +提供多种相似度计算方法: + +- 余弦相似度 (cosine similarity) +- 欧几里得距离 (euclidean distance) +- 曼哈顿距离 (manhattan distance) +- 杰卡德相似度 (jaccard similarity) + +### 3. VectorMatcher (基础向量匹配器) +提供基础的向量匹配功能: + +- 术语向量化 +- 相似度计算 +- 结果排序和过滤 + +### 4. AdvancedVectorMatcher (高级向量匹配器) +提供高级匹配功能: + +- 多种匹配策略集成 +- 并行处理 +- 缓存优化 + +### 5. MatchResult (匹配结果类) +封装匹配结果信息: + +- 匹配的术语 +- 相似度分数 +- 置信度 + +## 快速开始 + +### 1. 基础使用 + +```java +// 准备专业术语列表 +List terms = Arrays.asList( + "机器学习", "深度学习", "神经网络", "卷积神经网络", "循环神经网络", + "自然语言处理", "计算机视觉", "强化学习", "监督学习", "无监督学习" +); + +// 创建向量匹配器 +VectorMatcher matcher = new VectorMatcher(Vectorizer.VectorizationStrategy.TF_IDF); + +// 初始化匹配器 +matcher.initialize(terms); + +// 进行相似度匹配 +String query = "机器学习算法"; +List results = matcher.findSimilarTerms(query, 5, 0.1); + +// 输出结果 +for (MatchResult result : results) { + System.out.println(result.getTerm() + " (置信度: " + + String.format("%.2f%%", result.getConfidence() * 100) + ")"); +} +``` + +### 2. 高级使用 + +```java +// 创建高级匹配器 +AdvancedVectorMatcher advancedMatcher = new AdvancedVectorMatcher( + AdvancedVectorMatcher.MatchingStrategy.WEIGHTED_ENSEMBLE +); + +// 初始化 +advancedMatcher.initialize(terms); + +// 批量查询 +List queries = Arrays.asList("深度学习模型", "神经网络训练"); +Map> batchResults = + advancedMatcher.batchFindSimilarTerms(queries, 3, 0.2); + +// 输出批量结果 +batchResults.forEach((query, results) -> { + System.out.println("查询: " + query); + results.forEach(result -> + System.out.println(" " + result.getTerm() + " (置信度: " + + String.format("%.2f%%", result.getConfidence() * 100) + ")") + ); +}); +``` + +### 3. 不同策略对比 + +```java +String query = "神经网络"; + +// 测试不同的向量化策略 +Vectorizer.VectorizationStrategy[] strategies = { + Vectorizer.VectorizationStrategy.TF_IDF, + Vectorizer.VectorizationStrategy.CHARACTER_FREQUENCY, + Vectorizer.VectorizationStrategy.WORD_FREQUENCY, + Vectorizer.VectorizationStrategy.SIMPLE_HASH +}; + +for (Vectorizer.VectorizationStrategy strategy : strategies) { + VectorMatcher matcher = new VectorMatcher(strategy); + matcher.initialize(terms); + + List results = matcher.findSimilarTerms(query, 3, 0.1); + System.out.println("策略: " + strategy); + results.forEach(result -> + System.out.println(" " + result.getTerm() + " (相似度: " + + String.format("%.4f", result.getSimilarity()) + ")") + ); +} +``` + +## 应用场景 + +### 1. 智能客服系统 +- 用户输入问题,系统匹配相关专业术语 +- 提供准确的答案和建议 + +### 2. 搜索引擎 +- 相关词推荐 +- 查询扩展 +- 搜索结果排序 + +### 3. 知识图谱 +- 实体链接 +- 关系抽取 +- 知识推理 + +### 4. 文本分类和聚类 +- 文档相似度计算 +- 主题建模 +- 内容推荐 + +### 5. 推荐系统 +- 内容匹配 +- 用户兴趣建模 +- 协同过滤 + +## 性能优化 + +### 1. 向量维度控制 +- 词汇表大小限制 +- 特征选择 +- 降维处理 + +### 2. 缓存机制 +- 向量缓存 +- 结果缓存 +- 词汇表缓存 + +### 3. 并行处理 +- 批量查询并行化 +- 向量计算并行化 +- 多线程优化 + +### 4. 索引优化 +- 向量索引 +- 相似度索引 +- 快速检索 + +## 配置参数 + +### 向量化参数 +- `vocabularySize`: 词汇表大小 (默认: 1000) +- `minWordLength`: 最小词长度 (默认: 2) +- `vectorDimension`: 向量维度 + +### 匹配参数 +- `topK`: 返回结果数量 +- `minSimilarity`: 最小相似度阈值 +- `matchingStrategy`: 匹配策略 + +### 性能参数 +- `cacheSize`: 缓存大小 +- `threadPoolSize`: 线程池大小 +- `batchSize`: 批处理大小 + +## 扩展开发 + +### 1. 自定义向量化策略 +```java +public class CustomVectorizer extends Vectorizer { + @Override + public double[] vectorize(String text) { + // 实现自定义向量化逻辑 + return customVector; + } +} +``` + +### 2. 自定义相似度计算 +```java +public class CustomSimilarityCalculator { + public static double customSimilarity(double[] vector1, double[] vector2) { + // 实现自定义相似度计算 + return similarity; + } +} +``` + +### 3. 自定义匹配策略 +```java +public class CustomMatcher extends VectorMatcher { + @Override + public List findSimilarTerms(String input, int topK, double minSimilarity) { + // 实现自定义匹配逻辑 + return customResults; + } +} +``` + +## 测试和验证 + +### 运行测试 +```bash +mvn test -Dtest=VectorTest +``` + +### 运行演示 +```bash +mvn exec:java -Dexec.mainClass="com.github.kuangcp.vector.Demo" +``` + +### 性能测试 +```bash +mvn test -Dtest=VectorTest#testPerformance +``` + +## 注意事项 + +1. **内存使用**: 大量术语时注意内存占用 +2. **向量维度**: 高维向量可能影响性能 +3. **相似度阈值**: 合理设置阈值避免过多无关结果 +4. **词汇表更新**: 动态添加术语时需要重新构建词汇表 +5. **线程安全**: 多线程环境下注意并发安全 + +## 依赖要求 + +- Java 8+ +- Maven 3.6+ +- JUnit 4 (测试) + +## 许可证 + +MIT License + +## 贡献 + +欢迎提交Issue和Pull Request来改进这个项目。 \ No newline at end of file diff --git a/algorithms/src/main/java/com/github/kuangcp/vector/SimilarityCalculator.java b/algorithms/src/main/java/com/github/kuangcp/vector/SimilarityCalculator.java new file mode 100644 index 00000000..1b27e918 --- /dev/null +++ b/algorithms/src/main/java/com/github/kuangcp/vector/SimilarityCalculator.java @@ -0,0 +1,165 @@ +package com.github.kuangcp.vector; + +import java.util.Arrays; + +/** + * 向量相似度计算工具类 + */ +public class SimilarityCalculator { + + /** + * 计算余弦相似度 + * @param vector1 向量1 + * @param vector2 向量2 + * @return 余弦相似度值 (0-1之间,1表示完全相同) + */ + public static double cosineSimilarity(double[] vector1, double[] vector2) { + if (vector1 == null || vector2 == null) { + return 0.0; + } + + if (vector1.length != vector2.length) { + throw new IllegalArgumentException("向量维度必须相同"); + } + + double dotProduct = 0.0; + double norm1 = 0.0; + double norm2 = 0.0; + + for (int i = 0; i < vector1.length; i++) { + dotProduct += vector1[i] * vector2[i]; + norm1 += vector1[i] * vector1[i]; + norm2 += vector2[i] * vector2[i]; + } + + if (norm1 == 0.0 || norm2 == 0.0) { + return 0.0; + } + + return dotProduct / (Math.sqrt(norm1) * Math.sqrt(norm2)); + } + + /** + * 计算欧几里得距离 + * @param vector1 向量1 + * @param vector2 向量2 + * @return 欧几里得距离 + */ + public static double euclideanDistance(double[] vector1, double[] vector2) { + if (vector1 == null || vector2 == null) { + return Double.MAX_VALUE; + } + + if (vector1.length != vector2.length) { + throw new IllegalArgumentException("向量维度必须相同"); + } + + double sum = 0.0; + for (int i = 0; i < vector1.length; i++) { + double diff = vector1[i] - vector2[i]; + sum += diff * diff; + } + + return Math.sqrt(sum); + } + + /** + * 计算曼哈顿距离 + * @param vector1 向量1 + * @param vector2 向量2 + * @return 曼哈顿距离 + */ + public static double manhattanDistance(double[] vector1, double[] vector2) { + if (vector1 == null || vector2 == null) { + return Double.MAX_VALUE; + } + + if (vector1.length != vector2.length) { + throw new IllegalArgumentException("向量维度必须相同"); + } + + double sum = 0.0; + for (int i = 0; i < vector1.length; i++) { + sum += Math.abs(vector1[i] - vector2[i]); + } + + return sum; + } + + /** + * 计算杰卡德相似度(用于集合) + * @param set1 集合1 + * @param set2 集合2 + * @return 杰卡德相似度 + */ + public static double jaccardSimilarity(java.util.Set set1, java.util.Set set2) { + if (set1 == null || set2 == null) { + return 0.0; + } + + java.util.Set intersection = new java.util.HashSet<>(set1); + intersection.retainAll(set2); + + java.util.Set union = new java.util.HashSet<>(set1); + union.addAll(set2); + + if (union.isEmpty()) { + return 0.0; + } + + return (double) intersection.size() / union.size(); + } + + /** + * 将距离转换为相似度分数 + * @param distance 距离值 + * @param maxDistance 最大距离 + * @return 相似度分数 (0-1之间) + */ + public static double distanceToSimilarity(double distance, double maxDistance) { + if (maxDistance <= 0) { + return 0.0; + } + return Math.max(0.0, 1.0 - (distance / maxDistance)); + } + + /** + * 计算向量的模长 + * @param vector 向量 + * @return 模长 + */ + public static double vectorMagnitude(double[] vector) { + if (vector == null) { + return 0.0; + } + + double sum = 0.0; + for (double v : vector) { + sum += v * v; + } + return Math.sqrt(sum); + } + + /** + * 向量归一化 + * @param vector 输入向量 + * @return 归一化后的向量 + */ + public static double[] normalizeVector(double[] vector) { + if (vector == null) { + return null; + } + + double magnitude = vectorMagnitude(vector); + if (magnitude == 0.0) { + return Arrays.copyOf(vector, vector.length); + } + + double[] normalized = new double[vector.length]; + for (int i = 0; i < vector.length; i++) { + normalized[i] = vector[i] / magnitude; + } + + return normalized; + } +} \ No newline at end of file diff --git a/algorithms/src/main/java/com/github/kuangcp/vector/VectorMatcher.java b/algorithms/src/main/java/com/github/kuangcp/vector/VectorMatcher.java new file mode 100644 index 00000000..3387ad8e --- /dev/null +++ b/algorithms/src/main/java/com/github/kuangcp/vector/VectorMatcher.java @@ -0,0 +1,241 @@ +package com.github.kuangcp.vector; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; + +/** + * 向量匹配服务 + * 用于专业术语的向量化和相似度匹配 + */ +public class VectorMatcher { + + private final Vectorizer vectorizer; + private final Map termVectors; + private final List terms; + private boolean isInitialized = false; + + public VectorMatcher(Vectorizer.VectorizationStrategy strategy) { + this.vectorizer = new Vectorizer(strategy); + this.termVectors = new ConcurrentHashMap<>(); + this.terms = new ArrayList<>(); + } + + /** + * 初始化匹配器,添加专业术语并构建向量 + * @param terms 专业术语列表 + */ + public void initialize(List terms) { + if (terms == null || terms.isEmpty()) { + throw new IllegalArgumentException("术语列表不能为空"); + } + + this.terms.clear(); + this.terms.addAll(terms); + this.termVectors.clear(); + + // 构建词汇表 + vectorizer.buildVocabulary(terms); + + // 为每个术语生成向量 + for (String term : terms) { + double[] vector = vectorizer.vectorize(term); + termVectors.put(term, vector); + } + + isInitialized = true; + } + + /** + * 添加单个术语 + * @param term 术语 + */ + public void addTerm(String term) { + if (term == null || term.trim().isEmpty()) { + return; + } + + if (!isInitialized) { + // 如果还没初始化,先初始化 + initialize(Arrays.asList(term)); + return; + } + + if (!terms.contains(term)) { + terms.add(term); + double[] vector = vectorizer.vectorize(term); + termVectors.put(term, vector); + } + } + + /** + * 查找与输入字符串最相似的术语 + * @param input 输入字符串 + * @param topK 返回前K个最相似的结果 + * @param minSimilarity 最小相似度阈值 + * @return 匹配结果列表,按相似度降序排列 + */ + public List findSimilarTerms(String input, int topK, double minSimilarity) { + if (!isInitialized) { + throw new IllegalStateException("匹配器尚未初始化,请先调用initialize方法"); + } + + if (input == null || input.trim().isEmpty()) { + return Collections.emptyList(); + } + + // 将输入字符串向量化 + double[] inputVector = vectorizer.vectorize(input); + + // 计算与所有术语的相似度 + List results = new ArrayList<>(); + + for (String term : terms) { + double[] termVector = termVectors.get(term); + if (termVector != null) { + double similarity = SimilarityCalculator.cosineSimilarity(inputVector, termVector); + + if (similarity >= minSimilarity) { + results.add(new MatchResult(term, similarity, termVector)); + } + } + } + + // 按相似度排序并返回前K个结果 + return results.stream() + .sorted() + .limit(topK) + .collect(Collectors.toList()); + } + + /** + * 查找与输入字符串最相似的术语(使用默认参数) + * @param input 输入字符串 + * @return 匹配结果列表 + */ + public List findSimilarTerms(String input) { + return findSimilarTerms(input, 10, 0.1); + } + + /** + * 查找与输入字符串最相似的术语(指定数量) + * @param input 输入字符串 + * @param topK 返回前K个最相似的结果 + * @return 匹配结果列表 + */ + public List findSimilarTerms(String input, int topK) { + return findSimilarTerms(input, topK, 0.1); + } + + /** + * 批量查找相似术语 + * @param inputs 输入字符串列表 + * @param topK 每个输入返回前K个最相似的结果 + * @param minSimilarity 最小相似度阈值 + * @return 每个输入对应的匹配结果 + */ + public Map> batchFindSimilarTerms(List inputs, int topK, double minSimilarity) { + Map> results = new HashMap<>(); + + for (String input : inputs) { + List matches = findSimilarTerms(input, topK, minSimilarity); + results.put(input, matches); + } + + return results; + } + + /** + * 获取术语的向量表示 + * @param term 术语 + * @return 向量,如果术语不存在则返回null + */ + public double[] getTermVector(String term) { + return termVectors.get(term); + } + + /** + * 获取所有术语的向量表示 + * @return 术语到向量的映射 + */ + public Map getAllTermVectors() { + return new HashMap<>(termVectors); + } + + /** + * 获取所有术语列表 + * @return 术语列表 + */ + public List getAllTerms() { + return new ArrayList<>(terms); + } + + /** + * 获取向量维度 + * @return 向量维度 + */ + public int getVectorDimension() { + return vectorizer.getVectorDimension(); + } + + /** + * 获取词汇表 + * @return 词汇表 + */ + public Map getVocabulary() { + return vectorizer.getVocabulary(); + } + + /** + * 检查是否已初始化 + * @return 是否已初始化 + */ + public boolean isInitialized() { + return isInitialized; + } + + /** + * 获取术语数量 + * @return 术语数量 + */ + public int getTermCount() { + return terms.size(); + } + + /** + * 清除所有数据 + */ + public void clear() { + terms.clear(); + termVectors.clear(); + isInitialized = false; + } + + /** + * 移除指定术语 + * @param term 要移除的术语 + * @return 是否成功移除 + */ + public boolean removeTerm(String term) { + boolean removed = terms.remove(term); + if (removed) { + termVectors.remove(term); + } + return removed; + } + + /** + * 更新术语的向量(当词汇表发生变化时) + */ + public void updateVectors() { + if (!isInitialized) { + return; + } + + termVectors.clear(); + for (String term : terms) { + double[] vector = vectorizer.vectorize(term); + termVectors.put(term, vector); + } + } +} \ No newline at end of file diff --git a/algorithms/src/main/java/com/github/kuangcp/vector/VectorMatchingExample.java b/algorithms/src/main/java/com/github/kuangcp/vector/VectorMatchingExample.java new file mode 100644 index 00000000..36442d03 --- /dev/null +++ b/algorithms/src/main/java/com/github/kuangcp/vector/VectorMatchingExample.java @@ -0,0 +1,264 @@ +package com.github.kuangcp.vector; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * 向量匹配系统使用示例 + * 展示如何在实际项目中使用向量匹配功能 + */ +public class VectorMatchingExample { + + public static void main(String[] args) { + // 示例1:基础使用 + basicUsageExample(); + + System.out.println("\n" + String.join("", Collections.nCopies(50, "=")) + "\n"); + + // 示例2:高级使用 + advancedUsageExample(); + + System.out.println("\n" + String.join("", Collections.nCopies(50, "=")) + "\n"); + + // 示例3:不同策略对比 + strategyComparisonExample(); + + System.out.println("\n" + String.join("", Collections.nCopies(50, "=")) + "\n"); + + // 示例4:实际应用场景 + realWorldExample(); + } + + /** + * 基础使用示例 + */ + public static void basicUsageExample() { + System.out.println("=== 基础使用示例 ==="); + + // 1. 准备专业术语列表 + List terms = Arrays.asList( + "机器学习", "深度学习", "神经网络", "卷积神经网络", "循环神经网络", + "自然语言处理", "计算机视觉", "强化学习", "监督学习", "无监督学习", + "支持向量机", "决策树", "逻辑回归", "线性回归", "聚类算法" + ); + + // 2. 创建向量匹配器 + VectorMatcher matcher = new VectorMatcher(Vectorizer.VectorizationStrategy.TF_IDF); + + // 3. 初始化匹配器 + matcher.initialize(terms); + + // 4. 进行相似度匹配 + String query = "机器学习算法"; + List results = matcher.findSimilarTerms(query, 5, 0.1); + + // 5. 输出结果 + System.out.println("查询: " + query); + System.out.println("匹配结果:"); + for (int i = 0; i < results.size(); i++) { + MatchResult result = results.get(i); + System.out.printf(" %d. %s (相似度: %.4f, 置信度: %.2f%%)\n", + i + 1, result.getTerm(), result.getSimilarity(), + result.getConfidence() * 100); + } + } + + /** + * 高级使用示例 + */ + public static void advancedUsageExample() { + System.out.println("=== 高级使用示例 ==="); + + // 1. 准备更丰富的术语列表 + List terms = Arrays.asList( + "机器学习", "深度学习", "神经网络", "卷积神经网络", "循环神经网络", + "自然语言处理", "计算机视觉", "强化学习", "监督学习", "无监督学习", + "半监督学习", "迁移学习", "集成学习", "随机森林", "支持向量机", + "决策树", "逻辑回归", "线性回归", "聚类算法", "降维算法", + "特征工程", "数据预处理", "模型评估", "交叉验证", "过拟合", + "欠拟合", "正则化", "梯度下降", "反向传播", "激活函数", + "损失函数", "优化器", "批量归一化", "Dropout", "注意力机制", + "Transformer", "BERT", "GPT", "ResNet", "VGG", "AlexNet", + "YOLO", "R-CNN", "Fast R-CNN", "Faster R-CNN", "Mask R-CNN", + "目标检测", "图像分割", "语义分割", "实例分割", "姿态估计" + ); + + // 2. 创建高级匹配器 + AdvancedVectorMatcher matcher = new AdvancedVectorMatcher( + AdvancedVectorMatcher.MatchingStrategy.WEIGHTED_ENSEMBLE + ); + + // 3. 初始化匹配器 + matcher.initialize(terms); + + // 4. 进行批量查询 + List queries = Arrays.asList( + "深度学习模型", "神经网络训练", "计算机视觉技术" + ); + + System.out.println("批量查询结果:"); + for (String query : queries) { + List results = matcher.findSimilarTerms(query, 3, 0.2); + System.out.println("\n查询: " + query); + if (results.isEmpty()) { + System.out.println(" 无匹配结果"); + } else { + for (int i = 0; i < results.size(); i++) { + MatchResult result = results.get(i); + System.out.printf(" %d. %s (置信度: %.2f%%)\n", + i + 1, result.getTerm(), result.getConfidence() * 100); + } + } + } + } + + /** + * 不同策略对比示例 + */ + public static void strategyComparisonExample() { + System.out.println("=== 不同策略对比示例 ==="); + + List terms = Arrays.asList( + "机器学习", "深度学习", "神经网络", "卷积神经网络", "循环神经网络", + "自然语言处理", "计算机视觉", "强化学习", "监督学习", "无监督学习" + ); + + String query = "神经网络"; + + // 测试不同的向量化策略 + Vectorizer.VectorizationStrategy[] strategies = { + Vectorizer.VectorizationStrategy.TF_IDF, + Vectorizer.VectorizationStrategy.CHARACTER_FREQUENCY, + Vectorizer.VectorizationStrategy.WORD_FREQUENCY, + Vectorizer.VectorizationStrategy.SIMPLE_HASH + }; + + for (Vectorizer.VectorizationStrategy strategy : strategies) { + VectorMatcher matcher = new VectorMatcher(strategy); + matcher.initialize(terms); + + List results = matcher.findSimilarTerms(query, 3, 0.1); + + System.out.println("\n策略: " + strategy); + if (results.isEmpty()) { + System.out.println(" 无匹配结果"); + } else { + for (int i = 0; i < results.size(); i++) { + MatchResult result = results.get(i); + System.out.printf(" %d. %s (相似度: %.4f)\n", + i + 1, result.getTerm(), result.getSimilarity()); + } + } + } + } + + /** + * 实际应用场景示例 + */ + public static void realWorldExample() { + System.out.println("=== 实际应用场景示例 ==="); + + // 模拟一个智能客服系统的术语匹配 + List technicalTerms = Arrays.asList( + // 机器学习相关 + "机器学习", "深度学习", "神经网络", "监督学习", "无监督学习", + "强化学习", "迁移学习", "集成学习", "随机森林", "支持向量机", + "决策树", "逻辑回归", "线性回归", "聚类算法", "降维算法", + + // 深度学习框架 + "TensorFlow", "PyTorch", "Keras", "Scikit-learn", "XGBoost", + "LightGBM", "CatBoost", "Theano", "Caffe", "MXNet", + + // 计算机视觉 + "计算机视觉", "图像识别", "目标检测", "图像分割", "人脸识别", + "OCR", "图像处理", "特征提取", "边缘检测", "图像增强", + + // 自然语言处理 + "自然语言处理", "文本分类", "情感分析", "命名实体识别", "机器翻译", + "问答系统", "文本摘要", "语言模型", "词向量", "句向量", + + // 数据处理 + "数据预处理", "特征工程", "数据清洗", "数据标准化", "数据归一化", + "缺失值处理", "异常值检测", "数据增强", "特征选择", "特征提取" + ); + + // 创建匹配器 + AdvancedVectorMatcher matcher = new AdvancedVectorMatcher( + AdvancedVectorMatcher.MatchingStrategy.ENSEMBLE + ); + matcher.initialize(technicalTerms); + + // 模拟用户查询 + String[] userQueries = { + "我想了解机器学习", + "深度学习框架有哪些", + "如何处理图像数据", + "文本分析怎么做", + "数据预处理步骤" + }; + + System.out.println("智能客服术语匹配示例:"); + for (String query : userQueries) { + List results = matcher.findSimilarTerms(query, 5, 0.15); + + System.out.println("\n用户查询: " + query); + System.out.println("推荐术语:"); + + if (results.isEmpty()) { + System.out.println(" 暂无相关术语"); + } else { + for (int i = 0; i < results.size(); i++) { + MatchResult result = results.get(i); + System.out.printf(" %d. %s (相关度: %.2f%%)\n", + i + 1, result.getTerm(), result.getConfidence() * 100); + } + } + } + } + + /** + * 性能测试示例 + */ + public static void performanceTest() { + System.out.println("=== 性能测试示例 ==="); + + // 创建大量术语进行性能测试 + List largeTermSet = Arrays.asList( + "机器学习", "深度学习", "神经网络", "卷积神经网络", "循环神经网络", + "自然语言处理", "计算机视觉", "强化学习", "监督学习", "无监督学习", + "半监督学习", "迁移学习", "集成学习", "随机森林", "支持向量机", + "决策树", "逻辑回归", "线性回归", "聚类算法", "降维算法", + "特征工程", "数据预处理", "模型评估", "交叉验证", "过拟合", + "欠拟合", "正则化", "梯度下降", "反向传播", "激活函数", + "损失函数", "优化器", "批量归一化", "Dropout", "注意力机制", + "Transformer", "BERT", "GPT", "ResNet", "VGG", "AlexNet", + "YOLO", "R-CNN", "Fast R-CNN", "Faster R-CNN", "Mask R-CNN", + "目标检测", "图像分割", "语义分割", "实例分割", "姿态估计" + ); + + VectorMatcher matcher = new VectorMatcher(Vectorizer.VectorizationStrategy.TF_IDF); + matcher.initialize(largeTermSet); + + String testQuery = "机器学习算法"; + int iterations = 1000; + + long startTime = System.currentTimeMillis(); + for (int i = 0; i < iterations; i++) { + matcher.findSimilarTerms(testQuery, 10, 0.1); + } + long endTime = System.currentTimeMillis(); + + long duration = endTime - startTime; + double avgTime = (double) duration / iterations; + double qps = 1000.0 / avgTime; + + System.out.println("性能测试结果:"); + System.out.println("术语数量: " + largeTermSet.size()); + System.out.println("测试查询: " + testQuery); + System.out.println("迭代次数: " + iterations); + System.out.println("总耗时: " + duration + "ms"); + System.out.println("平均耗时: " + String.format("%.2f", avgTime) + "ms"); + System.out.println("QPS: " + String.format("%.2f", qps)); + } +} \ No newline at end of file diff --git a/algorithms/src/main/java/com/github/kuangcp/vector/Vectorizer.java b/algorithms/src/main/java/com/github/kuangcp/vector/Vectorizer.java new file mode 100644 index 00000000..68737072 --- /dev/null +++ b/algorithms/src/main/java/com/github/kuangcp/vector/Vectorizer.java @@ -0,0 +1,184 @@ +package com.github.kuangcp.vector; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * 文本向量化工具类 + * 支持多种向量化策略:TF-IDF、字符频率、词频等 + */ +public class Vectorizer { + + private final Map vocabulary; + private final int vectorDimension; + private final VectorizationStrategy strategy; + + public Vectorizer(VectorizationStrategy strategy) { + this.strategy = strategy; + this.vocabulary = new HashMap<>(); + this.vectorDimension = 0; + } + + public Vectorizer(Map vocabulary, VectorizationStrategy strategy) { + this.vocabulary = new HashMap<>(vocabulary); + this.strategy = strategy; + this.vectorDimension = vocabulary.size(); + } + + /** + * 构建词汇表 + */ + public void buildVocabulary(List documents) { + Map wordFreq = new HashMap<>(); + + for (String doc : documents) { + Set words = extractWords(doc); + for (String word : words) { + wordFreq.merge(word, 1, Integer::sum); + } + } + + // 按频率排序,取前N个词作为词汇表 + List> sortedWords = wordFreq.entrySet() + .stream() + .sorted(Map.Entry.comparingByValue().reversed()) + .collect(Collectors.toList()); + + vocabulary.clear(); + for (int i = 0; i < Math.min(sortedWords.size(), 1000); i++) { + vocabulary.put(sortedWords.get(i).getKey(), i); + } + } + + /** + * 将文本向量化 + */ + public double[] vectorize(String text) { + switch (strategy) { + case TF_IDF: + return vectorizeTFIDF(text); + case CHARACTER_FREQUENCY: + return vectorizeCharacterFrequency(text); + case WORD_FREQUENCY: + return vectorizeWordFrequency(text); + case SIMPLE_HASH: + return vectorizeSimpleHash(text); + default: + return vectorizeWordFrequency(text); + } + } + + /** + * 基于字符频率的向量化 + */ + private double[] vectorizeCharacterFrequency(String text) { + double[] vector = new double[128]; // ASCII字符集 + for (char c : text.toLowerCase().toCharArray()) { + if (c < 128) { + vector[c]++; + } + } + return normalize(vector); + } + + /** + * 基于词频的向量化 + */ + private double[] vectorizeWordFrequency(String text) { + double[] vector = new double[vocabulary.size()]; + Set words = extractWords(text); + + for (String word : words) { + Integer index = vocabulary.get(word); + if (index != null) { + vector[index]++; + } + } + + return normalize(vector); + } + + /** + * 基于TF-IDF的向量化 + */ + private double[] vectorizeTFIDF(String text) { + double[] vector = new double[vocabulary.size()]; + Set words = extractWords(text); + + for (String word : words) { + Integer index = vocabulary.get(word); + if (index != null) { + vector[index]++; + } + } + + // 简单的TF-IDF计算 + for (int i = 0; i < vector.length; i++) { + if (vector[i] > 0) { + vector[i] = 1 + Math.log(vector[i]); + } + } + + return normalize(vector); + } + + /** + * 基于简单哈希的向量化 + */ + private double[] vectorizeSimpleHash(String text) { + double[] vector = new double[256]; // 使用256维向量 + for (int i = 0; i < text.length(); i++) { + int hash = Math.abs(text.charAt(i) * 31 + i) % 256; + vector[hash]++; + } + return normalize(vector); + } + + /** + * 提取文本中的词汇 + */ + private Set extractWords(String text) { + return Arrays.stream(text.toLowerCase() + .replaceAll("[^a-zA-Z\\s]", " ") + .split("\\s+")) + .filter(word -> word.length() > 1) + .collect(Collectors.toSet()); + } + + /** + * 向量归一化 + */ + private double[] normalize(double[] vector) { + double magnitude = 0.0; + for (double v : vector) { + magnitude += v * v; + } + magnitude = Math.sqrt(magnitude); + + if (magnitude > 0) { + for (int i = 0; i < vector.length; i++) { + vector[i] /= magnitude; + } + } + + return vector; + } + + public Map getVocabulary() { + return Collections.unmodifiableMap(vocabulary); + } + + public int getVectorDimension() { + return vocabulary.size(); + } + + /** + * 向量化策略枚举 + */ + public enum VectorizationStrategy { + TF_IDF, + CHARACTER_FREQUENCY, + WORD_FREQUENCY, + SIMPLE_HASH + } +} \ No newline at end of file diff --git a/algorithms/src/test/java/com/github/kuangcp/vector/VectorTest.java b/algorithms/src/test/java/com/github/kuangcp/vector/VectorTest.java new file mode 100644 index 00000000..fc02b4dd --- /dev/null +++ b/algorithms/src/test/java/com/github/kuangcp/vector/VectorTest.java @@ -0,0 +1,229 @@ +package com.github.kuangcp.vector; + +import org.junit.Before; +import org.junit.Test; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +import static org.junit.Assert.*; + +/** + * 向量匹配系统测试 + */ +public class VectorTest { + + private VectorMatcher basicMatcher; + private AdvancedVectorMatcher advancedMatcher; + private List technicalTerms; + + @Before + public void setUp() { + // 初始化专业术语列表 + technicalTerms = Arrays.asList( + "机器学习", "深度学习", "神经网络", "卷积神经网络", "循环神经网络", + "自然语言处理", "计算机视觉", "强化学习", "监督学习", "无监督学习", + "半监督学习", "迁移学习", "集成学习", "随机森林", "支持向量机", + "决策树", "逻辑回归", "线性回归", "聚类算法", "降维算法", + "特征工程", "数据预处理", "模型评估", "交叉验证", "过拟合", + "欠拟合", "正则化", "梯度下降", "反向传播", "激活函数", + "损失函数", "优化器", "批量归一化", "Dropout", "注意力机制", + "Transformer", "BERT", "GPT", "ResNet", "VGG", "AlexNet", + "YOLO", "R-CNN", "Fast R-CNN", "Faster R-CNN", "Mask R-CNN", + "目标检测", "图像分割", "语义分割", "实例分割", "姿态估计" + ); + + // 初始化基础匹配器 + basicMatcher = new VectorMatcher(Vectorizer.VectorizationStrategy.TF_IDF); + basicMatcher.initialize(technicalTerms); + + // 初始化高级匹配器 + advancedMatcher = new AdvancedVectorMatcher(AdvancedVectorMatcher.MatchingStrategy.WEIGHTED_ENSEMBLE); + advancedMatcher.initialize(technicalTerms); + } + + @Test + public void testBasicVectorMatching() { + String input = "机器学习算法"; + List results = basicMatcher.findSimilarTerms(input, 5, 0.1); + + assertNotNull(results); + assertFalse(results.isEmpty()); + assertTrue(results.size() <= 5); + + // 验证结果按相似度降序排列 + for (int i = 1; i < results.size(); i++) { + assertTrue(results.get(i-1).getSimilarity() >= results.get(i).getSimilarity()); + } + + // 验证置信度 + for (MatchResult result : results) { + assertTrue(result.getConfidence() >= 0.1); + assertTrue(result.getConfidence() <= 1.0); + } + + System.out.println("基础匹配结果:"); + results.forEach(System.out::println); + } + + @Test + public void testAdvancedVectorMatching() { + String input = "深度学习网络"; + List results = advancedMatcher.findSimilarTerms(input, 5, 0.1); + + assertNotNull(results); + assertFalse(results.isEmpty()); + + System.out.println("高级匹配结果:"); + results.forEach(System.out::println); + } + + @Test + public void testDifferentVectorizationStrategies() { + String input = "神经网络"; + + // 测试不同的向量化策略 + Vectorizer.VectorizationStrategy[] strategies = { + Vectorizer.VectorizationStrategy.TF_IDF, + Vectorizer.VectorizationStrategy.CHARACTER_FREQUENCY, + Vectorizer.VectorizationStrategy.WORD_FREQUENCY, + Vectorizer.VectorizationStrategy.SIMPLE_HASH + }; + + for (Vectorizer.VectorizationStrategy strategy : strategies) { + VectorMatcher matcher = new VectorMatcher(strategy); + matcher.initialize(technicalTerms); + + List results = matcher.findSimilarTerms(input, 3, 0.1); + + System.out.println("\n策略: " + strategy); + results.forEach(System.out::println); + + assertNotNull(results); + } + } + + @Test + public void testBatchMatching() { + List inputs = Arrays.asList( + "机器学习", "深度学习", "神经网络", "计算机视觉", "自然语言处理" + ); + + Map> batchResults = advancedMatcher.batchFindSimilarTerms(inputs, 3, 0.1); + + assertNotNull(batchResults); + assertEquals(inputs.size(), batchResults.size()); + + System.out.println("批量匹配结果:"); + batchResults.forEach((input, results) -> { + System.out.println("\n输入: " + input); + results.forEach(System.out::println); + }); + } + + @Test + public void testSimilarityCalculation() { + double[] vector1 = {1.0, 0.0, 0.0}; + double[] vector2 = {0.0, 1.0, 0.0}; + double[] vector3 = {1.0, 0.0, 0.0}; + + // 测试余弦相似度 + double similarity1 = SimilarityCalculator.cosineSimilarity(vector1, vector2); + double similarity2 = SimilarityCalculator.cosineSimilarity(vector1, vector3); + + assertEquals(0.0, similarity1, 0.001); // 垂直向量相似度为0 + assertEquals(1.0, similarity2, 0.001); // 相同向量相似度为1 + + // 测试欧几里得距离 + double distance1 = SimilarityCalculator.euclideanDistance(vector1, vector2); + double distance2 = SimilarityCalculator.euclideanDistance(vector1, vector3); + + assertEquals(Math.sqrt(2), distance1, 0.001); + assertEquals(0.0, distance2, 0.001); + } + + @Test + public void testVectorization() { + Vectorizer vectorizer = new Vectorizer(Vectorizer.VectorizationStrategy.TF_IDF); + vectorizer.buildVocabulary(Arrays.asList("机器学习", "深度学习", "神经网络")); + + double[] vector = vectorizer.vectorize("机器学习"); + + assertNotNull(vector); + assertTrue(vector.length > 0); + + // 验证向量归一化 + double magnitude = SimilarityCalculator.vectorMagnitude(vector); + assertTrue(Math.abs(magnitude - 1.0) < 0.001 || magnitude == 0.0); + } + + @Test + public void testEdgeCases() { + // 测试空输入 + List emptyResults = basicMatcher.findSimilarTerms("", 5, 0.1); + assertTrue(emptyResults.isEmpty()); + + // 测试null输入 + List nullResults = basicMatcher.findSimilarTerms(null, 5, 0.1); + assertTrue(nullResults.isEmpty()); + + // 测试不存在的术语 + List noMatchResults = basicMatcher.findSimilarTerms("不存在的术语", 5, 0.9); + assertTrue(noMatchResults.isEmpty()); + } + + @Test + public void testPerformance() { + String input = "机器学习算法"; + int iterations = 1000; + + long startTime = System.currentTimeMillis(); + for (int i = 0; i < iterations; i++) { + basicMatcher.findSimilarTerms(input, 5, 0.1); + } + long endTime = System.currentTimeMillis(); + + long duration = endTime - startTime; + double avgTime = (double) duration / iterations; + + System.out.println("性能测试结果:"); + System.out.println("总耗时: " + duration + "ms"); + System.out.println("平均耗时: " + avgTime + "ms"); + System.out.println("QPS: " + (1000.0 / avgTime)); + + // 验证性能在合理范围内 + assertTrue(avgTime < 10.0); // 平均每次查询应该小于10ms + } + + @Test + public void testRealWorldScenario() { + // 模拟实际应用场景 + String[] queries = { + "深度学习模型", + "神经网络训练", + "机器学习算法", + "计算机视觉技术", + "自然语言处理系统", + "强化学习环境", + "卷积神经网络架构", + "注意力机制实现", + "目标检测算法", + "图像分割方法" + }; + + System.out.println("实际应用场景测试结果:"); + for (String query : queries) { + List results = advancedMatcher.findSimilarTerms(query, 3, 0.2); + System.out.println("\n查询: " + query); + if (results.isEmpty()) { + System.out.println(" 无匹配结果"); + } else { + results.forEach(result -> + System.out.println(" " + result.getTerm() + " (置信度: " + + String.format("%.2f%%", result.getConfidence() * 100) + ")") + ); + } + } + } +} diff --git a/class/src/test/java/security/aes/AESUtilTest.java b/class/src/test/java/security/aes/AESUtilTest.java index 856ea8b5..2d5df4f4 100644 --- a/class/src/test/java/security/aes/AESUtilTest.java +++ b/class/src/test/java/security/aes/AESUtilTest.java @@ -55,7 +55,7 @@ public void testHexKeyFlow() throws Exception { // 生成随机密钥 KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); // 128 192 256 - keyGenerator.init(192, new SecureRandom()); + keyGenerator.init(128, new SecureRandom()); SecretKey secretKey = keyGenerator.generateKey(); byte[] key = secretKey.getEncoded(); From 6465a92a3a963b7bd361409fbf1ab09c57e4b3e4 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Fri, 25 Jul 2025 10:54:00 +0800 Subject: [PATCH 465/476] todo --- .../organization/structure/NodeMgr.java | 44 +- .../com/github/kuangcp/recursion/Hanoi.java | 50 + .../strcuture/stack/MythArrayStack.java | 8 +- .../com/github/kuangcp/string/ACMatcher.java | 147 +++ .../kuangcp/random/RandomCompareTest.java | 921 ++++++++++++++++++ .../github/kuangcp/recursion/HanoiTest.java | 27 + .../strcuture/stack/MythArrayStackTest.java | 53 + .../github/kuangcp/string/ACMatcherTest.java | 33 + 8 files changed, 1277 insertions(+), 6 deletions(-) create mode 100644 algorithms/src/main/java/com/github/kuangcp/recursion/Hanoi.java create mode 100644 algorithms/src/main/java/com/github/kuangcp/string/ACMatcher.java create mode 100644 algorithms/src/test/java/com/github/kuangcp/random/RandomCompareTest.java create mode 100644 algorithms/src/test/java/com/github/kuangcp/recursion/HanoiTest.java create mode 100644 algorithms/src/test/java/com/github/kuangcp/string/ACMatcherTest.java diff --git a/algorithms/src/main/java/com/github/kuangcp/organization/structure/NodeMgr.java b/algorithms/src/main/java/com/github/kuangcp/organization/structure/NodeMgr.java index 45fa657e..40656705 100644 --- a/algorithms/src/main/java/com/github/kuangcp/organization/structure/NodeMgr.java +++ b/algorithms/src/main/java/com/github/kuangcp/organization/structure/NodeMgr.java @@ -1,6 +1,9 @@ package com.github.kuangcp.organization.structure; +import java.util.ArrayList; +import java.util.Comparator; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; @@ -27,16 +30,49 @@ public class NodeMgr implements NodeAction { // TODO 列出整个树 public String tree(String parentId) { Node node = getNode(parentId); - - if (!Objects.isNull(node.getParent())) { + if (node == null || (node.getParent() != null)) { return ERROR; } - return ""; + StringBuilder sb = new StringBuilder(); + buildTreeString(node, "", "", sb); + return sb.toString(); } // TODO 列出子节点 public String treeByNode(String parentId) { - return ""; + Node node = getNode(parentId); + if (node == null) { + return ERROR; + } + StringBuilder sb = new StringBuilder(); + buildTreeString(node, "", "", sb); + return sb.toString(); + } + + private void buildTreeString(Node node, String prefix, String childrenPrefix, StringBuilder sb) { + sb.append(prefix); + sb.append(node.getName()); + sb.append('\n'); + + Map children = getChildSet(node.getId()); + if (children.isEmpty()) { + return; + } + + // Convert to list and sort by sort field + List sortedChildren = new ArrayList<>(children.values()); + sortedChildren.sort(Comparator.comparingInt(Node::getSort)); + + for (int i = 0; i < sortedChildren.size(); i++) { + Node child = sortedChildren.get(i); + boolean isLast = i == sortedChildren.size() - 1; + buildTreeString( + child, + childrenPrefix + (isLast ? "└─ " : "├─ "), + childrenPrefix + (isLast ? " " : "│ "), + sb + ); + } } private Node getNode(String nodeId) { diff --git a/algorithms/src/main/java/com/github/kuangcp/recursion/Hanoi.java b/algorithms/src/main/java/com/github/kuangcp/recursion/Hanoi.java new file mode 100644 index 00000000..36314e20 --- /dev/null +++ b/algorithms/src/main/java/com/github/kuangcp/recursion/Hanoi.java @@ -0,0 +1,50 @@ +package com.github.kuangcp.recursion; + +/** + * 汉诺塔问题的递归实现 + * + * 问题描述: + * 1. 有三根柱子A、B、C,A柱子上有N个盘子,从上到下依次变大 + * 2. 目标是将所有盘子从A移动到C + * 3. 每次只能移动一个盘子,且大盘子不能放在小盘子上面 + * + * @author kuangcp + */ +class Hanoi { + + /** + * 移动汉诺塔 + * + * @param n 盘子数量 + * @param from 起始柱子 + * @param auxiliary 辅助柱子 + * @param to 目标柱子 + */ + static void move(int n, char from, char auxiliary, char to) { + if (n == 1) { + // 只有一个盘子时,直接移动 + System.out.printf("Move disk 1 from %c to %c%n", from, to); + return; + } + + // 将n-1个盘子从起始柱移动到辅助柱 + move(n - 1, from, to, auxiliary); + + // 将最大的盘子从起始柱移动到目标柱 + System.out.printf("Move disk %d from %c to %c%n", n, from, to); + + // 将n-1个盘子从辅助柱移动到目标柱 + move(n - 1, auxiliary, from, to); + } + + /** + * 计算移动n个盘子需要的最少步数 + * 使用递归公式:steps(n) = 2 * steps(n-1) + 1 + */ + static int calculateSteps(int n) { + if (n == 1) { + return 1; + } + return 2 * calculateSteps(n - 1) + 1; + } +} \ No newline at end of file diff --git a/algorithms/src/main/java/com/github/kuangcp/strcuture/stack/MythArrayStack.java b/algorithms/src/main/java/com/github/kuangcp/strcuture/stack/MythArrayStack.java index 4b1d3006..6c12be7b 100644 --- a/algorithms/src/main/java/com/github/kuangcp/strcuture/stack/MythArrayStack.java +++ b/algorithms/src/main/java/com/github/kuangcp/strcuture/stack/MythArrayStack.java @@ -24,8 +24,12 @@ public MythArrayStack(int maxSize) { @Override public void push(T data) { if (isFull()) { - // TODO 扩容 - return; + // 扩容为原来的2倍 + int newSize = maxSize * 2; + Object[] newArray = new Object[newSize]; + System.arraycopy(elementData, 0, newArray, 0, maxSize); + elementData = newArray; + maxSize = newSize; } top++; this.elementData[top] = data; diff --git a/algorithms/src/main/java/com/github/kuangcp/string/ACMatcher.java b/algorithms/src/main/java/com/github/kuangcp/string/ACMatcher.java new file mode 100644 index 00000000..e2b8c0a2 --- /dev/null +++ b/algorithms/src/main/java/com/github/kuangcp/string/ACMatcher.java @@ -0,0 +1,147 @@ +package com.github.kuangcp.string; + +import java.util.*; + +/** + * @author Kuangcp + * 2025-07-07 10:24 + */ +public class ACMatcher { + + private static class Node { + Map children = new HashMap<>(); + boolean isEnd = false; + String word = null; + Node fail = null; + } + + private final Node root = new Node(); + + // 构建Trie树并添加失败指针 + public void buildTrie(Set dictionary) { + if (dictionary == null || dictionary.isEmpty()) { + return; + } + + // 构建Trie树 + for (String word : dictionary) { + if (word == null || word.isEmpty()) { + continue; + } + Node current = root; + for (char c : word.toCharArray()) { + current.children.putIfAbsent(c, new Node()); + current = current.children.get(c); + } + current.isEnd = true; + current.word = word; + } + + // 构建失败指针 + Queue queue = new LinkedList<>(); + root.fail = root; + + // 将第一层节点的失败指针指向root + for (Map.Entry entry : root.children.entrySet()) { + Node child = entry.getValue(); + child.fail = root; + queue.offer(child); + } + + // 构建其他节点的失败指针 + while (!queue.isEmpty()) { + Node current = queue.poll(); + if (current == null) { + continue; + } + + for (Map.Entry entry : current.children.entrySet()) { + char ch = entry.getKey(); + Node child = entry.getValue(); + queue.offer(child); + + // 从父节点的失败指针开始查找 + Node failNode = current.fail; + while (failNode != null && !failNode.children.containsKey(ch)) { + failNode = failNode.fail; + } + + child.fail = (failNode != null && failNode.children.containsKey(ch)) + ? failNode.children.get(ch) + : root; + } + } + } + + // 查找所有匹配位置 + public List findMatches(String text) { + if (text == null || text.isEmpty()) { + return Collections.emptyList(); + } + + List matches = new ArrayList<>(); + Node current = root; + + for (int i = 0; i < text.length(); i++) { + char ch = text.charAt(i); + + while (current != root && !current.children.containsKey(ch)) { + current = current.fail; + } + + current = current.children.getOrDefault(ch, root); + + // 检查当前节点及其失败指针 + Node temp = current; + while (temp != root) { + if (temp.isEnd) { + int start = i - temp.word.length() + 1; + // 检查是否是更长词的一部分 + if (!isPartOfLongerWord(text, start, temp.word)) { + matches.add(new Match(start, i + 1, temp.word)); + } + } + temp = temp.fail; + } + } + return matches; + } + + // 检查是否是更长词的一部分 + private boolean isPartOfLongerWord(String text, int start, String word) { + // 检查前后是否有其他汉字 + if (start > 0 && isChineseChar(text.charAt(start - 1))) { + return true; + } + int end = start + word.length(); + if (end < text.length() && isChineseChar(text.charAt(end))) { + return true; + } + return false; + } + + private boolean isChineseChar(char c) { + return Character.UnicodeScript.of(c) == Character.UnicodeScript.HAN; + } + + public static class Match { + public final int start; + public final int end; + public final String word; + + public Match(int start, int end, String word) { + this.start = start; + this.end = end; + this.word = word; + } + + @Override + public String toString() { + return "Match{" + + "word='" + word + '\'' + + ", start=" + start + + ", end=" + end + + '}'; + } + } +} \ No newline at end of file diff --git a/algorithms/src/test/java/com/github/kuangcp/random/RandomCompareTest.java b/algorithms/src/test/java/com/github/kuangcp/random/RandomCompareTest.java new file mode 100644 index 00000000..e338e0e4 --- /dev/null +++ b/algorithms/src/test/java/com/github/kuangcp/random/RandomCompareTest.java @@ -0,0 +1,921 @@ +package com.github.kuangcp.random; + +import org.junit.Test; + +import java.security.SecureRandom; +import java.time.Duration; +import java.time.Instant; +import java.util.*; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertTrue; + +/** + * 全面对比 Random 和 ThreadLocalRandom 在各种场景下的性能差异和特性 + * + * @author kuangcp + */ +public class RandomCompareTest { + + private static final int LOOP_COUNT = 1000000; + private static final int THREAD_COUNT = 4; + private static final int WARM_UP_COUNT = 1000; + private static final String CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + + // 用于统计的数据结构 + private static class Statistics { + long minTime = Long.MAX_VALUE; + long maxTime = Long.MIN_VALUE; + long totalTime = 0; + long count = 0; + List allTimes = new ArrayList<>(); + + void addTime(long time) { + minTime = Math.min(minTime, time); + maxTime = Math.max(maxTime, time); + totalTime += time; + count++; + allTimes.add(time); + } + + double getAverage() { + return count > 0 ? (double) totalTime / count : 0; + } + + double getStandardDeviation() { + if (count < 2) return 0; + double mean = getAverage(); + double variance = allTimes.stream() + .mapToDouble(time -> time - mean) + .map(diff -> diff * diff) + .average() + .orElse(0.0); + return Math.sqrt(variance); + } + + double getMedian() { + if (allTimes.isEmpty()) return 0; + List sorted = new ArrayList<>(allTimes); + Collections.sort(sorted); + int middle = sorted.size() / 2; + if (sorted.size() % 2 == 0) { + return (sorted.get(middle - 1) + sorted.get(middle)) / 2.0; + } else { + return sorted.get(middle); + } + } + + @Override + public String toString() { + return String.format("min=%d ns, max=%d ns, avg=%.2f ns, median=%.2f ns, stddev=%.2f ns, count=%d", + minTime, maxTime, getAverage(), getMedian(), getStandardDeviation(), count); + } + } + + // 预热JVM + private void warmup() { + Random random = new Random(); + for (int i = 0; i < WARM_UP_COUNT; i++) { + random.nextInt(); + ThreadLocalRandom.current().nextInt(); + } + } + + @Test + public void testSingleThread() { + warmup(); + + // 测试 Random + long startRandom = System.nanoTime(); + Random random = new Random(); + for (int i = 0; i < LOOP_COUNT; i++) { + random.nextInt(100); + } + long randomTime = System.nanoTime() - startRandom; + + // 测试 ThreadLocalRandom + long startThreadLocal = System.nanoTime(); + for (int i = 0; i < LOOP_COUNT; i++) { + ThreadLocalRandom.current().nextInt(100); + } + long threadLocalTime = System.nanoTime() - startThreadLocal; + + System.out.printf("Single Thread Performance:%n"); + System.out.printf("Random time: %d ns%n", randomTime); + System.out.printf("ThreadLocalRandom time: %d ns%n", threadLocalTime); + } + + @Test + public void testMultiThread() throws InterruptedException { + warmup(); + ExecutorService executor = Executors.newFixedThreadPool(THREAD_COUNT); + CountDownLatch latch = new CountDownLatch(THREAD_COUNT); + List randomTimes = new ArrayList<>(); + List threadLocalTimes = new ArrayList<>(); + + // 测试共享 Random 实例 + Random sharedRandom = new Random(); + for (int t = 0; t < THREAD_COUNT; t++) { + CountDownLatch finalLatch = latch; + executor.submit(() -> { + long start = System.nanoTime(); + for (int i = 0; i < LOOP_COUNT; i++) { + sharedRandom.nextInt(100); + } + randomTimes.add(System.nanoTime() - start); + finalLatch.countDown(); + }); + } + latch.await(); + + // 重置 CountDownLatch + latch = new CountDownLatch(THREAD_COUNT); + + // 测试 ThreadLocalRandom + for (int t = 0; t < THREAD_COUNT; t++) { + CountDownLatch finalLatch1 = latch; + executor.submit(() -> { + long start = System.nanoTime(); + for (int i = 0; i < LOOP_COUNT; i++) { + ThreadLocalRandom.current().nextInt(100); + } + threadLocalTimes.add(System.nanoTime() - start); + finalLatch1.countDown(); + }); + } + latch.await(); + + executor.shutdown(); + executor.awaitTermination(10, TimeUnit.SECONDS); + + // 计算并打印结果 + long avgRandomTime = randomTimes.stream().mapToLong(Long::longValue).sum() / THREAD_COUNT; + long avgThreadLocalTime = threadLocalTimes.stream().mapToLong(Long::longValue).sum() / THREAD_COUNT; + + System.out.printf("Multi Thread Performance (average per thread):%n"); + System.out.printf("Shared Random time: %d ns%n", avgRandomTime); + System.out.printf("ThreadLocalRandom time: %d ns%n", avgThreadLocalTime); + } + + @Test + public void testDifferentRanges() { + int[] ranges = {10, 100, 1000, 10000, 100000, Integer.MAX_VALUE}; + Random random = new Random(); + + for (int range : ranges) { + Statistics randomStats = new Statistics(); + Statistics threadLocalStats = new Statistics(); + + for (int i = 0; i < LOOP_COUNT; i++) { + // Test Random + long start = System.nanoTime(); + random.nextInt(range); + randomStats.addTime(System.nanoTime() - start); + + // Test ThreadLocalRandom + start = System.nanoTime(); + ThreadLocalRandom.current().nextInt(range); + threadLocalStats.addTime(System.nanoTime() - start); + } + + System.out.printf("Range [0-%d]:%n", range); + System.out.printf("Random: %s%n", randomStats); + System.out.printf("ThreadLocalRandom: %s%n%n", threadLocalStats); + } + } + + @Test + public void testDifferentTypes() { + Random random = new Random(); + Statistics[] randomStats = new Statistics[5]; + Statistics[] threadLocalStats = new Statistics[5]; + + for (int i = 0; i < 5; i++) { + randomStats[i] = new Statistics(); + threadLocalStats[i] = new Statistics(); + } + + for (int i = 0; i < LOOP_COUNT; i++) { + // Test int + long start = System.nanoTime(); + random.nextInt(); + randomStats[0].addTime(System.nanoTime() - start); + + start = System.nanoTime(); + ThreadLocalRandom.current().nextInt(); + threadLocalStats[0].addTime(System.nanoTime() - start); + + // Test long + start = System.nanoTime(); + random.nextLong(); + randomStats[1].addTime(System.nanoTime() - start); + + start = System.nanoTime(); + ThreadLocalRandom.current().nextLong(); + threadLocalStats[1].addTime(System.nanoTime() - start); + + // Test double + start = System.nanoTime(); + random.nextDouble(); + randomStats[2].addTime(System.nanoTime() - start); + + start = System.nanoTime(); + ThreadLocalRandom.current().nextDouble(); + threadLocalStats[2].addTime(System.nanoTime() - start); + + // Test boolean + start = System.nanoTime(); + random.nextBoolean(); + randomStats[3].addTime(System.nanoTime() - start); + + start = System.nanoTime(); + ThreadLocalRandom.current().nextBoolean(); + threadLocalStats[3].addTime(System.nanoTime() - start); + + // Test float + start = System.nanoTime(); + random.nextFloat(); + randomStats[4].addTime(System.nanoTime() - start); + + start = System.nanoTime(); + ThreadLocalRandom.current().nextFloat(); + threadLocalStats[4].addTime(System.nanoTime() - start); + } + + String[] types = {"Integer", "Long", "Double", "Boolean", "Float"}; + for (int i = 0; i < types.length; i++) { + System.out.printf("%s:%n", types[i]); + System.out.printf("Random: %s%n", randomStats[i]); + System.out.printf("ThreadLocalRandom: %s%n%n", threadLocalStats[i]); + } + } + + @Test + public void testThreadScalability() throws InterruptedException { + int[] threadCounts = {1, 2, 4, 8, 16, 32}; + + for (int threadCount : threadCounts) { + ExecutorService executor = Executors.newFixedThreadPool(threadCount); + CountDownLatch latch = new CountDownLatch(threadCount); + List randomTimes = new ArrayList<>(); + List threadLocalTimes = new ArrayList<>(); + + // Test shared Random + Random sharedRandom = new Random(); + for (int t = 0; t < threadCount; t++) { + executor.submit(() -> { + long start = System.nanoTime(); + for (int i = 0; i < LOOP_COUNT; i++) { + sharedRandom.nextInt(100); + } + randomTimes.add(System.nanoTime() - start); + latch.countDown(); + }); + } + latch.await(); + + // Reset latch + CountDownLatch latch2 = new CountDownLatch(threadCount); + + // Test ThreadLocalRandom + for (int t = 0; t < threadCount; t++) { + executor.submit(() -> { + long start = System.nanoTime(); + for (int i = 0; i < LOOP_COUNT; i++) { + ThreadLocalRandom.current().nextInt(100); + } + threadLocalTimes.add(System.nanoTime() - start); + latch2.countDown(); + }); + } + latch2.await(); + + executor.shutdown(); + executor.awaitTermination(10, TimeUnit.SECONDS); + + double avgRandomTime = randomTimes.stream().mapToLong(Long::longValue).average().orElse(0); + double avgThreadLocalTime = threadLocalTimes.stream().mapToLong(Long::longValue).average().orElse(0); + + System.out.printf("Thread Count: %d%n", threadCount); + System.out.printf("Average Random time: %.2f ns%n", avgRandomTime); + System.out.printf("Average ThreadLocalRandom time: %.2f ns%n%n", avgThreadLocalTime); + } + } + + @Test + public void testDistribution() { + int bucketCount = 10; + int[] randomBuckets = new int[bucketCount]; + int[] threadLocalBuckets = new int[bucketCount]; + + Random random = new Random(); + + for (int i = 0; i < LOOP_COUNT; i++) { + int randomValue = random.nextInt(bucketCount); + int threadLocalValue = ThreadLocalRandom.current().nextInt(bucketCount); + + randomBuckets[randomValue]++; + threadLocalBuckets[threadLocalValue]++; + } + + System.out.println("Distribution Analysis:"); + System.out.println("Random distribution:"); + printDistribution(randomBuckets); + System.out.println("\nThreadLocalRandom distribution:"); + printDistribution(threadLocalBuckets); + + // Chi-square test for uniformity + double randomChiSquare = calculateChiSquare(randomBuckets); + double threadLocalChiSquare = calculateChiSquare(threadLocalBuckets); + + System.out.printf("%nChi-square test results:%n"); + System.out.printf("Random: %.2f%n", randomChiSquare); + System.out.printf("ThreadLocalRandom: %.2f%n", threadLocalChiSquare); + } + + private void printDistribution(int[] buckets) { + int total = Arrays.stream(buckets).sum(); + for (int i = 0; i < buckets.length; i++) { + double percentage = (double) buckets[i] / total * 100; + System.out.printf("Bucket %d: %d (%.2f%%)%n", i, buckets[i], percentage); + } + } + + private double calculateChiSquare(int[] buckets) { + double expected = Arrays.stream(buckets).sum() / (double) buckets.length; + return Arrays.stream(buckets) + .mapToDouble(observed -> Math.pow(observed - expected, 2) / expected) + .sum(); + } + + @Test + public void testRandomString() { + int[] lengths = {5, 10, 20, 50, 100}; + Random random = new Random(); + + for (int length : lengths) { + Statistics randomStats = new Statistics(); + Statistics threadLocalStats = new Statistics(); + + for (int i = 0; i < LOOP_COUNT / 100; i++) { // Reduced iterations due to string operations + // Test Random + long start = System.nanoTime(); + generateRandomString(random, length); + randomStats.addTime(System.nanoTime() - start); + + // Test ThreadLocalRandom + start = System.nanoTime(); + generateRandomString(ThreadLocalRandom.current(), length); + threadLocalStats.addTime(System.nanoTime() - start); + } + + System.out.printf("String length: %d%n", length); + System.out.printf("Random: %s%n", randomStats); + System.out.printf("ThreadLocalRandom: %s%n%n", threadLocalStats); + } + } + + private String generateRandomString(Random random, int length) { + return random.ints(length, 0, 36) + .mapToObj(i -> Character.toString("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ".charAt(i))) + .collect(Collectors.joining()); + } + + private String generateRandomString(ThreadLocalRandom random, int length) { + return random.ints(length, 0, 36) + .mapToObj(i -> Character.toString("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ".charAt(i))) + .collect(Collectors.joining()); + } + + @Test + public void testRandomArray() { + int[] sizes = {100, 1000, 10000}; + Random random = new Random(); + + for (int size : sizes) { + Statistics randomStats = new Statistics(); + Statistics threadLocalStats = new Statistics(); + + for (int i = 0; i < LOOP_COUNT / 1000; i++) { // Reduced iterations due to array operations + // Test Random + long start = System.nanoTime(); + int[] randomArray = generateRandomArray(random, size); + randomStats.addTime(System.nanoTime() - start); + + // Test ThreadLocalRandom + start = System.nanoTime(); + int[] threadLocalArray = generateRandomArray(ThreadLocalRandom.current(), size); + threadLocalStats.addTime(System.nanoTime() - start); + + // Verify arrays are properly filled + assertTrue(Arrays.stream(randomArray).anyMatch(x -> x != 0)); + assertTrue(Arrays.stream(threadLocalArray).anyMatch(x -> x != 0)); + } + + System.out.printf("Array size: %d%n", size); + System.out.printf("Random: %s%n", randomStats); + System.out.printf("ThreadLocalRandom: %s%n%n", threadLocalStats); + } + } + + private int[] generateRandomArray(Random random, int size) { + return random.ints(size).toArray(); + } + + private int[] generateRandomArray(ThreadLocalRandom random, int size) { + return random.ints(size).toArray(); + } + + @Test + public void testGaussianDistribution() { + int bucketCount = 20; + int[] randomBuckets = new int[bucketCount]; + int[] threadLocalBuckets = new int[bucketCount]; + + Random random = new Random(); + + for (int i = 0; i < LOOP_COUNT; i++) { + double randomGaussian = random.nextGaussian(); + double threadLocalGaussian = ThreadLocalRandom.current().nextGaussian(); + + int randomBucket = getBucket(randomGaussian, bucketCount); + int threadLocalBucket = getBucket(threadLocalGaussian, bucketCount); + + if (randomBucket >= 0 && randomBucket < bucketCount) { + randomBuckets[randomBucket]++; + } + if (threadLocalBucket >= 0 && threadLocalBucket < bucketCount) { + threadLocalBuckets[threadLocalBucket]++; + } + } + + System.out.println("Gaussian Distribution Analysis:"); + System.out.println("Random Gaussian distribution:"); + printDistribution(randomBuckets); + System.out.println("\nThreadLocalRandom Gaussian distribution:"); + printDistribution(threadLocalBuckets); + } + + private int getBucket(double gaussian, int bucketCount) { + // Map the gaussian value (-3 to 3 standard deviations) to a bucket + double normalized = (gaussian + 3.0) / 6.0; // Map to 0-1 range + return (int) (normalized * bucketCount); + } + + @Test + public void testBoundaryValues() { + Random random = new Random(); + ThreadLocalRandom threadLocalRandom = ThreadLocalRandom.current(); + + // Test various boundary conditions + int[] boundaries = { + Integer.MAX_VALUE, + Integer.MAX_VALUE - 1, + Integer.MIN_VALUE, + Integer.MIN_VALUE + 1, + 0, + 1, + -1 + }; + + for (int boundary : boundaries) { + try { + random.nextInt(boundary); + System.out.printf("Random.nextInt(%d) succeeded%n", boundary); + } catch (Exception e) { + System.out.printf("Random.nextInt(%d) threw %s%n", boundary, e.getClass().getSimpleName()); + } + + try { + threadLocalRandom.nextInt(boundary); + System.out.printf("ThreadLocalRandom.nextInt(%d) succeeded%n", boundary); + } catch (Exception e) { + System.out.printf("ThreadLocalRandom.nextInt(%d) threw %s%n", boundary, e.getClass().getSimpleName()); + } + } + } + + @Test + public void testSequentialGeneration() { + int sequenceLength = 1000; + Random random = new Random(); + + // Generate sequences + int[] randomSequence = new int[sequenceLength]; + int[] threadLocalSequence = new int[sequenceLength]; + + for (int i = 0; i < sequenceLength; i++) { + randomSequence[i] = random.nextInt(100); + threadLocalSequence[i] = ThreadLocalRandom.current().nextInt(100); + } + + // Analyze sequences for patterns + analyzeSequence("Random", randomSequence); + analyzeSequence("ThreadLocalRandom", threadLocalSequence); + } + + private void analyzeSequence(String name, int[] sequence) { + // Calculate basic statistics + IntSummaryStatistics stats = Arrays.stream(sequence).summaryStatistics(); + + // Calculate runs (consecutive increasing/decreasing numbers) + int increasingRuns = 0; + int decreasingRuns = 0; + int currentRun = 1; + boolean wasIncreasing = true; + + for (int i = 1; i < sequence.length; i++) { + if (sequence[i] > sequence[i - 1]) { + if (wasIncreasing) { + currentRun++; + } else { + if (currentRun > 1) decreasingRuns++; + currentRun = 1; + wasIncreasing = true; + } + } else if (sequence[i] < sequence[i - 1]) { + if (!wasIncreasing) { + currentRun++; + } else { + if (currentRun > 1) increasingRuns++; + currentRun = 1; + wasIncreasing = false; + } + } + } + + System.out.printf("%s Sequence Analysis:%n", name); + System.out.printf("Min: %d, Max: %d, Average: %.2f%n", + stats.getMin(), stats.getMax(), stats.getAverage()); + System.out.printf("Increasing runs: %d, Decreasing runs: %d%n%n", + increasingRuns, decreasingRuns); + } + + @Test + public void testSeedReproducibility() { + long seed = 12345L; + Random random1 = new Random(seed); + Random random2 = new Random(seed); + ThreadLocalRandom tlRandom = ThreadLocalRandom.current(); + + int count = 1000; + boolean allMatch = true; + List sequence1 = new ArrayList<>(); + List sequence2 = new ArrayList<>(); + + // 测试Random的种子复现性 + for (int i = 0; i < count; i++) { + sequence1.add(random1.nextInt()); + sequence2.add(random2.nextInt()); + } + + assertEquals("使用相同种子的Random应产生相同序列", sequence1, sequence2); + + // 记录ThreadLocalRandom的序列 + List tlSequence = new ArrayList<>(); + for (int i = 0; i < count; i++) { + tlSequence.add(tlRandom.nextInt()); + } + + // 验证ThreadLocalRandom序列与Random序列的不同 + assertNotEquals("ThreadLocalRandom序列应与Random序列不同", sequence1, tlSequence); + } + + @Test + public void testStreamPerformance() { + int streamSize = 1000000; + Statistics randomStats = new Statistics(); + Statistics threadLocalStats = new Statistics(); + Statistics parallelRandomStats = new Statistics(); + Statistics parallelThreadLocalStats = new Statistics(); + + // 测试串行流 + for (int i = 0; i < 10; i++) { + // Random串行流 + long start = System.nanoTime(); + Random random = new Random(); + random.ints(streamSize).forEach(x -> { + }); + randomStats.addTime(System.nanoTime() - start); + + // ThreadLocalRandom串行流 + start = System.nanoTime(); + ThreadLocalRandom.current().ints(streamSize).forEach(x -> { + }); + threadLocalStats.addTime(System.nanoTime() - start); + + // Random并行流 + start = System.nanoTime(); + random.ints(streamSize).parallel().forEach(x -> { + }); + parallelRandomStats.addTime(System.nanoTime() - start); + + // ThreadLocalRandom并行流 + start = System.nanoTime(); + ThreadLocalRandom.current().ints(streamSize).parallel().forEach(x -> { + }); + parallelThreadLocalStats.addTime(System.nanoTime() - start); + } + + System.out.println("Stream Performance Test Results:"); + System.out.printf("Random (Serial): %s%n", randomStats); + System.out.printf("ThreadLocalRandom (Serial): %s%n", threadLocalStats); + System.out.printf("Random (Parallel): %s%n", parallelRandomStats); + System.out.printf("ThreadLocalRandom (Parallel): %s%n", parallelThreadLocalStats); + } + + @Test + public void testMemoryUsage() throws InterruptedException { + int iterations = 1000000; + System.gc(); // 尝试在测试前清理内存 + Thread.sleep(1000); // 等待GC完成 + + long startMemory = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory(); + + // 测试Random + List randoms = new ArrayList<>(); + for (int i = 0; i < iterations; i++) { + randoms.add(new Random()); + } + long randomMemory = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory() - startMemory; + randoms = null; + + System.gc(); + Thread.sleep(1000); + + // 测试ThreadLocalRandom + startMemory = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory(); + List threadLocals = new ArrayList<>(); + for (int i = 0; i < iterations; i++) { + threadLocals.add(ThreadLocalRandom.current()); + } + long threadLocalMemory = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory() - startMemory; + + System.out.printf("Memory Usage Test Results:%n"); + System.out.printf("Random instances memory: %d bytes%n", randomMemory); + System.out.printf("ThreadLocalRandom instances memory: %d bytes%n", threadLocalMemory); + } + + @Test + public void testSecureRandomComparison() { + int iterations = 10000; + Statistics randomStats = new Statistics(); + Statistics threadLocalStats = new Statistics(); + Statistics secureRandomStats = new Statistics(); + + Random random = new Random(); + ThreadLocalRandom threadLocal = ThreadLocalRandom.current(); + SecureRandom secureRandom = new SecureRandom(); + + for (int i = 0; i < iterations; i++) { + // Test Random + long start = System.nanoTime(); + random.nextBytes(new byte[16]); + randomStats.addTime(System.nanoTime() - start); + + // Test ThreadLocalRandom + start = System.nanoTime(); + threadLocal.nextBytes(new byte[16]); + threadLocalStats.addTime(System.nanoTime() - start); + + // Test SecureRandom + start = System.nanoTime(); + secureRandom.nextBytes(new byte[16]); + secureRandomStats.addTime(System.nanoTime() - start); + } + + System.out.println("Secure Random Comparison Results:"); + System.out.printf("Random: %s%n", randomStats); + System.out.printf("ThreadLocalRandom: %s%n", threadLocalStats); + System.out.printf("SecureRandom: %s%n", secureRandomStats); + } + + @Test + public void testRangeDistribution() { + int iterations = 1000000; + int[] ranges = {10, 100, 1000}; + + for (int range : ranges) { + Map randomDist = new HashMap<>(); + Map threadLocalDist = new HashMap<>(); + + Random random = new Random(); + ThreadLocalRandom threadLocal = ThreadLocalRandom.current(); + + // 生成随机数并统计分布 + for (int i = 0; i < iterations; i++) { + int randomNum = random.nextInt(range); + int threadLocalNum = threadLocal.nextInt(range); + + randomDist.merge(randomNum, 1, Integer::sum); + threadLocalDist.merge(threadLocalNum, 1, Integer::sum); + } + + // 计算分布均匀性 + double expectedFreq = (double) iterations / range; + double randomChiSquare = calculateChiSquare(randomDist.values(), expectedFreq); + double threadLocalChiSquare = calculateChiSquare(threadLocalDist.values(), expectedFreq); + + System.out.printf("Range [0-%d] Distribution Analysis:%n", range); + System.out.printf("Random Chi-square: %.2f%n", randomChiSquare); + System.out.printf("ThreadLocalRandom Chi-square: %.2f%n%n", threadLocalChiSquare); + } + } + + private double calculateChiSquare(Collection observed, double expected) { + return observed.stream() + .mapToDouble(freq -> Math.pow(freq - expected, 2) / expected) + .sum(); + } + + @Test + public void testConcurrentModification() throws InterruptedException { + int threadCount = 10; + int operationsPerThread = 10000; + ExecutorService executor = Executors.newFixedThreadPool(threadCount); + CountDownLatch latch = new CountDownLatch(threadCount); + + // 共享的Random实例 + Random sharedRandom = new Random(); + AtomicInteger randomExceptions = new AtomicInteger(0); + AtomicInteger threadLocalExceptions = new AtomicInteger(0); + + // 启动多个线程同时修改Random的种子 + for (int i = 0; i < threadCount; i++) { + executor.submit(() -> { + try { + for (int j = 0; j < operationsPerThread; j++) { + try { + sharedRandom.setSeed(System.nanoTime()); + sharedRandom.nextInt(); + } catch (Exception e) { + randomExceptions.incrementAndGet(); + } + + try { + ThreadLocalRandom.current().nextInt(); + } catch (Exception e) { + threadLocalExceptions.incrementAndGet(); + } + } + } finally { + latch.countDown(); + } + }); + } + + latch.await(); + executor.shutdown(); + executor.awaitTermination(10, TimeUnit.SECONDS); + + System.out.println("Concurrent Modification Test Results:"); + System.out.printf("Random exceptions: %d%n", randomExceptions.get()); + System.out.printf("ThreadLocalRandom exceptions: %d%n", threadLocalExceptions.get()); + } + + @Test + public void testRandomUUID() { + int iterations = 100000; + Statistics randomStats = new Statistics(); + Statistics threadLocalStats = new Statistics(); + + for (int i = 0; i < iterations; i++) { + // 使用Random生成UUID + long start = System.nanoTime(); + generateUUID(new Random()); + randomStats.addTime(System.nanoTime() - start); + + // 使用ThreadLocalRandom生成UUID + start = System.nanoTime(); + generateUUID(ThreadLocalRandom.current()); + threadLocalStats.addTime(System.nanoTime() - start); + } + + System.out.println("UUID Generation Performance:"); + System.out.printf("Random: %s%n", randomStats); + System.out.printf("ThreadLocalRandom: %s%n", threadLocalStats); + } + + private UUID generateUUID(Random random) { + byte[] randomBytes = new byte[16]; + random.nextBytes(randomBytes); + randomBytes[6] &= 0x0f; // clear version + randomBytes[6] |= 0x40; // set to version 4 + randomBytes[8] &= 0x3f; // clear variant + randomBytes[8] |= 0x80; // set to IETF variant + return UUID.nameUUIDFromBytes(randomBytes); + } + + @Test + public void testLongTermStability() throws InterruptedException { + Duration testDuration = Duration.ofMinutes(1); + int checkInterval = 1000000; + + Random random = new Random(); + ThreadLocalRandom threadLocal = ThreadLocalRandom.current(); + + Instant startTime = Instant.now(); + long iteration = 0; + Statistics randomStats = new Statistics(); + Statistics threadLocalStats = new Statistics(); + + while (Duration.between(startTime, Instant.now()).compareTo(testDuration) < 0) { + if (iteration % checkInterval == 0) { + // 测试Random性能 + long start = System.nanoTime(); + for (int i = 0; i < 1000; i++) { + random.nextInt(); + } + randomStats.addTime(System.nanoTime() - start); + + // 测试ThreadLocalRandom性能 + start = System.nanoTime(); + for (int i = 0; i < 1000; i++) { + threadLocal.nextInt(); + } + threadLocalStats.addTime(System.nanoTime() - start); + } + + // 持续生成随机数 + random.nextInt(); + threadLocal.nextInt(); + iteration++; + } + + System.out.println("Long-term Stability Test Results:"); + System.out.printf("Total iterations: %d%n", iteration); + System.out.printf("Random performance samples: %s%n", randomStats); + System.out.printf("ThreadLocalRandom performance samples: %s%n", threadLocalStats); + } + + @Test + public void testRandomWithCustomThreadPool() throws InterruptedException { + // 测试不同线程池配置下的性能 + int[] poolSizes = {2, 4, 8, 16}; + int tasksPerPool = 1000000; + + for (int poolSize : poolSizes) { + // 创建不同类型的线程池 + ExecutorService fixedPool = Executors.newFixedThreadPool(poolSize); + ExecutorService cachedPool = Executors.newCachedThreadPool(); + ExecutorService workStealingPool = Executors.newWorkStealingPool(poolSize); + + // 测试每种线程池 + System.out.printf("Testing with pool size: %d%n", poolSize); + testWithThreadPool("Fixed Thread Pool", fixedPool, tasksPerPool); + testWithThreadPool("Cached Thread Pool", cachedPool, tasksPerPool); + testWithThreadPool("Work Stealing Pool", workStealingPool, tasksPerPool); + + // 关闭线程池 + fixedPool.shutdown(); + cachedPool.shutdown(); + workStealingPool.shutdown(); + + fixedPool.awaitTermination(10, TimeUnit.SECONDS); + cachedPool.awaitTermination(10, TimeUnit.SECONDS); + workStealingPool.awaitTermination(10, TimeUnit.SECONDS); + + System.out.println(); + } + } + + private void testWithThreadPool(String poolType, ExecutorService pool, int taskCount) throws InterruptedException { + Statistics randomStats = new Statistics(); + Statistics threadLocalStats = new Statistics(); + CountDownLatch latch = new CountDownLatch(taskCount); + Random sharedRandom = new Random(); + + // 提交任务到线程池 + for (int i = 0; i < taskCount; i++) { + pool.submit(() -> { + try { + // 测试Random + long start = System.nanoTime(); + sharedRandom.nextInt(); + randomStats.addTime(System.nanoTime() - start); + + // 测试ThreadLocalRandom + start = System.nanoTime(); + ThreadLocalRandom.current().nextInt(); + threadLocalStats.addTime(System.nanoTime() - start); + } finally { + latch.countDown(); + } + }); + } + + latch.await(); + + System.out.printf("%s Results:%n", poolType); + System.out.printf("Random: %s%n", randomStats); + System.out.printf("ThreadLocalRandom: %s%n", threadLocalStats); + } +} \ No newline at end of file diff --git a/algorithms/src/test/java/com/github/kuangcp/recursion/HanoiTest.java b/algorithms/src/test/java/com/github/kuangcp/recursion/HanoiTest.java new file mode 100644 index 00000000..ebac3b5d --- /dev/null +++ b/algorithms/src/test/java/com/github/kuangcp/recursion/HanoiTest.java @@ -0,0 +1,27 @@ +package com.github.kuangcp.recursion; + +import org.junit.Test; +import static org.junit.Assert.assertEquals; + +/** + * 汉诺塔问题的测试类 + * + * @author kuangcp + */ +public class HanoiTest { + + @Test + public void testMove() { + System.out.println("Moving 3 disks from A to C using B as auxiliary:"); + Hanoi.move(3, 'A', 'B', 'C'); + } + + @Test + public void testCalculateSteps() { + // 测试不同盘子数量的最少步数 + assertEquals(1, Hanoi.calculateSteps(1)); // 1个盘子需要1步 + assertEquals(3, Hanoi.calculateSteps(2)); // 2个盘子需要3步 + assertEquals(7, Hanoi.calculateSteps(3)); // 3个盘子需要7步 + assertEquals(15, Hanoi.calculateSteps(4)); // 4个盘子需要15步 + } +} \ No newline at end of file diff --git a/algorithms/src/test/java/com/github/kuangcp/strcuture/stack/MythArrayStackTest.java b/algorithms/src/test/java/com/github/kuangcp/strcuture/stack/MythArrayStackTest.java index 5645ffd3..cdeb3311 100644 --- a/algorithms/src/test/java/com/github/kuangcp/strcuture/stack/MythArrayStackTest.java +++ b/algorithms/src/test/java/com/github/kuangcp/strcuture/stack/MythArrayStackTest.java @@ -78,4 +78,57 @@ public void testClear() { stack.clear(); assertThat(stack.isEmpty(), equalTo(true)); } + + @Test + public void testPushAndPop() { + MythArrayStack stack = new MythArrayStack<>(3); + stack.push(1); + stack.push(2); + stack.push(3); + + assertThat(stack.size(), equalTo(3)); + assertThat(stack.pop(), equalTo(Integer.valueOf(3))); + assertThat(stack.pop(), equalTo(Integer.valueOf(2))); + assertThat(stack.pop(), equalTo(Integer.valueOf(1))); + assertThat(stack.isEmpty(), equalTo(true)); + } + + @Test + public void testAutoExpand() { + MythArrayStack stack = new MythArrayStack<>(2); + // 测试扩容 + for (int i = 0; i < 5; i++) { + stack.push(i); + } + assertThat(stack.size(), equalTo(5)); + + // 验证数据正确性 + for (int i = 4; i >= 0; i--) { + assertThat(stack.pop(), equalTo(Integer.valueOf(i))); + } + } + + @Test + public void testPeekString() { + MythArrayStack stack = new MythArrayStack<>(); + stack.push("test"); + assertThat(stack.peek(), equalTo("test")); + assertThat(stack.size(), equalTo(1)); // peek不应该移除元素 + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testPopEmpty() { + MythArrayStack stack = new MythArrayStack<>(); + stack.pop(); // 应该抛出异常 + } + + @Test + public void testClearStack() { + MythArrayStack stack = new MythArrayStack<>(); + stack.push(1); + stack.push(2); + stack.clear(); + assertThat(stack.isEmpty(), equalTo(true)); + assertThat(stack.size(), equalTo(0)); + } } diff --git a/algorithms/src/test/java/com/github/kuangcp/string/ACMatcherTest.java b/algorithms/src/test/java/com/github/kuangcp/string/ACMatcherTest.java new file mode 100644 index 00000000..0a278d7c --- /dev/null +++ b/algorithms/src/test/java/com/github/kuangcp/string/ACMatcherTest.java @@ -0,0 +1,33 @@ +package com.github.kuangcp.string; + +import org.junit.Test; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * @author Kuangcp + * 2025-07-07 10:24 + */ +public class ACMatcherTest { + + @Test + public void testMatch() throws Exception { + // 初始化字典 + Set dictionary = new HashSet<>(Arrays.asList("鸡胸", "性病", "感冒")); + ACMatcher matcher = new ACMatcher(); + matcher.buildTrie(dictionary); + +// 测试文本 + String text = "这个鸡胸肉很好吃,但是感冒药吃多了对身体不好,器质性病变需要及时治疗"; + List matches = matcher.findMatches(text); + +// 输出匹配结果 + for (ACMatcher.Match match : matches) { + System.out.printf("找到匹配:%s (位置:%d-%d)%n", + match.word, match.start, match.end); + } + } +} From 0c526d4007fca6d9023249117a0b9314bf2bc04a Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Thu, 7 Aug 2025 14:16:15 +0800 Subject: [PATCH 466/476] java date to json --- .gitignore | 2 + class/pom.xml | 4 ++ .../serialize/json/speed/GsonTool.java | 29 ++++++------ .../serialize/json/speed/JacksonTool.java | 32 +++++++------ .../kuangcp/serialize/json/DateObj.java | 30 ++++++++++++ .../kuangcp/serialize/json/ReadJsonTest.java | 15 ++---- .../kuangcp/serialize/json/WriteJsonTest.java | 46 ++++++++++++++++--- pom.xml | 6 +++ 8 files changed, 119 insertions(+), 45 deletions(-) create mode 100644 class/src/test/java/com/github/kuangcp/serialize/json/DateObj.java diff --git a/.gitignore b/.gitignore index d3c8eb40..4448765e 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,8 @@ logs/ jmh/ jmh_* +git-report/ + # maven # target/ diff --git a/class/pom.xml b/class/pom.xml index 6a9e1c5b..b8c21a73 100644 --- a/class/pom.xml +++ b/class/pom.xml @@ -97,6 +97,10 @@ com.fasterxml.jackson.dataformat jackson-dataformat-smile + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + org.openjdk.jmh diff --git a/class/src/main/java/com/github/kuangcp/serialize/json/speed/GsonTool.java b/class/src/main/java/com/github/kuangcp/serialize/json/speed/GsonTool.java index beedb56a..5070f93a 100644 --- a/class/src/main/java/com/github/kuangcp/serialize/json/speed/GsonTool.java +++ b/class/src/main/java/com/github/kuangcp/serialize/json/speed/GsonTool.java @@ -2,9 +2,10 @@ import com.github.kuangcp.serialize.Person; import com.google.gson.Gson; -import java.util.List; import lombok.extern.slf4j.Slf4j; +import java.util.List; + /** * Created by https://github.com/kuangcp * @@ -13,20 +14,20 @@ @Slf4j public class GsonTool implements JsonTool { - private static final Gson gson = new Gson(); + public static final Gson gson = new Gson(); - @Override - public Person fromJSON(String json, Class target) { - return gson.fromJson(json, target); - } + @Override + public Person fromJSON(String json, Class target) { + return gson.fromJson(json, target); + } - @Override - public String toJSON(List dataList) { - return gson.toJson(dataList); - } + @Override + public String toJSON(List dataList) { + return gson.toJson(dataList); + } - @Override - public String getName() { - return "GSON"; - } + @Override + public String getName() { + return "GSON"; + } } diff --git a/class/src/main/java/com/github/kuangcp/serialize/json/speed/JacksonTool.java b/class/src/main/java/com/github/kuangcp/serialize/json/speed/JacksonTool.java index 90ce6266..856d80cf 100644 --- a/class/src/main/java/com/github/kuangcp/serialize/json/speed/JacksonTool.java +++ b/class/src/main/java/com/github/kuangcp/serialize/json/speed/JacksonTool.java @@ -2,7 +2,9 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import com.github.kuangcp.serialize.Person; + import java.io.IOException; import java.util.List; @@ -13,20 +15,24 @@ */ public class JacksonTool implements JsonTool { - public static final ObjectMapper mapper = new ObjectMapper(); + public static final ObjectMapper mapper = new ObjectMapper(); + + static { + mapper.registerModule(new JavaTimeModule()); + } - @Override - public Person fromJSON(String json, Class target) throws IOException { - return mapper.readValue(json, target); - } + @Override + public Person fromJSON(String json, Class target) throws IOException { + return mapper.readValue(json, target); + } - @Override - public String toJSON(List dataList) throws JsonProcessingException { - return mapper.writeValueAsString(dataList); - } + @Override + public String toJSON(List dataList) throws JsonProcessingException { + return mapper.writeValueAsString(dataList); + } - @Override - public String getName() { - return "Jackson"; - } + @Override + public String getName() { + return "Jackson"; + } } diff --git a/class/src/test/java/com/github/kuangcp/serialize/json/DateObj.java b/class/src/test/java/com/github/kuangcp/serialize/json/DateObj.java new file mode 100644 index 00000000..da52fc37 --- /dev/null +++ b/class/src/test/java/com/github/kuangcp/serialize/json/DateObj.java @@ -0,0 +1,30 @@ +package com.github.kuangcp.serialize.json; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +import java.time.LocalDate; +import java.util.Date; + +/** + * @author Kuangcp + * 2025-08-07 14:02 + */ +@Data +public class DateObj { + /** + * 不带时区 + */ + @JsonFormat(pattern = "yyyy-MM-dd") + public LocalDate day; + + /** + * 带时区 + */ + @JsonFormat(pattern = "yyyy-MM-dd") + public Date dayZone; + + @JsonFormat(pattern = "yyyy-MM-dd") + public Date dayZone2; + +} diff --git a/class/src/test/java/com/github/kuangcp/serialize/json/ReadJsonTest.java b/class/src/test/java/com/github/kuangcp/serialize/json/ReadJsonTest.java index cef0e6d8..81a283ca 100644 --- a/class/src/test/java/com/github/kuangcp/serialize/json/ReadJsonTest.java +++ b/class/src/test/java/com/github/kuangcp/serialize/json/ReadJsonTest.java @@ -4,22 +4,15 @@ import com.github.kuangcp.serialize.json.speed.FastJsonTool; import com.github.kuangcp.serialize.json.speed.GsonTool; import com.github.kuangcp.serialize.json.speed.JacksonTool; - -import java.io.IOException; -import java.util.concurrent.TimeUnit; - import org.junit.Test; -import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.BenchmarkMode; -import org.openjdk.jmh.annotations.Measurement; -import org.openjdk.jmh.annotations.Mode; -import org.openjdk.jmh.annotations.OutputTimeUnit; -import org.openjdk.jmh.annotations.Threads; -import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.annotations.*; import org.openjdk.jmh.runner.Runner; import org.openjdk.jmh.runner.options.Options; import org.openjdk.jmh.runner.options.OptionsBuilder; +import java.io.IOException; +import java.util.concurrent.TimeUnit; + /** * Created by https://github.com/kuangcp * diff --git a/class/src/test/java/com/github/kuangcp/serialize/json/WriteJsonTest.java b/class/src/test/java/com/github/kuangcp/serialize/json/WriteJsonTest.java index e86b1c44..ed2898b9 100644 --- a/class/src/test/java/com/github/kuangcp/serialize/json/WriteJsonTest.java +++ b/class/src/test/java/com/github/kuangcp/serialize/json/WriteJsonTest.java @@ -5,18 +5,19 @@ import com.github.kuangcp.serialize.json.speed.FastJsonTool; import com.github.kuangcp.serialize.json.speed.GsonTool; import com.github.kuangcp.serialize.json.speed.JacksonTool; +import org.junit.Assert; import org.junit.Test; -import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.BenchmarkMode; -import org.openjdk.jmh.annotations.Measurement; -import org.openjdk.jmh.annotations.Mode; -import org.openjdk.jmh.annotations.OutputTimeUnit; -import org.openjdk.jmh.annotations.Threads; -import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.annotations.*; import org.openjdk.jmh.runner.Runner; import org.openjdk.jmh.runner.options.Options; import org.openjdk.jmh.runner.options.OptionsBuilder; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.util.Date; import java.util.List; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; @@ -58,4 +59,35 @@ public void testCompareRead() throws Exception { .output("/tmp/" + WriteJsonTest.class.getSimpleName() + ".log").build(); new Runner(options).run(); } + + @Test + public void testSerializeDate() throws Exception { + DateObj obj = new DateObj(); + obj.day = LocalDate.of(2000, 1, 2); + obj.dayZone = Date.from(LocalDateTime.of(obj.getDay(), LocalTime.MIN).atZone(ZoneOffset.UTC).toInstant()); + obj.dayZone2 = Date.from(LocalDateTime.of(obj.getDay(), LocalTime.MIN).atZone(ZoneId.systemDefault()).toInstant()); + { + String a = JacksonTool.mapper.writeValueAsString(obj); + System.out.println(a); + + DateObj read = JacksonTool.mapper.readValue(a, DateObj.class); + System.out.println(read); + Assert.assertEquals(read.getDayZone(), obj.dayZone); + // 因为Jackson在序列化和反序列化Date对象到ymd字符串 而不是对象时,采用的是UTC, + // 如果从无时区转为Date对象过程中使用的系统+8时区转换就会有时差问题,从天数来看就会看起来少了一天 + // jackson 处理 java.time下的类需要引入 jsr310 依赖 + Assert.assertNotEquals(read.getDayZone2(), obj.dayZone2); + } + { + String json = GsonTool.gson.toJson(obj); + System.out.println(json); + DateObj read = GsonTool.gson.fromJson(json, DateObj.class); + System.out.println(read); + // gson 序列化为了对象,时区未丢 所以正常。 即使调整为ymd格式,和fastjson一样是用系统时区去反序列化,所以不会有问题 + // 但是gson有另一个问题,未指定格式时序列化的字符串是随环境变化(会出现不同机器格式不一致问题),jackson默认是 ISO-8601 + Assert.assertEquals(read.getDayZone(), obj.dayZone); + Assert.assertEquals(read.getDayZone2(), obj.dayZone2); + } + + } } diff --git a/pom.xml b/pom.xml index ffe630ef..708cf927 100644 --- a/pom.xml +++ b/pom.xml @@ -262,6 +262,12 @@ jackson-dataformat-smile ${jackson.version} + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + ${jackson.version} + org.aspectj aspectjrt From 0ba2049a362c8daf1443d97c2873f05800ac252b Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Wed, 3 Sep 2025 11:19:13 +0800 Subject: [PATCH 467/476] future --- java8/pom.xml | 8 + .../kuangcp/future/CompletableFutureTest.java | 7 - .../com/github/kuangcp/future/FutureTest.java | 152 ++++++++++++++++++ .../stream/debug/UsePluginWithDebugTest.java | 27 ---- 4 files changed, 160 insertions(+), 34 deletions(-) create mode 100644 java8/src/test/java/com/github/kuangcp/future/FutureTest.java delete mode 100644 java8/src/test/java/com/github/kuangcp/stream/debug/UsePluginWithDebugTest.java diff --git a/java8/pom.xml b/java8/pom.xml index 64741431..5e307fe9 100644 --- a/java8/pom.xml +++ b/java8/pom.xml @@ -24,6 +24,14 @@ kcp-tool + + com.google.guava + guava + + + io.netty + netty-all + diff --git a/java8/src/test/java/com/github/kuangcp/future/CompletableFutureTest.java b/java8/src/test/java/com/github/kuangcp/future/CompletableFutureTest.java index 67d40bf2..de7cb115 100644 --- a/java8/src/test/java/com/github/kuangcp/future/CompletableFutureTest.java +++ b/java8/src/test/java/com/github/kuangcp/future/CompletableFutureTest.java @@ -172,11 +172,4 @@ private boolean enableRun() { LocalTime now1 = LocalTime.now(); return now1.isAfter(now.plusSeconds(10)); } - - @Test - public void testNow() throws Exception { - // 1726284853554 - // 1685014528 - System.out.println(System.currentTimeMillis()); - } } diff --git a/java8/src/test/java/com/github/kuangcp/future/FutureTest.java b/java8/src/test/java/com/github/kuangcp/future/FutureTest.java new file mode 100644 index 00000000..0ddb6ffd --- /dev/null +++ b/java8/src/test/java/com/github/kuangcp/future/FutureTest.java @@ -0,0 +1,152 @@ +package com.github.kuangcp.future; + +import com.google.common.util.concurrent.AbstractFuture; +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.common.util.concurrent.MoreExecutors; +import io.netty.util.concurrent.DefaultEventExecutorGroup; +import io.netty.util.concurrent.DefaultPromise; +import io.netty.util.concurrent.EventExecutorGroup; +import io.netty.util.concurrent.Future; +import io.netty.util.concurrent.FutureListener; +import io.netty.util.concurrent.GenericFutureListener; +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +/** + * @author Kuangcp + * 2025-09-03 10:28 + */ +@Slf4j +public class FutureTest { + + // JDK Future 不支持设置回调,Guava和Netty有扩展实现 + + /** + * @see DefaultPromise#addListener(GenericFutureListener) 如果未执行结束加入队列,否则直接执行 + */ + @Test + public void testNettyCallback() throws Exception { + long l = System.currentTimeMillis(); + EventExecutorGroup group = new DefaultEventExecutorGroup(4); // 4 threads + Future f = group.submit(() -> { + log.info("开始执行耗时操作..."); + TimeUnit.SECONDS.sleep(3); + return 100; + }); + + f.addListener((FutureListener) objectFuture -> log.info("计算结果::" + objectFuture.get())); + log.info("主线程运算耗时:" + (System.currentTimeMillis() - l) + " ms"); + Thread.currentThread().join(); + } + + @Test + public void testGuavaCallback() throws Exception { + long start = System.currentTimeMillis(); + ListeningExecutorService service = MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor()); + ListenableFuture future = service.submit(() -> { + log.info("开始执行耗时操作..."); + TimeUnit.SECONDS.sleep(3); + return 100; + }); + + Futures.addCallback(future, new FutureCallback() { + public void onSuccess(Integer result) { + log.info("计算结果:{}", result); + } + + public void onFailure(Throwable throwable) { + log.info("异步处理失败 ", throwable); + } + }, service); + + log.info("主线程运算耗时:{} ms", System.currentTimeMillis() - start); + Thread.currentThread().join(); + } + + /** + * @see com.google.common.util.concurrent.AbstractFuture#complete(com.google.common.util.concurrent.AbstractFuture, boolean) 任务完成后执行所有监听器 + * @see AbstractFuture#addListener(Runnable, Executor) 如果已经执行完了就直接执行回调,没有的话,头插式加入监听器链表(CAS保证并发安全) + */ + @Test + public void testGuavaCallbackDelayListen() throws Exception { + long start = System.currentTimeMillis(); + ListeningExecutorService service = MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor()); + ListenableFuture future = service.submit(() -> { + log.info("开始执行耗时操作..."); + TimeUnit.SECONDS.sleep(1); + return 100; + }); + + // 即使处理完成早于添加回调,也能保证会执行回调 + TimeUnit.SECONDS.sleep(4); + + Futures.addCallback(future, new FutureCallback() { + public void onSuccess(Integer result) { + log.info("计算结果:{}", result); + } + + public void onFailure(Throwable throwable) { + log.info("异步处理失败 ", throwable); + } + }, service); + + log.info("主线程运算耗时:{} ms", System.currentTimeMillis() - start); + Thread.currentThread().join(); + } + + @Test + public void testCompletableCallback() throws Exception { + long start = System.currentTimeMillis(); + CompletableFuture completableFuture = CompletableFuture.supplyAsync(() -> { + log.info("执行耗时操作..."); + try { + TimeUnit.SECONDS.sleep(3); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + return 100; + }); + completableFuture.whenComplete((result, e) -> { + log.info("结果:{}", result); + }); + log.info("主线程运算耗时:" + (System.currentTimeMillis() - start) + " ms"); + Thread.currentThread().join(); + } + + @Test + public void testComplexCallback() throws Exception { + long start = System.currentTimeMillis(); + CompletableFuture completableFuture = CompletableFuture.supplyAsync(() -> { + log.info("执行耗时操作..."); + try { + TimeUnit.SECONDS.sleep(3); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + return 100; + }); + completableFuture = completableFuture.thenCompose(i -> CompletableFuture.supplyAsync(() -> { + log.info("在回调中执行耗时操作..."); + try { + TimeUnit.SECONDS.sleep(3); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + return i + 100; + })); + completableFuture.whenComplete((result, e) -> { + log.info("计算结果:" + result); + }); + + log.info("主线程运算耗时:" + (System.currentTimeMillis() - start) + " ms"); + Thread.currentThread().join(); + } +} diff --git a/java8/src/test/java/com/github/kuangcp/stream/debug/UsePluginWithDebugTest.java b/java8/src/test/java/com/github/kuangcp/stream/debug/UsePluginWithDebugTest.java deleted file mode 100644 index 0b25d363..00000000 --- a/java8/src/test/java/com/github/kuangcp/stream/debug/UsePluginWithDebugTest.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.github.kuangcp.stream.debug; - -import java.util.List; -import java.util.Objects; -import java.util.stream.Collectors; -import java.util.stream.Stream; -import org.junit.Test; - -/** - * @author kuangcp on 3/13/19-9:00 PM - * plugin: Java Stream Debugger - */ -public class UsePluginWithDebugTest { - - @Test - public void testFirst() { - Stream.of("A", "B", 1, 2, null).filter(Objects::nonNull).forEach(System.out::println); - } - - @Test - public void testSecond() { - List result = Stream.of("1", "3", "8", "10", "22").map(Integer::parseInt) - .filter(s -> s > 9).map(Integer::byteValue).collect(Collectors.toList()); - - result.forEach(System.out::println); - } -} From d4ec2e0c1c8a7766ab4c1bdff94066eb1b3abbda Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Thu, 11 Dec 2025 11:21:39 +0800 Subject: [PATCH 468/476] pool --- .../java/thread/pool/CusSchedulePool.java | 2 + .../java/thread/pool/RecommendUsePool.java | 75 +++-- .../com/github/kuangcp/juc/PhaseTest.java | 310 ++++++++++++++++++ .../thread/pool/RecommendUsePoolTest.java | 8 +- 4 files changed, 364 insertions(+), 31 deletions(-) create mode 100644 concurrency/src/test/java/com/github/kuangcp/juc/PhaseTest.java diff --git a/concurrency/src/main/java/thread/pool/CusSchedulePool.java b/concurrency/src/main/java/thread/pool/CusSchedulePool.java index 1cea56bb..69ffdf76 100644 --- a/concurrency/src/main/java/thread/pool/CusSchedulePool.java +++ b/concurrency/src/main/java/thread/pool/CusSchedulePool.java @@ -7,6 +7,8 @@ import java.util.concurrent.*; /** + * 任务封装 监控时效 + * * @author Kuangcp * 2024-05-15 09:34 */ diff --git a/concurrency/src/main/java/thread/pool/RecommendUsePool.java b/concurrency/src/main/java/thread/pool/RecommendUsePool.java index dd29c2e2..692b3789 100644 --- a/concurrency/src/main/java/thread/pool/RecommendUsePool.java +++ b/concurrency/src/main/java/thread/pool/RecommendUsePool.java @@ -24,9 +24,8 @@ public class RecommendUsePool { new LinkedBlockingQueue<>(5), new TrackDiscardPolicy()); /** - * 限制最高并发 批量处理任务 - *

- * max 非业务值 仅仅为安全值 + * max为业务实际值即最大并发数 无队列 超出的任务就丢弃 + * 需额外做限流逻辑 */ public static ThreadPoolExecutor limitPool = new ThreadPoolExecutor(0, 20, 60L, TimeUnit.SECONDS, new SynchronousQueue<>(), @@ -34,7 +33,8 @@ public class RecommendUsePool { new ThreadPoolExecutor.AbortPolicy()); /** - * max为业务实际值即最大并发数 + * max为业务实际值即最大并发数 无队列 超出的任务就丢弃 + * 需额外加限流逻辑 */ public static ThreadPoolExecutor limitCachePool = new ThreadPoolExecutor(0, 3, 60L, TimeUnit.SECONDS, new SynchronousQueue<>(), @@ -42,31 +42,52 @@ public class RecommendUsePool { new ThreadPoolExecutor.AbortPolicy()); /** - * IO密集型线程池 快速消费任务和快速回收线程 - *

- * 待执行的任务一旦达到max就会被丢弃,因为队列是零空间的阻塞实现 + * IO型线程池 */ - public static ThreadPoolExecutor cachePool = new ThreadPoolExecutor(0, 100, - 30L, TimeUnit.SECONDS, new SynchronousQueue<>(), - new BasicThreadFactory.Builder().namingPattern("nio-%d").build(), - new ThreadPoolExecutor.AbortPolicy()); + public static class IO { + /** + * IO密集型线程池 快速消费任务和快速回收线程 + *

+ * 待执行的任务一旦达到max就会被丢弃,因为队列是零空间的阻塞实现 + * 任务提交过程: 核心线程从0增长到100,丢弃任务 + * 当任务逐步消费后,空闲的核心线程逐步回收 + */ + public static ThreadPoolExecutor ioPool = new ThreadPoolExecutor(0, 100, + 30L, TimeUnit.SECONDS, new SynchronousQueue<>(), + new BasicThreadFactory.Builder().namingPattern("nio-%d").build(), + new ThreadPoolExecutor.AbortPolicy()); + + /** + * 弹性IO密集型线程池 快速消费快速回收 具有队列 + *

+ * 任务提交过程: 核心线程从0增长到20,然后进队列(平稳消费),当队列满了开始增长核心线程到40个,直到全满丢弃任务 + * 相较 Executors.newCachedThreadPool 的实现是无限线程数,具有风险。 + */ + public static ThreadPoolExecutor coreCachePool = new ThreadPoolExecutor(20, 50, + 8L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(50), + new BasicThreadFactory.Builder().namingPattern("react-%d").build(), + new TrackDiscardPolicy()); + + static { + // 默认不会回收idle超时的 core线程 ,只会回收 core到max部分的临时线程,设置该值以节省资源 + coreCachePool.allowCoreThreadTimeOut(true); + // 初始化默认不会启动core线程,有任务提交才会创建,如果设置这个就会预启动全部core线程 +// coreCachePool.prestartCoreThread(); + } - /** - * IO密集型线程池 快速消费快速回收具有超量任务缓冲的能力 - *

- * 任务总数量小于core时 提交后都会立刻有线程响应,超过core部分会进入队列等待,队列满了就会创建新线程直到达到max 然后丢弃后续的任务 - * Executors.newCachedThreadPool 实现是无限线程数,具有风险。 - */ - public static ThreadPoolExecutor coreCachePool = new ThreadPoolExecutor(20, 50, - 8L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(50), - new BasicThreadFactory.Builder().namingPattern("react-%d").build(), - new TrackDiscardPolicy()); - - static { - // 默认不会回收idle超时的 core线程 ,只会回收 core到max部分的临时线程,设置该值以节省资源 - coreCachePool.allowCoreThreadTimeOut(true); - // 初始化不会启动core线程,有任务提交才会创建 -// coreCachePool.prestartAllCoreThreads(); + /** + * 弹性 IO密集型线程池 平稳的消费任务和回收线程。 + * 任务提交过程: 核心线程从0增长到40,然后进队列(平稳消费),当队列满了开始增长核心线程到100个,直到全满丢弃任务 + * 当任务逐步消费后,空闲的核心线程逐步回收 + */ + public static ThreadPoolExecutor smoothIOPool = new ThreadPoolExecutor(40, 100, + 120L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(500), + new BasicThreadFactory.Builder().namingPattern("nio-%d").build(), + new ThreadPoolExecutor.AbortPolicy()); + + static { + smoothIOPool.allowCoreThreadTimeOut(true); + } } /** diff --git a/concurrency/src/test/java/com/github/kuangcp/juc/PhaseTest.java b/concurrency/src/test/java/com/github/kuangcp/juc/PhaseTest.java new file mode 100644 index 00000000..1b900b5e --- /dev/null +++ b/concurrency/src/test/java/com/github/kuangcp/juc/PhaseTest.java @@ -0,0 +1,310 @@ +package com.github.kuangcp.juc; + +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Phaser; +import java.util.concurrent.ThreadLocalRandom; + +/** + * @author Kuangcp + * 2025-11-13 22:02 + */ +@Slf4j +public class PhaseTest { + + /** + * 演示 Phaser 协调多个线程的多个计算任务 + * 场景:多个线程并行执行三个阶段的计算(平方、立方、求和) + * 要求:每个阶段完成后,所有线程等待其他线程,然后按阶段依次打印结果 + */ + @Test + public void testBasic() throws Exception { + int threadCount = 4; // 线程数量 + int phaseCount = 3; // 阶段数量 + + // 创建 Phaser,注册主线程和所有工作线程 + Phaser phaser = new Phaser(1) { + @Override + protected boolean onAdvance(int phase, int registeredParties) { + // 每个阶段完成时的回调 + log.info("\n========== 阶段 {} 完成 ==========", phase + 1); + return phase >= phaseCount - 1; // 如果完成所有阶段,返回true终止 + } + }; + + // 存储每个线程的结果 + List results = new ArrayList<>(); + + // 创建并启动工作线程 + for (int i = 0; i < threadCount; i++) { + final int threadId = i; + ThreadResult result = new ThreadResult(threadId); + results.add(result); + + phaser.register(); // 注册线程到 Phaser + + Thread thread = new Thread(() -> { + try { + // 阶段1:计算平方 + int value = ThreadLocalRandom.current().nextInt(1, 10); + int square = value * value; + result.setPhase1Value(value); + result.setPhase1Result(square); + log.info("线程-{}: 阶段1完成,输入={},平方={}", threadId, value, square); + phaser.arriveAndAwaitAdvance(); // 等待所有线程完成阶段1 + + // 阶段2:计算立方 + int cube = value * value * value; + result.setPhase2Result(cube); + log.info("线程-{}: 阶段2完成,立方={}", threadId, cube); + phaser.arriveAndAwaitAdvance(); // 等待所有线程完成阶段2 + + // 阶段3:计算总和 + int sum = square + cube; + result.setPhase3Result(sum); + log.info("线程-{}: 阶段3完成,总和={}", threadId, sum); + phaser.arriveAndAwaitAdvance(); // 等待所有线程完成阶段3 + + } catch (Exception e) { + log.error("", e); + } finally { + phaser.arriveAndDeregister(); // 完成并注销 + } + }); + + thread.start(); + } + + // 主线程等待所有阶段完成 + for (int phase = 0; phase < phaseCount; phase++) { + phaser.arriveAndAwaitAdvance(); // 等待当前阶段完成 + log.info("\n--- 阶段 {} 结果汇总 ---", phase + 1); + printPhaseResults(results, phase + 1); + } + + // 等待所有线程完成 + phaser.arriveAndDeregister(); + + // 打印最终汇总 + log.info("\n========== 最终结果汇总 =========="); + for (ThreadResult result : results) { + log.info("线程-{}: 输入={}, 平方={}, 立方={}, 总和={}", + result.threadId, + result.phase1Value, + result.phase1Result, + result.phase2Result, + result.phase3Result); + } + } + + private void printPhaseResults(List results, int phase) { + for (ThreadResult result : results) { + switch (phase) { + case 1: + log.info(" 线程-{}: 平方={}", result.threadId, result.phase1Result); + break; + case 2: + log.info(" 线程-{}: 立方={}", result.threadId, result.phase2Result); + break; + case 3: + log.info(" 线程-{}: 总和={}", result.threadId, result.phase3Result); + break; + } + } + } + + /** + * 演示并行执行但按依赖顺序输出的场景 + * 场景:4个任务 A、B、C、D 可以并行执行 + * 但输出结果时:D依赖C,C依赖B,B依赖A + * 即输出顺序必须是:A -> B -> C -> D + */ + @Test + public void testDependentOutput() throws Exception { + // 创建 Phaser,用于控制输出顺序 + // 4个阶段:phase 0(A输出) -> phase 1(B输出) -> phase 2(C输出) -> phase 3(D输出) + Phaser outputPhaser = new Phaser(1) { + @Override + protected boolean onAdvance(int phase, int registeredParties) { + log.info("========== 输出阶段 {} 完成 ==========", phase + 1); + return phase >= 3; // 完成所有4个输出阶段后终止 + } + }; + + // 任务结果存储 + TaskResult resultA = new TaskResult("A"); + TaskResult resultB = new TaskResult("B"); + TaskResult resultC = new TaskResult("C"); + TaskResult resultD = new TaskResult("D"); + + log.info("========== 开始并行执行任务 =========="); + + // 任务A:独立执行,完成后可以立即输出(phase 0) + outputPhaser.register(); + Thread taskA = new Thread(() -> { + try { + // 模拟任务执行(随机耗时) + int duration = ThreadLocalRandom.current().nextInt(500, 2000); + log.info("任务A: 开始执行,预计耗时 {}ms", duration); + Thread.sleep(duration); + resultA.setResult("任务A的结果: " + duration); + log.info("任务A: 执行完成"); + + // 到达输出阶段0,等待其他任务(如果有)也到达,然后输出 + outputPhaser.arriveAndAwaitAdvance(); + log.info(">>> 输出任务A结果: {}", resultA.getResult()); + + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + log.error("任务A被中断", e); + } finally { + outputPhaser.arriveAndDeregister(); + } + }); + + // 任务B:独立执行,但输出需要等待A(phase 1) + outputPhaser.register(); + Thread taskB = new Thread(() -> { + try { + int duration = ThreadLocalRandom.current().nextInt(500, 2000); + log.info("任务B: 开始执行,预计耗时 {}ms", duration); + Thread.sleep(duration); + resultB.setResult("任务B的结果: " + duration); + log.info("任务B: 执行完成"); + + // 等待A输出完成(phase 0),然后到达phase 1并输出 + outputPhaser.arriveAndAwaitAdvance(); + log.info(">>> 输出任务B结果: {} (依赖A)", resultB.getResult()); + + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + log.error("任务B被中断", e); + } finally { + outputPhaser.arriveAndDeregister(); + } + }); + + // 任务C:独立执行,但输出需要等待B(phase 2) + outputPhaser.register(); + Thread taskC = new Thread(() -> { + try { + int duration = ThreadLocalRandom.current().nextInt(500, 2000); + log.info("任务C: 开始执行,预计耗时 {}ms", duration); + Thread.sleep(duration); + resultC.setResult("任务C的结果: " + duration); + log.info("任务C: 执行完成"); + + // 等待B输出完成(phase 1),然后到达phase 2并输出 + outputPhaser.arriveAndAwaitAdvance(); + log.info(">>> 输出任务C结果: {} (依赖B)", resultC.getResult()); + + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + log.error("任务C被中断", e); + } finally { + outputPhaser.arriveAndDeregister(); + } + }); + + // 任务D:独立执行,但输出需要等待C(phase 3) + outputPhaser.register(); + Thread taskD = new Thread(() -> { + try { + int duration = ThreadLocalRandom.current().nextInt(500, 2000); + log.info("任务D: 开始执行,预计耗时 {}ms", duration); + Thread.sleep(duration); + resultD.setResult("任务D的结果: " + duration); + log.info("任务D: 执行完成"); + + // 等待C输出完成(phase 2),然后到达phase 3并输出 + outputPhaser.arriveAndAwaitAdvance(); + log.info(">>> 输出任务D结果: {} (依赖C)", resultD.getResult()); + + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + log.error("任务D被中断", e); + } finally { + outputPhaser.arriveAndDeregister(); + } + }); + + // 启动所有任务(并行执行) + long startTime = System.currentTimeMillis(); + taskA.start(); + taskB.start(); + taskC.start(); + taskD.start(); + + // 主线程等待所有输出阶段完成 + for (int phase = 0; phase < 4; phase++) { + outputPhaser.arriveAndAwaitAdvance(); + } + + // 等待所有线程完成 + outputPhaser.arriveAndDeregister(); + + taskA.join(); + taskB.join(); + taskC.join(); + taskD.join(); + + long totalTime = System.currentTimeMillis() - startTime; + log.info("\n========== 所有任务完成 =========="); + log.info("总耗时: {}ms", totalTime); + log.info("注意:虽然任务并行执行,但输出顺序严格按照 A -> B -> C -> D"); + } + + // 任务结果封装类 + static class TaskResult { + private final String taskName; + private String result; + + TaskResult(String taskName) { + this.taskName = taskName; + } + + void setResult(String result) { + this.result = result; + } + + String getResult() { + return result; + } + + String getTaskName() { + return taskName; + } + } + + // 线程结果封装类 + static class ThreadResult { + final int threadId; + int phase1Value; + int phase1Result; + int phase2Result; + int phase3Result; + + ThreadResult(int threadId) { + this.threadId = threadId; + } + + void setPhase1Value(int value) { + this.phase1Value = value; + } + + void setPhase1Result(int result) { + this.phase1Result = result; + } + + void setPhase2Result(int result) { + this.phase2Result = result; + } + + void setPhase3Result(int result) { + this.phase3Result = result; + } + } +} diff --git a/concurrency/src/test/java/thread/pool/RecommendUsePoolTest.java b/concurrency/src/test/java/thread/pool/RecommendUsePoolTest.java index df12a2be..4bb6e534 100644 --- a/concurrency/src/test/java/thread/pool/RecommendUsePoolTest.java +++ b/concurrency/src/test/java/thread/pool/RecommendUsePoolTest.java @@ -220,7 +220,7 @@ private static void addTask(RouteContext ctx, AtomicInteger counter, LinkedBlock @Test public void testEmptyCoreThread() throws Exception { - RecommendUsePool.coreCachePool.getActiveCount(); + System.out.println(RecommendUsePool.IO.coreCachePool.getActiveCount()); Thread.currentThread().join(); } @@ -239,7 +239,7 @@ public void testKillCoreThread() throws Exception { // 注意当此处提交任务的频率 当 taskTime/submitTime < core 时任务消费正常,当大于core就会开始使用到临时线程, 当大于max以及队列满时就会开始丢弃任务了 Thread.sleep(submitTime); } - RecommendUsePool.coreCachePool.execute(new RecommendUsePool.Task("task-" + i, () -> { + RecommendUsePool.IO.coreCachePool.execute(new RecommendUsePool.Task("task-" + i, () -> { try { Thread.sleep(taskTime); } catch (InterruptedException e) { @@ -250,7 +250,7 @@ public void testKillCoreThread() throws Exception { Thread.currentThread().join(10000); // 此时已经销毁了所有的core线程(大于idle的8s),下面的任务会开启新的core - RecommendUsePool.coreCachePool.execute(() -> { + RecommendUsePool.IO.coreCachePool.execute(() -> { try { Thread.sleep(10000); log.info("Last task"); @@ -264,7 +264,7 @@ public void testKillCoreThread() throws Exception { /** - * @see CompletableFutureTest + * @see com.github.kuangcp.future.CompletableFutureTest */ @Test public void testTmpNewAsyncPool() throws Exception { From 9b65e3d4d07779e1e5e8dec580fc51998feae2ac Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Thu, 11 Dec 2025 11:39:46 +0800 Subject: [PATCH 469/476] pool test --- .../java/thread/pool/RecommendUsePool.java | 17 +++++- .../thread/pool/RecommendUsePoolTest.java | 53 +++++++++++++++++++ 2 files changed, 69 insertions(+), 1 deletion(-) diff --git a/concurrency/src/main/java/thread/pool/RecommendUsePool.java b/concurrency/src/main/java/thread/pool/RecommendUsePool.java index 692b3789..b549e4f5 100644 --- a/concurrency/src/main/java/thread/pool/RecommendUsePool.java +++ b/concurrency/src/main/java/thread/pool/RecommendUsePool.java @@ -83,7 +83,7 @@ public static class IO { public static ThreadPoolExecutor smoothIOPool = new ThreadPoolExecutor(40, 100, 120L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(500), new BasicThreadFactory.Builder().namingPattern("nio-%d").build(), - new ThreadPoolExecutor.AbortPolicy()); + new TrackDiscardPolicy()); static { smoothIOPool.allowCoreThreadTimeOut(true); @@ -129,5 +129,20 @@ public void run() { } } + public static String stat(ThreadPoolExecutor threadPool) { + return "| 运行中 | 待运行 | 总任务 | 核心 | 活跃 | 最大 |\n| " + + String.format("%4s", threadPool.getActiveCount() > 0 ? "🚨" + threadPool.getActiveCount() : threadPool.getActiveCount()) + + " | " + + String.format("%4s", !threadPool.getQueue().isEmpty() ? "🚨" + threadPool.getQueue().size() : threadPool.getQueue().size()) + + " | " + + String.format("%4d", threadPool.getTaskCount()) + + " | " + + String.format("%4d", threadPool.getCorePoolSize()) + + " | " + + String.format("%4d", threadPool.getPoolSize()) + + " | " + + String.format("%4d", threadPool.getMaximumPoolSize()) + + " |\n"; + } } diff --git a/concurrency/src/test/java/thread/pool/RecommendUsePoolTest.java b/concurrency/src/test/java/thread/pool/RecommendUsePoolTest.java index 4bb6e534..62e3f9c0 100644 --- a/concurrency/src/test/java/thread/pool/RecommendUsePoolTest.java +++ b/concurrency/src/test/java/thread/pool/RecommendUsePoolTest.java @@ -308,4 +308,57 @@ public void testTmpNewAsyncPool() throws Exception { log.info("start"); Thread.currentThread().join(); } + + /** + * 测试直到任务丢弃的过程 + */ + @Test + public void testSmoothPoolFull() throws Exception { + ScheduledExecutorService customScheduler = new CusSchedulePool(1, "sch-"); + customScheduler.scheduleAtFixedRate(() -> { + System.out.println(RecommendUsePool.stat(RecommendUsePool.IO.smoothIOPool)); + }, 1000, 1000, TimeUnit.MILLISECONDS); + + for (int i = 0; i < 1000; i++) { + int finalI = i; + RecommendUsePool.IO.smoothIOPool.execute(() -> { + try { + TimeUnit.MILLISECONDS.sleep(10_000); + } catch (InterruptedException e) { + + } +// System.out.print(finalI + " "); + }); + TimeUnit.MILLISECONDS.sleep(20); + } + + + Thread.currentThread().join(); + } + + /** + * 核心线程满到回收到0的过程 + */ + @Test + public void testSmoothPoolIdle() throws Exception { + ScheduledExecutorService customScheduler = new CusSchedulePool(1, "sch-"); + customScheduler.scheduleAtFixedRate(() -> { + System.out.println(RecommendUsePool.stat(RecommendUsePool.IO.smoothIOPool)); + }, 1000, 1000, TimeUnit.MILLISECONDS); + + for (int i = 0; i < 100; i++) { + int finalI = i; + RecommendUsePool.IO.smoothIOPool.execute(() -> { + try { + TimeUnit.MILLISECONDS.sleep(10_000); + } catch (InterruptedException e) { + + } + System.out.print(finalI + " "); + }); + TimeUnit.MILLISECONDS.sleep(20); + } + + Thread.currentThread().join(); + } } From ca287f0eae87614cdeda64d06c58cb6aed4d0478 Mon Sep 17 00:00:00 2001 From: Kcp Date: Mon, 15 Dec 2025 11:45:51 +0800 Subject: [PATCH 470/476] datetime --- .../java/syntax/doubles/DoubleConstTest.java | 44 ++- .../kuangcp/time/DateApiComparisonTest.java | 293 +++++++++++++++++ .../DateTimeOperationsComparisonTest.java | 308 ++++++++++++++++++ .../time/Java8DateApiAdvantagesTest.java | 294 +++++++++++++++++ 4 files changed, 935 insertions(+), 4 deletions(-) create mode 100644 java8/src/test/java/com/github/kuangcp/time/DateApiComparisonTest.java create mode 100644 java8/src/test/java/com/github/kuangcp/time/DateTimeOperationsComparisonTest.java create mode 100644 java8/src/test/java/com/github/kuangcp/time/Java8DateApiAdvantagesTest.java diff --git a/class/src/test/java/syntax/doubles/DoubleConstTest.java b/class/src/test/java/syntax/doubles/DoubleConstTest.java index 53a70be9..d146dd0f 100644 --- a/class/src/test/java/syntax/doubles/DoubleConstTest.java +++ b/class/src/test/java/syntax/doubles/DoubleConstTest.java @@ -11,8 +11,44 @@ @Slf4j public class DoubleConstTest { - @Test - public void testExtremum(){ - log.info("{} {}", ShowBinary.toBinary(Double.MIN_VALUE), ShowBinary.toBinary(Double.MIN_NORMAL)); - } + @Test + public void testExtremum() { + log.info("{} {}", ShowBinary.toBinary(Double.MIN_VALUE), ShowBinary.toBinary(Double.MIN_NORMAL)); + } + + @Test + public void testCompare() throws Exception { + System.out.println(0.1 + 0.2 == 0.3); + } + + @Test + public void testIntPerformance() throws Exception { + StringBuilder buf = new StringBuilder(); + for (int j = 0; j < 10; j++) { + int capacity = 32; + int total = 0; + long s1 = System.currentTimeMillis(); + int full = 2000000000; + for (int i = 0; i < full + j; i++) { + int re = (int) (capacity * 0.7191726); + total += re; + } +// System.out.println(total); + long e1 = System.currentTimeMillis(); + buf.append(total); + System.out.println("-- " + (e1 - s1)); + + total = 0; + long s2 = System.currentTimeMillis(); + for (int i = 0; i < full + j; i++) { + int re = (int) (capacity * 0.75); + total += re; + } + long e2 = System.currentTimeMillis(); + buf.append(total); + System.out.println((e2 - s2)+"--"); +// System.out.println(total); + } + System.out.println(buf.substring(0, 20)); + } } diff --git a/java8/src/test/java/com/github/kuangcp/time/DateApiComparisonTest.java b/java8/src/test/java/com/github/kuangcp/time/DateApiComparisonTest.java new file mode 100644 index 00000000..4b8d02f8 --- /dev/null +++ b/java8/src/test/java/com/github/kuangcp/time/DateApiComparisonTest.java @@ -0,0 +1,293 @@ +package com.github.kuangcp.time; + +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; + +import java.text.SimpleDateFormat; +import java.time.*; +import java.time.format.DateTimeFormatter; +import java.time.temporal.ChronoUnit; +import java.util.Calendar; +import java.util.Date; + +/** + * Java 8 新日期API vs 传统日期API 对比测试 + * + * @author kuangcp + */ +@Slf4j +public class DateApiComparisonTest { + + /** + * 获取当前时间对比 + */ + @Test + public void testGetCurrentTime() { + log.info("=== 获取当前时间对比 ==="); + + // 传统API + Date now = new Date(); + Calendar calendar = Calendar.getInstance(); + + // Java 8 API + LocalDateTime localDateTime = LocalDateTime.now(); + LocalDate localDate = LocalDate.now(); + LocalTime localTime = LocalTime.now(); + Instant instant = Instant.now(); + + log.info("传统API - Date: {}", now); + log.info("传统API - Calendar: {}", calendar.getTime()); + log.info("Java 8 - LocalDateTime: {}", localDateTime); + log.info("Java 8 - LocalDate: {}", localDate); + log.info("Java 8 - LocalTime: {}", localTime); + log.info("Java 8 - Instant: {}", instant); + } + + /** + * 创建指定日期时间对比 + */ + @Test + public void testCreateSpecificDateTime() { + log.info("=== 创建指定日期时间对比 ==="); + + // 传统API - 创建2023年12月25日 15:30:45 + Calendar calendar = Calendar.getInstance(); + calendar.set(2023, Calendar.DECEMBER, 25, 15, 30, 45); + calendar.set(Calendar.MILLISECOND, 0); + Date specificDate = calendar.getTime(); + + // Java 8 API + LocalDateTime specificDateTime = LocalDateTime.of(2023, 12, 25, 15, 30, 45); + LocalDate specificDateOnly = LocalDate.of(2023, 12, 25); + LocalTime specificTimeOnly = LocalTime.of(15, 30, 45); + + log.info("传统API - 指定日期时间: {}", specificDate); + log.info("Java 8 - LocalDateTime: {}", specificDateTime); + log.info("Java 8 - LocalDate: {}", specificDateOnly); + log.info("Java 8 - LocalTime: {}", specificTimeOnly); + } + + /** + * 日期时间计算对比 + */ + @Test + public void testDateTimeCalculation() { + log.info("=== 日期时间计算对比 ==="); + + // 传统API - 加一天 + Calendar calendar = Calendar.getInstance(); + calendar.add(Calendar.DAY_OF_MONTH, 1); + Date tomorrow = calendar.getTime(); + + // 传统API - 减一个月 + calendar.add(Calendar.MONTH, -1); + Date lastMonth = calendar.getTime(); + + // Java 8 API - 加一天 + LocalDateTime tomorrow8 = LocalDateTime.now().plusDays(1); + + // Java 8 API - 减一个月 + LocalDateTime lastMonth8 = LocalDateTime.now().minusMonths(1); + + // Java 8 API - 更灵活的计算 + LocalDateTime nextWeek = LocalDateTime.now().plus(1, ChronoUnit.WEEKS); + LocalDateTime nextYear = LocalDateTime.now().plus(1, ChronoUnit.YEARS); + + log.info("传统API - 明天: {}", tomorrow); + log.info("传统API - 上个月: {}", lastMonth); + log.info("Java 8 - 明天: {}", tomorrow8); + log.info("Java 8 - 上个月: {}", lastMonth8); + log.info("Java 8 - 下周: {}", nextWeek); + log.info("Java 8 - 明年: {}", nextYear); + } + + /** + * 日期时间比较对比 + */ + @Test + public void testDateTimeComparison() { + log.info("=== 日期时间比较对比 ==="); + + // 传统API + Calendar cal1 = Calendar.getInstance(); + Calendar cal2 = Calendar.getInstance(); + cal2.add(Calendar.DAY_OF_MONTH, 1); + + boolean isBefore = cal1.before(cal2); + boolean isAfter = cal1.after(cal2); + int comparison = cal1.compareTo(cal2); + + // Java 8 API + LocalDateTime now = LocalDateTime.now(); + LocalDateTime tomorrow = now.plusDays(1); + + boolean isBefore8 = now.isBefore(tomorrow); + boolean isAfter8 = now.isAfter(tomorrow); + boolean isEqual8 = now.isEqual(tomorrow); + + log.info("传统API - isBefore: {}, isAfter: {}, comparison: {}", isBefore, isAfter, comparison); + log.info("Java 8 - isBefore: {}, isAfter: {}, isEqual: {}", isBefore8, isAfter8, isEqual8); + } + + /** + * 格式化对比 + */ + @Test + public void testFormatting() { + log.info("=== 格式化对比 ==="); + + // 传统API + Date now = new Date(); + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + String formattedDate = sdf.format(now); + + // Java 8 API + LocalDateTime now8 = LocalDateTime.now(); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + String formattedDate8 = now8.format(formatter); + + // Java 8 预定义格式 + String isoDate = now8.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME); + String customFormat = now8.format(DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH时mm分ss秒")); + + log.info("传统API - 格式化: {}", formattedDate); + log.info("Java 8 - 格式化: {}", formattedDate8); + log.info("Java 8 - ISO格式: {}", isoDate); + log.info("Java 8 - 中文格式: {}", customFormat); + } + + /** + * 解析字符串对比 + */ + @Test + public void testParsing() { + log.info("=== 解析字符串对比 ==="); + + String dateStr = "2023-12-25 15:30:45"; + + try { + // 传统API + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + Date parsedDate = sdf.parse(dateStr); + + // Java 8 API + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + LocalDateTime parsedDateTime = LocalDateTime.parse(dateStr, formatter); + + log.info("传统API - 解析结果: {}", parsedDate); + log.info("Java 8 - 解析结果: {}", parsedDateTime); + + } catch (Exception e) { + log.error("解析失败", e); + } + } + + /** + * 获取日期时间组件对比 + */ + @Test + public void testGetComponents() { + log.info("=== 获取日期时间组件对比 ==="); + + // 传统API + Calendar calendar = Calendar.getInstance(); + int year = calendar.get(Calendar.YEAR); + int month = calendar.get(Calendar.MONTH) + 1; // 注意:Calendar月份从0开始 + int day = calendar.get(Calendar.DAY_OF_MONTH); + int hour = calendar.get(Calendar.HOUR_OF_DAY); + int minute = calendar.get(Calendar.MINUTE); + int second = calendar.get(Calendar.SECOND); + + // Java 8 API + LocalDateTime now = LocalDateTime.now(); + int year8 = now.getYear(); + int month8 = now.getMonthValue(); + int day8 = now.getDayOfMonth(); + int hour8 = now.getHour(); + int minute8 = now.getMinute(); + int second8 = now.getSecond(); + + log.info("传统API - 年:{}, 月:{}, 日:{}, 时:{}, 分:{}, 秒:{}", + year, month, day, hour, minute, second); + log.info("Java 8 - 年:{}, 月:{}, 日:{}, 时:{}, 分:{}, 秒:{}", + year8, month8, day8, hour8, minute8, second8); + } + + /** + * 时区处理对比 + */ + @Test + public void testTimeZoneHandling() { + log.info("=== 时区处理对比 ==="); + + // 传统API + Calendar calendar = Calendar.getInstance(); + calendar.setTimeZone(java.util.TimeZone.getTimeZone("Asia/Shanghai")); + Date shanghaiTime = calendar.getTime(); + + // Java 8 API + ZonedDateTime shanghaiDateTime = ZonedDateTime.now(ZoneId.of("Asia/Shanghai")); + ZonedDateTime utcDateTime = ZonedDateTime.now(ZoneId.of("UTC")); + + log.info("传统API - 上海时间: {}", shanghaiTime); + log.info("Java 8 - 上海时间: {}", shanghaiDateTime); + log.info("Java 8 - UTC时间: {}", utcDateTime); + log.info("Java 8 - 时区转换: {}", shanghaiDateTime.withZoneSameInstant(ZoneId.of("UTC"))); + } + + /** + * 不可变性对比 + */ + @Test + public void testImmutability() { + log.info("=== 不可变性对比 ==="); + + // 传统API - Date是可变的 + Date originalDate = new Date(); + Date modifiedDate = originalDate; + modifiedDate.setTime(originalDate.getTime() + 86400000); // 加一天 + + log.info("传统API - 原始日期: {}", originalDate); + log.info("传统API - 修改后日期: {}", modifiedDate); + log.info("传统API - 原始日期是否被修改: {}", originalDate.equals(modifiedDate)); + + // Java 8 API - 不可变对象 + LocalDateTime originalDateTime = LocalDateTime.now(); + LocalDateTime modifiedDateTime = originalDateTime.plusDays(1); + + log.info("Java 8 - 原始日期时间: {}", originalDateTime); + log.info("Java 8 - 修改后日期时间: {}", modifiedDateTime); + log.info("Java 8 - 原始日期时间是否被修改: {}", originalDateTime.equals(modifiedDateTime)); + } + + /** + * 性能对比测试 + */ + @Test + public void testPerformance() { + log.info("=== 性能对比测试 ==="); + + int iterations = 100000; + + // 传统API性能测试 + long startTime = System.currentTimeMillis(); + for (int i = 0; i < iterations; i++) { + Calendar calendar = Calendar.getInstance(); + calendar.add(Calendar.DAY_OF_MONTH, i); + Date date = calendar.getTime(); + } + long traditionalTime = System.currentTimeMillis() - startTime; + + // Java 8 API性能测试 + startTime = System.currentTimeMillis(); + for (int i = 0; i < iterations; i++) { + LocalDateTime dateTime = LocalDateTime.now().plusDays(i); + } + long java8Time = System.currentTimeMillis() - startTime; + + log.info("传统API - {}次操作耗时: {}ms", iterations, traditionalTime); + log.info("Java 8 API - {}次操作耗时: {}ms", iterations, java8Time); + log.info("性能提升: {}%", ((double)(traditionalTime - java8Time) / traditionalTime) * 100); + } +} + diff --git a/java8/src/test/java/com/github/kuangcp/time/DateTimeOperationsComparisonTest.java b/java8/src/test/java/com/github/kuangcp/time/DateTimeOperationsComparisonTest.java new file mode 100644 index 00000000..7c96a760 --- /dev/null +++ b/java8/src/test/java/com/github/kuangcp/time/DateTimeOperationsComparisonTest.java @@ -0,0 +1,308 @@ +package com.github.kuangcp.time; + +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; + +import java.text.SimpleDateFormat; +import java.time.*; +import java.time.format.DateTimeFormatter; +import java.time.temporal.ChronoUnit; +import java.time.temporal.TemporalAdjusters; +import java.util.Calendar; +import java.util.Date; + +/** + * 日期时间操作场景对比测试 + * + * @author kuangcp + */ +@Slf4j +public class DateTimeOperationsComparisonTest { + + /** + * 获取月份第一天和最后一天对比 + */ + @Test + public void testMonthBoundaries() { + log.info("=== 获取月份边界对比 ==="); + + // 传统API - 获取当前月份第一天 + Calendar calendar = Calendar.getInstance(); + calendar.set(Calendar.DAY_OF_MONTH, 1); + calendar.set(Calendar.HOUR_OF_DAY, 0); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.SECOND, 0); + calendar.set(Calendar.MILLISECOND, 0); + Date firstDayOfMonth = calendar.getTime(); + + // 传统API - 获取当前月份最后一天 + calendar.set(Calendar.DAY_OF_MONTH, calendar.getActualMaximum(Calendar.DAY_OF_MONTH)); + calendar.set(Calendar.HOUR_OF_DAY, 23); + calendar.set(Calendar.MINUTE, 59); + calendar.set(Calendar.SECOND, 59); + calendar.set(Calendar.MILLISECOND, 999); + Date lastDayOfMonth = calendar.getTime(); + + // Java 8 API - 获取当前月份第一天 + LocalDateTime firstDay8 = LocalDateTime.now().withDayOfMonth(1).withHour(0).withMinute(0).withSecond(0).withNano(0); + + // Java 8 API - 获取当前月份最后一天 + LocalDateTime lastDay8 = LocalDateTime.now().with(TemporalAdjusters.lastDayOfMonth()) + .withHour(23).withMinute(59).withSecond(59).withNano(999999999); + + log.info("传统API - 月份第一天: {}", firstDayOfMonth); + log.info("传统API - 月份最后一天: {}", lastDayOfMonth); + log.info("Java 8 - 月份第一天: {}", firstDay8); + log.info("Java 8 - 月份最后一天: {}", lastDay8); + } + + /** + * 计算两个日期之间的差值对比 + */ + @Test + public void testDateDifference() { + log.info("=== 计算日期差值对比 ==="); + + // 传统API - 计算两个日期之间的天数差 + Calendar cal1 = Calendar.getInstance(); + Calendar cal2 = Calendar.getInstance(); + cal2.add(Calendar.DAY_OF_MONTH, 10); + + long diffInMillis = cal2.getTimeInMillis() - cal1.getTimeInMillis(); + long diffInDays = diffInMillis / (24 * 60 * 60 * 1000); + + // Java 8 API - 计算两个日期之间的差值 + LocalDate date1 = LocalDate.now(); + LocalDate date2 = date1.plusDays(10); + + long diffInDays8 = ChronoUnit.DAYS.between(date1, date2); + long diffInWeeks8 = ChronoUnit.WEEKS.between(date1, date2); + long diffInMonths8 = ChronoUnit.MONTHS.between(date1, date2); + + log.info("传统API - 天数差: {}", diffInDays); + log.info("Java 8 - 天数差: {}", diffInDays8); + log.info("Java 8 - 周数差: {}", diffInWeeks8); + log.info("Java 8 - 月数差: {}", diffInMonths8); + } + + /** + * 工作日计算对比 + */ + @Test + public void testWorkingDays() { + log.info("=== 工作日计算对比 ==="); + + // 传统API - 计算下一个工作日 + Calendar calendar = Calendar.getInstance(); + do { + calendar.add(Calendar.DAY_OF_MONTH, 1); + } while (calendar.get(Calendar.DAY_OF_WEEK) == Calendar.SATURDAY || + calendar.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY); + Date nextWorkingDay = calendar.getTime(); + + // Java 8 API - 计算下一个工作日 + LocalDate nextWorkingDay8 = LocalDate.now(); + do { + nextWorkingDay8 = nextWorkingDay8.plusDays(1); + } while (nextWorkingDay8.getDayOfWeek() == DayOfWeek.SATURDAY || + nextWorkingDay8.getDayOfWeek() == DayOfWeek.SUNDAY); + + log.info("传统API - 下一个工作日: {}", nextWorkingDay); + log.info("Java 8 - 下一个工作日: {}", nextWorkingDay8); + } + + /** + * 季度计算对比 + */ + @Test + public void testQuarterCalculation() { + log.info("=== 季度计算对比 ==="); + + // 传统API - 获取当前季度 + Calendar calendar = Calendar.getInstance(); + int month = calendar.get(Calendar.MONTH); + int quarter = (month / 3) + 1; + + // 传统API - 获取季度第一天 + calendar.set(Calendar.MONTH, (quarter - 1) * 3); + calendar.set(Calendar.DAY_OF_MONTH, 1); + calendar.set(Calendar.HOUR_OF_DAY, 0); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.SECOND, 0); + calendar.set(Calendar.MILLISECOND, 0); + Date quarterStart = calendar.getTime(); + + // Java 8 API - 获取当前季度 + LocalDate now = LocalDate.now(); + int quarter8 = (now.getMonthValue() - 1) / 3 + 1; + + // Java 8 API - 获取季度第一天 + LocalDate quarterStart8 = now.withMonth((quarter8 - 1) * 3 + 1).withDayOfMonth(1); + + log.info("传统API - 当前季度: Q{}", quarter); + log.info("传统API - 季度第一天: {}", quarterStart); + log.info("Java 8 - 当前季度: Q{}", quarter8); + log.info("Java 8 - 季度第一天: {}", quarterStart8); + } + + /** + * 年龄计算对比 + */ + @Test + public void testAgeCalculation() { + log.info("=== 年龄计算对比 ==="); + + // 传统API - 计算年龄 + Calendar birthDate = Calendar.getInstance(); + birthDate.set(1990, Calendar.JANUARY, 15); + + Calendar today = Calendar.getInstance(); + int age = today.get(Calendar.YEAR) - birthDate.get(Calendar.YEAR); + if (today.get(Calendar.DAY_OF_YEAR) < birthDate.get(Calendar.DAY_OF_YEAR)) { + age--; + } + + // Java 8 API - 计算年龄 + LocalDate birthDate8 = LocalDate.of(1990, 1, 15); + LocalDate today8 = LocalDate.now(); + long age8 = ChronoUnit.YEARS.between(birthDate8, today8); + + log.info("传统API - 年龄: {}岁", age); + log.info("Java 8 - 年龄: {}岁", age8); + } + + /** + * 时间间隔计算对比 + */ + @Test + public void testTimeInterval() { + log.info("=== 时间间隔计算对比 ==="); + + // 传统API - 计算两个时间点之间的间隔 + Calendar startTime = Calendar.getInstance(); + startTime.set(Calendar.HOUR_OF_DAY, 9); + startTime.set(Calendar.MINUTE, 0); + startTime.set(Calendar.SECOND, 0); + + Calendar endTime = Calendar.getInstance(); + endTime.set(Calendar.HOUR_OF_DAY, 17); + endTime.set(Calendar.MINUTE, 30); + endTime.set(Calendar.SECOND, 0); + + long diffInMillis = endTime.getTimeInMillis() - startTime.getTimeInMillis(); + long hours = diffInMillis / (60 * 60 * 1000); + long minutes = (diffInMillis % (60 * 60 * 1000)) / (60 * 1000); + + // Java 8 API - 计算时间间隔 + LocalTime startTime8 = LocalTime.of(9, 0); + LocalTime endTime8 = LocalTime.of(17, 30); + + Duration duration = Duration.between(startTime8, endTime8); + long hours8 = duration.toHours(); + long minutes8 = duration.toMinutes() % 60; + + log.info("传统API - 工作时间: {}小时{}分钟", hours, minutes); + log.info("Java 8 - 工作时间: {}小时{}分钟", hours8, minutes8); + } + + /** + * 日期验证对比 + */ + @Test + public void testDateValidation() { + log.info("=== 日期验证对比 ==="); + + // 传统API - 验证日期有效性 + Calendar calendar = Calendar.getInstance(); + calendar.setLenient(false); + boolean isValidTraditional = true; + try { + calendar.set(2023, Calendar.FEBRUARY, 30); // 无效日期 + calendar.getTime(); + } catch (Exception e) { + isValidTraditional = false; + } + + // Java 8 API - 验证日期有效性 + boolean isValidJava8 = true; + try { + LocalDate.of(2023, 2, 30); // 无效日期 + } catch (DateTimeException e) { + isValidJava8 = false; + } + + log.info("传统API - 2023年2月30日是否有效: {}", isValidTraditional); + log.info("Java 8 - 2023年2月30日是否有效: {}", isValidJava8); + } + + /** + * 夏令时处理对比 + */ + @Test + public void testDaylightSavingTime() { + log.info("=== 夏令时处理对比 ==="); + + // 传统API - 夏令时处理 + Calendar calendar = Calendar.getInstance(); + calendar.setTimeZone(java.util.TimeZone.getTimeZone("America/New_York")); + calendar.set(2023, Calendar.MARCH, 12, 2, 30, 0); // 夏令时开始时间 + Date dstStart = calendar.getTime(); + + // Java 8 API - 夏令时处理 + ZonedDateTime dstStart8 = ZonedDateTime.of(2023, 3, 12, 2, 30, 0, 0, + ZoneId.of("America/New_York")); + + log.info("传统API - 夏令时开始: {}", dstStart); + log.info("Java 8 - 夏令时开始: {}", dstStart8); + log.info("Java 8 - 是否夏令时: {}", dstStart8.getZone().getRules().isDaylightSavings(dstStart8.toInstant())); + } + + /** + * 日期范围检查对比 + */ + @Test + public void testDateRangeCheck() { + log.info("=== 日期范围检查对比 ==="); + + // 传统API - 检查日期是否在范围内 + Calendar startDate = Calendar.getInstance(); + startDate.set(2023, Calendar.JANUARY, 1); + + Calendar endDate = Calendar.getInstance(); + endDate.set(2023, Calendar.DECEMBER, 31); + + Calendar checkDate = Calendar.getInstance(); + checkDate.set(2023, Calendar.JUNE, 15); + + boolean isInRange = checkDate.after(startDate) && checkDate.before(endDate); + + // Java 8 API - 检查日期是否在范围内 + LocalDate startDate8 = LocalDate.of(2023, 1, 1); + LocalDate endDate8 = LocalDate.of(2023, 12, 31); + LocalDate checkDate8 = LocalDate.of(2023, 6, 15); + + boolean isInRange8 = checkDate8.isAfter(startDate8) && checkDate8.isBefore(endDate8); + + log.info("传统API - 日期是否在范围内: {}", isInRange); + log.info("Java 8 - 日期是否在范围内: {}", isInRange8); + } + + /** + * 线程安全性对比 + */ + @Test + public void testThreadSafety() { + log.info("=== 线程安全性对比 ==="); + + // 传统API - SimpleDateFormat不是线程安全的 + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + + // Java 8 API - DateTimeFormatter是线程安全的 + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + + log.info("传统API - SimpleDateFormat线程安全: false"); + log.info("Java 8 - DateTimeFormatter线程安全: true"); + log.info("建议: 在多线程环境中使用Java 8的DateTimeFormatter"); + } +} + diff --git a/java8/src/test/java/com/github/kuangcp/time/Java8DateApiAdvantagesTest.java b/java8/src/test/java/com/github/kuangcp/time/Java8DateApiAdvantagesTest.java new file mode 100644 index 00000000..3d0ddba7 --- /dev/null +++ b/java8/src/test/java/com/github/kuangcp/time/Java8DateApiAdvantagesTest.java @@ -0,0 +1,294 @@ +package com.github.kuangcp.time; + +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; + +import java.time.*; +import java.time.format.DateTimeFormatter; +import java.time.temporal.ChronoUnit; +import java.time.temporal.TemporalAdjusters; +import java.util.Calendar; +import java.util.Date; + +/** + * Java 8 日期API主要优势展示 + * + * @author kuangcp + */ +@Slf4j +public class Java8DateApiAdvantagesTest { + + /** + * 优势1: 更清晰的API设计 + */ + @Test + public void testClearApiDesign() { + log.info("=== 优势1: 更清晰的API设计 ==="); + + // 传统API - 月份从0开始,容易出错 + Calendar calendar = Calendar.getInstance(); + calendar.set(2023, Calendar.DECEMBER, 25); // 注意:DECEMBER是11,不是12 + Date date = calendar.getTime(); + + // Java 8 API - 月份从1开始,更直观 + LocalDate localDate = LocalDate.of(2023, 12, 25); // 直接使用12表示12月 + + log.info("传统API - 设置12月25日: {}", date); + log.info("Java 8 - 设置12月25日: {}", localDate); + log.info("优势: Java 8 API月份从1开始,更符合人类习惯"); + } + + /** + * 优势2: 不可变性 + */ + @Test + public void testImmutability() { + log.info("=== 优势2: 不可变性 ==="); + + // 传统API - 可变对象,容易产生副作用 + Date originalDate = new Date(); + Date modifiedDate = originalDate; + modifiedDate.setTime(originalDate.getTime() + 86400000); // 修改会影响原对象 + + // Java 8 API - 不可变对象,每次操作返回新对象 + LocalDateTime originalDateTime = LocalDateTime.now(); + LocalDateTime modifiedDateTime = originalDateTime.plusDays(1); // 返回新对象,原对象不变 + + log.info("传统API - 原始日期: {}", originalDate); + log.info("传统API - 修改后日期: {}", modifiedDate); + log.info("传统API - 原始日期是否被修改: {}", originalDate.equals(modifiedDate)); + + log.info("Java 8 - 原始日期时间: {}", originalDateTime); + log.info("Java 8 - 修改后日期时间: {}", modifiedDateTime); + log.info("Java 8 - 原始日期时间是否被修改: {}", originalDateTime.equals(modifiedDateTime)); + log.info("优势: Java 8 API不可变,避免副作用,更适合函数式编程"); + } + + /** + * 优势3: 线程安全 + */ + @Test + public void testThreadSafety() { + log.info("=== 优势3: 线程安全 ==="); + + // 传统API - SimpleDateFormat不是线程安全的 + // 在多线程环境中需要为每个线程创建独立的实例或使用ThreadLocal + + // Java 8 API - DateTimeFormatter是线程安全的 + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + LocalDateTime now = LocalDateTime.now(); + + // 可以在多个线程中安全使用同一个formatter + String formatted = now.format(formatter); + + log.info("Java 8 - 格式化结果: {}", formatted); + log.info("优势: DateTimeFormatter线程安全,可以在多线程环境中共享使用"); + } + + /** + * 优势4: 更好的时区支持 + */ + @Test + public void testBetterTimeZoneSupport() { + log.info("=== 优势4: 更好的时区支持 ==="); + + // Java 8 API - 丰富的时区支持 + ZonedDateTime shanghaiTime = ZonedDateTime.now(ZoneId.of("Asia/Shanghai")); + ZonedDateTime newYorkTime = ZonedDateTime.now(ZoneId.of("America/New_York")); + ZonedDateTime utcTime = ZonedDateTime.now(ZoneId.of("UTC")); + + log.info("上海时间: {}", shanghaiTime); + log.info("纽约时间: {}", newYorkTime); + log.info("UTC时间: {}", utcTime); + + // 时区转换 + ZonedDateTime converted = shanghaiTime.withZoneSameInstant(ZoneId.of("UTC")); + log.info("上海时间转换为UTC: {}", converted); + + log.info("优势: Java 8 API提供更丰富的时区支持和转换功能"); + } + + /** + * 优势5: 更精确的时间表示 + */ + @Test + public void testPrecision() { + log.info("=== 优势5: 更精确的时间表示 ==="); + + // Java 8 API - 支持纳秒精度 + LocalDateTime preciseTime = LocalDateTime.now(); + Instant instant = Instant.now(); + + log.info("LocalDateTime: {}", preciseTime); + log.info("Instant: {}", instant); + log.info("纳秒: {}", preciseTime.getNano()); + + // 传统API - 只能精确到毫秒 + Date date = new Date(); + log.info("传统Date: {}", date); + + log.info("优势: Java 8 API支持纳秒精度,提供更精确的时间表示"); + } + + /** + * 优势6: 更丰富的日期时间操作 + */ + @Test + public void testRichOperations() { + log.info("=== 优势6: 更丰富的日期时间操作 ==="); + + LocalDateTime now = LocalDateTime.now(); + + // 获取月份第一天 + LocalDateTime firstDayOfMonth = now.with(TemporalAdjusters.firstDayOfMonth()); + + // 获取下个工作日 + LocalDateTime nextWorkingDay = now.with(TemporalAdjusters.next(DayOfWeek.MONDAY)); + + // 获取本月的最后一个工作日 + LocalDateTime lastWorkingDayOfMonth = now.with(TemporalAdjusters.lastDayOfMonth()) + .with(TemporalAdjusters.previousOrSame(DayOfWeek.FRIDAY)); + + // 计算两个日期之间的差值 + LocalDate startDate = LocalDate.of(2023, 1, 1); + LocalDate endDate = LocalDate.of(2023, 12, 31); + long daysBetween = ChronoUnit.DAYS.between(startDate, endDate); + long monthsBetween = ChronoUnit.MONTHS.between(startDate, endDate); + + log.info("月份第一天: {}", firstDayOfMonth); + log.info("下个工作日: {}", nextWorkingDay); + log.info("本月最后一个工作日: {}", lastWorkingDayOfMonth); + log.info("2023年总天数: {}", daysBetween); + log.info("2023年总月数: {}", monthsBetween); + + log.info("优势: Java 8 API提供丰富的日期时间操作和计算功能"); + } + + /** + * 优势7: 更好的格式化支持 + */ + @Test + public void testBetterFormatting() { + log.info("=== 优势7: 更好的格式化支持 ==="); + + LocalDateTime now = LocalDateTime.now(); + + // 预定义格式 + String isoFormat = now.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME); + + // 自定义格式 + String customFormat = now.format(DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH时mm分ss秒")); + + // 本地化格式 + String localizedFormat = now.format(DateTimeFormatter.ofLocalizedDateTime(java.time.format.FormatStyle.MEDIUM)); + + log.info("ISO格式: {}", isoFormat); + log.info("自定义格式: {}", customFormat); + log.info("本地化格式: {}", localizedFormat); + + log.info("优势: Java 8 API提供更丰富的格式化选项和本地化支持"); + } + + /** + * 优势8: 更好的错误处理 + */ + @Test + public void testBetterErrorHandling() { + log.info("=== 优势8: 更好的错误处理 ==="); + + // Java 8 API - 明确的异常处理 + try { + LocalDate invalidDate = LocalDate.of(2023, 2, 30); // 2月没有30日 + } catch (DateTimeException e) { + log.info("Java 8 API捕获到无效日期异常: {}", e.getMessage()); + } + + // 传统API - 可能静默失败或产生意外结果 + Calendar calendar = Calendar.getInstance(); + calendar.setLenient(false); + try { + calendar.set(2023, Calendar.FEBRUARY, 30); + calendar.getTime(); + } catch (Exception e) { + log.info("传统API捕获到异常: {}", e.getMessage()); + } + + log.info("优势: Java 8 API提供更明确的错误处理和异常信息"); + } + + /** + * 优势9: 更好的性能 + */ + @Test + public void testBetterPerformance() { + log.info("=== 优势9: 更好的性能 ==="); + + int iterations = 100000; + + // 传统API性能测试 + long startTime = System.currentTimeMillis(); + for (int i = 0; i < iterations; i++) { + Calendar calendar = Calendar.getInstance(); + calendar.add(Calendar.DAY_OF_MONTH, i); + Date date = calendar.getTime(); + } + long traditionalTime = System.currentTimeMillis() - startTime; + + // Java 8 API性能测试 + startTime = System.currentTimeMillis(); + for (int i = 0; i < iterations; i++) { + LocalDateTime dateTime = LocalDateTime.now().plusDays(i); + } + long java8Time = System.currentTimeMillis() - startTime; + + log.info("传统API - {}次操作耗时: {}ms", iterations, traditionalTime); + log.info("Java 8 API - {}次操作耗时: {}ms", iterations, java8Time); + log.info("性能提升: {}%", ((double)(traditionalTime - java8Time) / traditionalTime) * 100); + log.info("优势: Java 8 API通常具有更好的性能"); + } + + /** + * 优势10: 更好的可读性和维护性 + */ + @Test + public void testBetterReadability() { + log.info("=== 优势10: 更好的可读性和维护性 ==="); + + // 传统API - 代码冗长,可读性差 + Calendar calendar = Calendar.getInstance(); + calendar.add(Calendar.DAY_OF_MONTH, 1); + calendar.add(Calendar.HOUR_OF_DAY, 2); + calendar.add(Calendar.MINUTE, 30); + Date result1 = calendar.getTime(); + + // Java 8 API - 代码简洁,可读性好 + LocalDateTime result2 = LocalDateTime.now() + .plusDays(1) + .plusHours(2) + .plusMinutes(30); + + log.info("传统API结果: {}", result1); + log.info("Java 8 API结果: {}", result2); + log.info("优势: Java 8 API代码更简洁、更易读、更易维护"); + } + + /** + * 总结Java 8日期API的主要优势 + */ + @Test + public void testSummary() { + log.info("=== Java 8日期API主要优势总结 ==="); + log.info("1. 更清晰的API设计 - 月份从1开始,更符合人类习惯"); + log.info("2. 不可变性 - 避免副作用,适合函数式编程"); + log.info("3. 线程安全 - DateTimeFormatter等类线程安全"); + log.info("4. 更好的时区支持 - 丰富的时区操作和转换"); + log.info("5. 更精确的时间表示 - 支持纳秒精度"); + log.info("6. 更丰富的日期时间操作 - TemporalAdjusters、ChronoUnit等"); + log.info("7. 更好的格式化支持 - 预定义格式、本地化等"); + log.info("8. 更好的错误处理 - 明确的异常信息"); + log.info("9. 更好的性能 - 通常比传统API更快"); + log.info("10. 更好的可读性和维护性 - 代码更简洁清晰"); + log.info("建议: 在新项目中使用Java 8日期API,逐步迁移现有代码"); + } +} + From e9df9921c3f66b0a366bf2796e61322f711b222b Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Mon, 15 Dec 2025 11:47:46 +0800 Subject: [PATCH 471/476] doc --- .gitignore | 3 +++ concurrency/src/test/java/thread/pool/PoolErrorTest.java | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 4448765e..17cb4568 100644 --- a/.gitignore +++ b/.gitignore @@ -31,3 +31,6 @@ build/ gradle/ gradlew* + +.cursorindexingignore +.specstory/ \ No newline at end of file diff --git a/concurrency/src/test/java/thread/pool/PoolErrorTest.java b/concurrency/src/test/java/thread/pool/PoolErrorTest.java index f957f13b..0030e860 100644 --- a/concurrency/src/test/java/thread/pool/PoolErrorTest.java +++ b/concurrency/src/test/java/thread/pool/PoolErrorTest.java @@ -14,7 +14,8 @@ public class PoolErrorTest { @Test public void testNoMethod() throws Exception { ExecutorsThreadPool.pool.execute(() -> { - //TODO 3.1.1 3.6.1 版本差异 低版本没有该方法,报NoSuchMethodError, 但是日志中没有错误栈,导致问题难定位 + // 3.1.1 3.6.1 版本差异 低版本没有该方法,报NoSuchMethodError, 但是日志中没有错误栈,导致问题难定位 + // 所以需要提交到线程池的任务都需要catch Throwable级别 防止异常被吞 try { Pair a = Pair.create("", ""); log.info("finish"); From f9899aa69145e345d73deaf1dca3658893969718 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Thu, 18 Dec 2025 21:21:30 +0800 Subject: [PATCH 472/476] split --- .../com/github/kuangcp/util/ShowBinary.java | 4 - class/src/main/java/security/aes/AESUtil.java | 72 +----------- .../main/java/security/aes/AesSimpleUtil.java | 100 +++++++++++++++++ .../test/java/security/aes/AESUtilTest.java | 77 ------------- .../java/security/aes/AesSimpleUtilTest.java | 104 ++++++++++++++++++ 5 files changed, 205 insertions(+), 152 deletions(-) create mode 100644 class/src/main/java/security/aes/AesSimpleUtil.java create mode 100644 class/src/test/java/security/aes/AesSimpleUtilTest.java diff --git a/class/src/main/java/com/github/kuangcp/util/ShowBinary.java b/class/src/main/java/com/github/kuangcp/util/ShowBinary.java index f4a1d170..f98f7a0d 100644 --- a/class/src/main/java/com/github/kuangcp/util/ShowBinary.java +++ b/class/src/main/java/com/github/kuangcp/util/ShowBinary.java @@ -46,10 +46,6 @@ public static byte[] hexToByte(String hex) { int val = Integer.parseInt(hex.substring(index, index + 2), 16); ans[i] = (byte) val; } -// for (byte b : ans) { -// System.out.print(b + " "); -// } -// System.out.println(hex.length() + " " + ans.length); return ans; } } diff --git a/class/src/main/java/security/aes/AESUtil.java b/class/src/main/java/security/aes/AESUtil.java index 17194d53..434b7965 100644 --- a/class/src/main/java/security/aes/AESUtil.java +++ b/class/src/main/java/security/aes/AESUtil.java @@ -1,7 +1,5 @@ package security.aes; -import com.github.kuangcp.util.ShowBinary; - import javax.crypto.*; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.PBEKeySpec; @@ -11,7 +9,6 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.Serializable; -import java.nio.charset.StandardCharsets; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; @@ -23,41 +20,7 @@ public class AESUtil { - public static byte[] encrypt(String hexKey, byte[] data) throws Exception { - return encrypt(ShowBinary.hexToByte(hexKey), data); - } - - public static byte[] decrypt(String hexKey, byte[] data) throws Exception { - return decrypt(ShowBinary.hexToByte(hexKey), data); - } - - /** - * 加密 - * - * @param key 密钥 - * @param data 加密数据 - * @return 密文 - */ - public static byte[] encrypt(byte[] key, byte[] data) throws Exception { - SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES"); - Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); - cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec); - return cipher.doFinal(data); - } - - /** - * 解密 - * - * @param key 密钥 - * @param data 密文 - * @return 解密后的数据 - */ - public static byte[] decrypt(byte[] key, byte[] data) throws Exception { - SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES"); - Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); - cipher.init(Cipher.DECRYPT_MODE, secretKeySpec); - return cipher.doFinal(data); - } + // 繁琐用法 public static String encrypt(String algorithm, String input, SecretKey key, IvParameterSpec iv) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, @@ -204,37 +167,4 @@ public static String decryptPass(String text, String passwd, String salt, String } - // 密钥长度(AES支持128、192、256位) - private static final int KEY_SIZE = 256; - - // 固定IV 0值 - private static final byte[] FIXED_IV = new byte[16]; // AES块大小为16字节 - - // 将字符串密钥转换为SecretKeySpec - - /** - * @param key 字符串长度对应 KEY_SIZE 长度/8 - */ - public static SecretKeySpec generateKey(String key) throws Exception { - byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8); - byte[] keyArray = new byte[KEY_SIZE / 8]; - System.arraycopy(keyBytes, 0, keyArray, 0, Math.min(keyBytes.length, keyArray.length)); - return new SecretKeySpec(keyArray, "AES"); - } - - // 加密方法 - public static String encrypt(String plainText, SecretKeySpec key) throws Exception { - Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); - cipher.init(Cipher.ENCRYPT_MODE, key, new javax.crypto.spec.IvParameterSpec(FIXED_IV)); - byte[] encryptedBytes = cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8)); - return Base64.getEncoder().encodeToString(encryptedBytes); - } - - // 解密方法 - public static String decrypt(String encryptedText, SecretKeySpec key) throws Exception { - Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); - cipher.init(Cipher.DECRYPT_MODE, key, new javax.crypto.spec.IvParameterSpec(FIXED_IV)); - byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(encryptedText)); - return new String(decryptedBytes, StandardCharsets.UTF_8); - } } diff --git a/class/src/main/java/security/aes/AesSimpleUtil.java b/class/src/main/java/security/aes/AesSimpleUtil.java new file mode 100644 index 00000000..150bc873 --- /dev/null +++ b/class/src/main/java/security/aes/AesSimpleUtil.java @@ -0,0 +1,100 @@ +package security.aes; + +import com.github.kuangcp.util.ShowBinary; + +import javax.crypto.Cipher; +import javax.crypto.spec.SecretKeySpec; +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.util.Base64; + +/** + * + * @author Kuangcp + * 2025-12-18 21:01 + */ +public class AesSimpleUtil { + + public static byte[] encrypt(String hexKey, byte[] data) throws Exception { + return encrypt(ShowBinary.hexToByte(hexKey), data); + } + + public static byte[] decrypt(String hexKey, byte[] data) throws Exception { + return decrypt(ShowBinary.hexToByte(hexKey), data); + } + + /** + * 加密 + * + * @param key 密钥 + * @param data 加密数据 + * @return 密文 + */ + public static byte[] encrypt(byte[] key, byte[] data) throws Exception { + SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES"); + Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); + cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec); + return cipher.doFinal(data); + } + + /** + * 解密 + * + * @param key 密钥 + * @param data 密文 + * @return 解密后的数据 + */ + public static byte[] decrypt(byte[] key, byte[] data) throws Exception { + SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES"); + Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); + cipher.init(Cipher.DECRYPT_MODE, secretKeySpec); + return cipher.doFinal(data); + } + + + // 密钥长度(AES支持128、192、256位) + private static final int KEY_SIZE = 256; + + // 固定IV 0值 + private static final byte[] FIXED_IV = new byte[16]; // AES块大小为16字节 + + /** + * @param key 字符串长度 允许任意,但是这里选择了256,最好要32位 + */ + public static SecretKeySpec generateKey(String key) { + byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8); + byte[] keyArray = new byte[KEY_SIZE / 8]; + System.arraycopy(keyBytes, 0, keyArray, 0, Math.min(keyBytes.length, keyArray.length)); + return new SecretKeySpec(keyArray, "AES"); + } + + /** + * 使用MD5就是使用了 128位长度的密钥 + */ + public static SecretKeySpec generateKeyViaMd5(String key) { + try { + MessageDigest md5 = MessageDigest.getInstance("MD5"); + byte[] keyBytes = md5.digest(key.getBytes(StandardCharsets.UTF_8)); + return new SecretKeySpec(keyBytes, "AES"); + } catch (Exception e) { + throw new RuntimeException("derive AES key error", e); + } + } + + // 加密方法 + public static String encrypt(String plainText, SecretKeySpec key) throws Exception { + Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); + cipher.init(Cipher.ENCRYPT_MODE, key, new javax.crypto.spec.IvParameterSpec(FIXED_IV)); + byte[] encryptedBytes = cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8)); + return Base64.getEncoder().encodeToString(encryptedBytes); + } + + // 解密方法 + public static String decrypt(String encryptedText, SecretKeySpec key) throws Exception { + Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); + cipher.init(Cipher.DECRYPT_MODE, key, new javax.crypto.spec.IvParameterSpec(FIXED_IV)); + byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(encryptedText)); + return new String(decryptedBytes, StandardCharsets.UTF_8); + } + +} diff --git a/class/src/test/java/security/aes/AESUtilTest.java b/class/src/test/java/security/aes/AESUtilTest.java index 2d5df4f4..c5c5d6a7 100644 --- a/class/src/test/java/security/aes/AESUtilTest.java +++ b/class/src/test/java/security/aes/AESUtilTest.java @@ -1,13 +1,10 @@ package security.aes; -import com.github.kuangcp.util.ShowBinary; import lombok.extern.slf4j.Slf4j; import org.junit.Assert; import org.junit.Test; -import org.junit.jupiter.api.RepeatedTest; -import javax.crypto.KeyGenerator; import javax.crypto.SealedObject; import javax.crypto.SecretKey; import javax.crypto.spec.IvParameterSpec; @@ -17,67 +14,12 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.security.SecureRandom; -import java.util.Base64; import static org.hamcrest.MatcherAssert.assertThat; @Slf4j public class AESUtilTest { - - @Test - public void testFlow() throws Exception { - // 生成随机密钥 - KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); - // 128 192 256 - keyGenerator.init(128, new SecureRandom()); - SecretKey secretKey = keyGenerator.generateKey(); - - byte[] key = secretKey.getEncoded(); - System.out.println("AES 密钥:" + Base64.getEncoder().encodeToString(key)); - System.out.println("AES 密钥:" + ShowBinary.byteToHex(key)); - - String content = "KeyGenerator keyGenerator = KeyGenerator.getInstance(\"AES\");"; - System.out.println("原文:" + content); - - byte[] ret = AESUtil.encrypt(key, content.getBytes()); - System.out.println("密文:" + Base64.getEncoder().encodeToString(ret)); - System.out.println("密文:" + ShowBinary.byteToHex(ret)); - - byte[] raw = AESUtil.decrypt(key, ret); - System.out.println("原文:" + new String(raw)); - Assert.assertEquals(content, new String(raw)); - } - - @Test - @RepeatedTest(6) - public void testHexKeyFlow() throws Exception { - // 生成随机密钥 - KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); - // 128 192 256 - keyGenerator.init(128, new SecureRandom()); - SecretKey secretKey = keyGenerator.generateKey(); - byte[] key = secretKey.getEncoded(); - -// System.out.println("Origin: " + key.length); - String hexKey = ShowBinary.byteToHex(key); - - System.out.println("AES 密钥:" + hexKey); - - String content = "KeyGenerator keyGenerator = KeyGenerator.getInstance(\"AES\");"; - System.out.println("原文:" + content); - - System.out.println("------"); - byte[] ret = AESUtil.encrypt(hexKey, content.getBytes()); -// System.out.println("密文:" + Base64.getEncoder().encodeToString(ret)); - System.out.println("密文:" + ShowBinary.byteToHex(ret)); - - byte[] raw = AESUtil.decrypt(hexKey, ret); - System.out.println("原文:" + new String(raw)); - Assert.assertEquals(content, new String(raw)); - } - - @Test public void testStringEncrypt() throws Exception { // given @@ -188,24 +130,5 @@ public void testConfigWay() throws Exception { Assert.assertEquals(origin, plainText); } - /** - * 只保留一个密钥,iv使用0值,安全性降低,但是更方便 - */ - @Test - public void testSimple() throws Exception { - try { - String key = "b423eb489f47dbe933f7e761946ec216"; - SecretKeySpec secretKey = AESUtil.generateKey(key); - - String plainText = "Hello, AES Encryption!"; - String encryptedText = AESUtil.encrypt(plainText, secretKey); - System.out.println("加密后的密文 (Base64): " + encryptedText); - - String decryptedText = AESUtil.decrypt(encryptedText, secretKey); - System.out.println("解密后的明文: " + decryptedText); - } catch (Exception e) { - log.error("", e); - } - } } diff --git a/class/src/test/java/security/aes/AesSimpleUtilTest.java b/class/src/test/java/security/aes/AesSimpleUtilTest.java new file mode 100644 index 00000000..20519b0d --- /dev/null +++ b/class/src/test/java/security/aes/AesSimpleUtilTest.java @@ -0,0 +1,104 @@ +package security.aes; + +import com.github.kuangcp.util.ShowBinary; +import lombok.extern.slf4j.Slf4j; +import org.junit.Assert; +import org.junit.Test; +import org.junit.jupiter.api.RepeatedTest; + +import javax.crypto.KeyGenerator; +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; +import java.security.SecureRandom; +import java.util.Base64; + +/** + * + * @author Kuangcp + * 2025-12-18 21:01 + */ +@Slf4j +public class AesSimpleUtilTest { + + @Test + public void testFlow() throws Exception { + // 生成随机密钥 + KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); + // 128 192 256 + keyGenerator.init(128, new SecureRandom()); + SecretKey secretKey = keyGenerator.generateKey(); + + byte[] key = secretKey.getEncoded(); + System.out.println("AES 密钥 Base64:" + Base64.getEncoder().encodeToString(key)); + System.out.println("AES 密钥 Hex:" + ShowBinary.byteToHex(key)); + + String content = "KeyGenerator keyGenerator = KeyGenerator.getInstance(\"AES\");"; + System.out.println("原文:" + content); + + byte[] ret = AesSimpleUtil.encrypt(key, content.getBytes()); + System.out.println("密文:" + Base64.getEncoder().encodeToString(ret)); + System.out.println("密文:" + ShowBinary.byteToHex(ret)); + + byte[] raw = AesSimpleUtil.decrypt(key, ret); + System.out.println("原文:" + new String(raw)); + Assert.assertEquals(content, new String(raw)); + } + + @Test + @RepeatedTest(6) + public void testHexKeyFlow() throws Exception { + // 生成随机密钥 + KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); + // 128 192 256 + keyGenerator.init(128, new SecureRandom()); + SecretKey secretKey = keyGenerator.generateKey(); + byte[] key = secretKey.getEncoded(); + +// System.out.println("Origin: " + key.length); + String hexKey = ShowBinary.byteToHex(key); + + System.out.println("AES 密钥:" + hexKey); + + String content = "KeyGenerator keyGenerator = KeyGenerator.getInstance(\"AES\");"; + System.out.println("原文:" + content); + + System.out.println("------"); + byte[] ret = AesSimpleUtil.encrypt(hexKey, content.getBytes()); +// System.out.println("密文:" + Base64.getEncoder().encodeToString(ret)); + System.out.println("密文:" + ShowBinary.byteToHex(ret)); + + byte[] raw = AesSimpleUtil.decrypt(hexKey, ret); + System.out.println("原文:" + new String(raw)); + Assert.assertEquals(content, new String(raw)); + } + + + /** + * 只保留一个密钥,iv使用0值,安全性降低,但是更方便 + */ + @Test + public void testSimple() throws Exception { + String key = "这个不是密钥"; + SecretKeySpec secretKey = AesSimpleUtil.generateKey(key); + + String plainText = "Hello, AES Encryption!"; + String encryptedText = AesSimpleUtil.encrypt(plainText, secretKey); + System.out.println("加密后的密文 (Base64): " + encryptedText); + + String decryptedText = AesSimpleUtil.decrypt(encryptedText, secretKey); + System.out.println("解密后的明文: " + decryptedText); + } + + @Test + public void testViaMd5() throws Exception { + String key = "这个不是密钥"; + SecretKeySpec secretKey = AesSimpleUtil.generateKeyViaMd5(key); + + String plainText = "Hello, AES Encryption!"; + String encryptedText = AesSimpleUtil.encrypt(plainText, secretKey); + System.out.println("加密后的密文 (Base64): " + encryptedText); + + String decryptedText = AesSimpleUtil.decrypt(encryptedText, secretKey); + System.out.println("解密后的明文: " + decryptedText); + } +} From 4267c83ebbfb6c1bc1c46811031da34564557948 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Wed, 7 Jan 2026 14:54:23 +0800 Subject: [PATCH 473/476] queue --- .../queue/ConcurrentLinkedQueueV1Test.java | 127 ++++++++++++++++++ 1 file changed, 127 insertions(+) create mode 100644 concurrency/src/test/java/com/github/kuangcp/queue/ConcurrentLinkedQueueV1Test.java diff --git a/concurrency/src/test/java/com/github/kuangcp/queue/ConcurrentLinkedQueueV1Test.java b/concurrency/src/test/java/com/github/kuangcp/queue/ConcurrentLinkedQueueV1Test.java new file mode 100644 index 00000000..d51b3640 --- /dev/null +++ b/concurrency/src/test/java/com/github/kuangcp/queue/ConcurrentLinkedQueueV1Test.java @@ -0,0 +1,127 @@ +package com.github.kuangcp.queue; + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.concurrent.BasicThreadFactory; +import org.apache.commons.math3.util.Pair; +import org.junit.Test; + +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicLong; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * + * @author Kuangcp + * 2026-01-07 14:17 + */ +@Slf4j +public class ConcurrentLinkedQueueV1Test { + + private final ConcurrentLinkedQueue msgQueue = new ConcurrentLinkedQueue<>(); + private final AtomicBoolean delay = new AtomicBoolean(); + private final AtomicLong lastSend = new AtomicLong(); + private final ThreadLocal cnt = new ThreadLocal<>(); + private final ThreadLocal id = new ThreadLocal<>(); + + // 窗口的最大容量 + private final static int boomThreshold = 200; + // 时间窗口流攒批 + private final Duration window = Duration.ofSeconds(7); + + static final ScheduledExecutorService scheduler = new ScheduledThreadPoolExecutor(1, + new BasicThreadFactory.Builder().namingPattern("batch") + .daemon(true).build()); + + private void verify(String block) { + String[] all = block.split(","); + Map>> threadMap = Stream.of(all).map(v -> { + String[] sl = v.split("_"); + return Pair.create(Long.parseLong(sl[0]), sl[1]); + }).collect(Collectors.groupingBy(Pair::getKey)); + List sortKey = threadMap.keySet().stream().sorted().collect(Collectors.toList()); + StringBuilder buf = new StringBuilder(); + for (Long key : sortKey) { + List> now = threadMap.get(key); + List vals = now.stream().map(Pair::getValue).map(Long::parseLong).sorted().collect(Collectors.toList()); + Long min = vals.get(0); + Long max = vals.get(vals.size() - 1); + buf.append(String.format("%s [%3d,%3d] ", key, min, max)); + } + log.info("{}", buf); + } + + private void handle(String msg) { + long now = System.currentTimeMillis(); + long last = lastSend.get(); + Duration delta = Duration.ofMillis(now - last); + + if (msgQueue.size() > boomThreshold) { +// log.warn("BOOM"); + return; + } + msgQueue.offer(msg); + if (delay.get()) { + return; + } + + delay.set(true); + scheduler.schedule(() -> { + // 批量取出队列中的所有消息 + List messages = new ArrayList<>(); + String tmp; + while ((tmp = msgQueue.poll()) != null) { + messages.add(tmp); + } + if (!messages.isEmpty()) { + String msgBlock = String.join(",", messages); + lastSend.set(System.currentTimeMillis()); +// log.info("msgBlock={}", msgBlock); + verify(msgBlock); + } + + + delay.set(false); + }, window.minus(delta).toMillis(), TimeUnit.MILLISECONDS); + } + + private void submitTask(int i) { + cnt.set(0L); + id.set((long) i); + try { + while (true) { + TimeUnit.MILLISECONDS.sleep(100 + ThreadLocalRandom.current().nextInt(800)); + Long val = cnt.get(); + long start = System.currentTimeMillis(); + handle(id.get() + "_" + val); + long ms = System.currentTimeMillis() - start; + if (ms > 1) { + log.info("ms={}", ms); + } + cnt.set(val + 1); + } + } catch (Exception e) { + log.error("", e); + } + } + + /** + * 流攒批 处理 + */ + @Test + public void testWaitBatch() throws Exception { + int num = 30; + ExecutorService producer = Executors.newFixedThreadPool(num); + for (int i = 0; i < num; i++) { + int finalI = i; + producer.submit(() -> this.submitTask(finalI + 1)); + } + + Thread.currentThread().join(); + } +} From 30d0a2a706b49c1e11be3529f4761d6b0f7391d0 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Wed, 7 Jan 2026 15:13:14 +0800 Subject: [PATCH 474/476] rewrite --- .../queue/ConcurrentLinkedQueueV1Test.java | 101 ++++++++++++------ 1 file changed, 70 insertions(+), 31 deletions(-) diff --git a/concurrency/src/test/java/com/github/kuangcp/queue/ConcurrentLinkedQueueV1Test.java b/concurrency/src/test/java/com/github/kuangcp/queue/ConcurrentLinkedQueueV1Test.java index d51b3640..a348c997 100644 --- a/concurrency/src/test/java/com/github/kuangcp/queue/ConcurrentLinkedQueueV1Test.java +++ b/concurrency/src/test/java/com/github/kuangcp/queue/ConcurrentLinkedQueueV1Test.java @@ -10,7 +10,6 @@ import java.util.List; import java.util.Map; import java.util.concurrent.*; -import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -23,21 +22,53 @@ @Slf4j public class ConcurrentLinkedQueueV1Test { - private final ConcurrentLinkedQueue msgQueue = new ConcurrentLinkedQueue<>(); - private final AtomicBoolean delay = new AtomicBoolean(); - private final AtomicLong lastSend = new AtomicLong(); private final ThreadLocal cnt = new ThreadLocal<>(); private final ThreadLocal id = new ThreadLocal<>(); + private final ConcurrentLinkedQueue msgQueue = new ConcurrentLinkedQueue<>(); // 窗口的最大容量 private final static int boomThreshold = 200; + private final AtomicLong lastAlertTime = new AtomicLong(0); // 时间窗口流攒批 - private final Duration window = Duration.ofSeconds(7); + private static final Duration BATCH_INTERVAL = Duration.ofSeconds(10); + private static final Duration MIN_INTERVAL = Duration.ofSeconds(10); + + /** + * 定时批量发送任务的Future,用于取消任务 + */ + private volatile ScheduledFuture scheduledTask; static final ScheduledExecutorService scheduler = new ScheduledThreadPoolExecutor(1, new BasicThreadFactory.Builder().namingPattern("batch") .daemon(true).build()); + public void init() { + // 启动定时批量处理任务 + scheduledTask = scheduler.scheduleAtFixedRate( + this::batchSend, + BATCH_INTERVAL.toMillis(), + BATCH_INTERVAL.toMillis(), + TimeUnit.MILLISECONDS + ); + } + + public void destroy() { + if (scheduledTask != null) { + scheduledTask.cancel(false); + } + // 关闭前发送剩余消息 + batchSend(); + scheduler.shutdown(); + try { + if (!scheduler.awaitTermination(5, TimeUnit.SECONDS)) { + scheduler.shutdownNow(); + } + } catch (InterruptedException e) { + scheduler.shutdownNow(); + Thread.currentThread().interrupt(); + } + } + private void verify(String block) { String[] all = block.split(","); Map>> threadMap = Stream.of(all).map(v -> { @@ -56,38 +87,45 @@ private void verify(String block) { log.info("{}", buf); } - private void handle(String msg) { - long now = System.currentTimeMillis(); - long last = lastSend.get(); - Duration delta = Duration.ofMillis(now - last); + private void consumer(String msg) { + // 但是这里会占用生产者 线程资源 +// long now = System.currentTimeMillis(); +// long last = lastAlertTime.get(); +// long elapsed = now - last; + + // 如果距离上次发送超过最小间隔,立即发送 +// if (elapsed >= MIN_INTERVAL.toMillis()) { +// if (lastAlertTime.compareAndSet(last, now)) { +// batchSend(); +// return; +// } +// } - if (msgQueue.size() > boomThreshold) { -// log.warn("BOOM"); + // 否则加入队列等待批量处理 + // 先检查队列大小,避免内存溢出 + if (msgQueue.size() >= boomThreshold) { return; } + msgQueue.offer(msg); - if (delay.get()) { + } + + private void batchSend() { + if (msgQueue.isEmpty()) { return; } - delay.set(true); - scheduler.schedule(() -> { - // 批量取出队列中的所有消息 - List messages = new ArrayList<>(); - String tmp; - while ((tmp = msgQueue.poll()) != null) { - messages.add(tmp); - } - if (!messages.isEmpty()) { - String msgBlock = String.join(",", messages); - lastSend.set(System.currentTimeMillis()); -// log.info("msgBlock={}", msgBlock); - verify(msgBlock); - } - - - delay.set(false); - }, window.minus(delta).toMillis(), TimeUnit.MILLISECONDS); + // 批量取出队列中的所有消息 + List messages = new ArrayList<>(); + String tmp; + while ((tmp = msgQueue.poll()) != null) { + messages.add(tmp); + } + if (!messages.isEmpty()) { + String msgBlock = String.join(",", messages); + lastAlertTime.set(System.currentTimeMillis()); + verify(msgBlock); + } } private void submitTask(int i) { @@ -98,7 +136,7 @@ private void submitTask(int i) { TimeUnit.MILLISECONDS.sleep(100 + ThreadLocalRandom.current().nextInt(800)); Long val = cnt.get(); long start = System.currentTimeMillis(); - handle(id.get() + "_" + val); + consumer(id.get() + "_" + val); long ms = System.currentTimeMillis() - start; if (ms > 1) { log.info("ms={}", ms); @@ -115,6 +153,7 @@ private void submitTask(int i) { */ @Test public void testWaitBatch() throws Exception { + init(); int num = 30; ExecutorService producer = Executors.newFixedThreadPool(num); for (int i = 0; i < num; i++) { From b8a8b6751c9ddca6e77039df65391f1caa9d465a Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Fri, 9 Jan 2026 16:55:21 +0800 Subject: [PATCH 475/476] order --- .../src/main/java/thread/order/README.md | 4 ++ .../src/main/java/thread/order/Task.java | 2 + .../java/thread/order/TaskWithVolatile.java | 2 + .../java/thread/order/OrderedThreadTest.java | 55 +++++++++++++++++-- 4 files changed, 57 insertions(+), 6 deletions(-) diff --git a/concurrency/src/main/java/thread/order/README.md b/concurrency/src/main/java/thread/order/README.md index f3dc9432..906dd48a 100644 --- a/concurrency/src/main/java/thread/order/README.md +++ b/concurrency/src/main/java/thread/order/README.md @@ -5,3 +5,7 @@ ## 第二种 使用volatile关键字, 达到需求 + +## 第三种 +LockSupport 阻塞和唤醒 + diff --git a/concurrency/src/main/java/thread/order/Task.java b/concurrency/src/main/java/thread/order/Task.java index cada382e..65fb94b0 100644 --- a/concurrency/src/main/java/thread/order/Task.java +++ b/concurrency/src/main/java/thread/order/Task.java @@ -12,6 +12,7 @@ * @author kuangcp */ @Slf4j +@Deprecated public class Task implements Runnable { private String target; @@ -36,6 +37,7 @@ public void run() { log.info("target={} order={} count={}", target, order, count); try { + // 本质是利用耗时一致 控制排序,不是实际意义的先后依赖 TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { log.error("", e); diff --git a/concurrency/src/main/java/thread/order/TaskWithVolatile.java b/concurrency/src/main/java/thread/order/TaskWithVolatile.java index a54a5778..ef2c6cde 100644 --- a/concurrency/src/main/java/thread/order/TaskWithVolatile.java +++ b/concurrency/src/main/java/thread/order/TaskWithVolatile.java @@ -17,6 +17,7 @@ * @author kuangcp */ @Slf4j +@Deprecated public class TaskWithVolatile implements Runnable { private String target; @@ -39,6 +40,7 @@ public void run() { count += 1; try { + // 本质是利用耗时一致 控制排序,不是实际意义的先后依赖 Thread.sleep(1000); } catch (InterruptedException e) { log.error("", e); diff --git a/concurrency/src/test/java/thread/order/OrderedThreadTest.java b/concurrency/src/test/java/thread/order/OrderedThreadTest.java index ec319f36..e808758a 100644 --- a/concurrency/src/test/java/thread/order/OrderedThreadTest.java +++ b/concurrency/src/test/java/thread/order/OrderedThreadTest.java @@ -1,8 +1,10 @@ package thread.order; +import lombok.extern.slf4j.Slf4j; import org.junit.Test; import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.locks.LockSupport; /** * Created by https://github.com/kuangcp on 18-1-18 下午1:54 @@ -13,6 +15,7 @@ * * @author kuangcp */ +@Slf4j public class OrderedThreadTest { private static final AtomicInteger count = new AtomicInteger(0); @@ -28,9 +31,7 @@ public void testOrder() throws InterruptedException { thread3.start(); // 避免Junit主线程直接退出 - thread1.join(); - thread2.join(); - thread3.join(); + Thread.currentThread().join(); } @Test @@ -44,8 +45,50 @@ public void testOrderWithVolatile() throws InterruptedException { thread3.start(); // 避免Junit主线程直接退出 - thread1.join(); - thread2.join(); - thread3.join(); + Thread.currentThread().join(); + } + + private static Thread t1, t2, t3; + + @Test + public void testOrderWithLock() throws Exception { + int num = 10; + t1 = new Thread(() -> { + for (int i = 0; i < num; i++) { + LockSupport.park(); + System.out.print("A"); + LockSupport.unpark(t2); + } + }); + + t2 = new Thread(() -> { + for (int i = 0; i < num; i++) { + LockSupport.park(); + System.out.print("B"); + LockSupport.unpark(t3); + } + }); + + t3 = new Thread(() -> { + for (int i = 0; i < num; i++) { + LockSupport.park(); + System.out.print("C "); + LockSupport.unpark(t1); + } + }); + + t1.start(); + t2.start(); + t3.start(); + + // 主线程稍微等待一下,确保其他线程已经启动并且进入park状态。 + try { + Thread.sleep(100); + } catch (InterruptedException e) { + log.error("", e); + } + + // 启动整个流程 + LockSupport.unpark(t1); } } \ No newline at end of file From 078d7e1e62e0911ed6b7d9c19a976cd41d26fc75 Mon Sep 17 00:00:00 2001 From: Kuangcp Date: Fri, 16 Jan 2026 15:57:06 +0800 Subject: [PATCH 476/476] stream collector --- .../kuangcp/stream/collector/DataPoint.java | 22 +++++++ .../kuangcp/stream/collector/Statistics.java | 37 ++++++++++++ .../collector/StatisticsAccumulator.java | 38 +++++++++++++ .../kuangcp/stream/CollectorToMapTest.java | 56 ------------------ .../stream/collector/CollectorTest.java | 42 ++++++++++++++ .../stream/collector/CollectorToMapTest.java | 57 +++++++++++++++++++ 6 files changed, 196 insertions(+), 56 deletions(-) create mode 100644 java8/src/main/java/com/github/kuangcp/stream/collector/DataPoint.java create mode 100644 java8/src/main/java/com/github/kuangcp/stream/collector/Statistics.java create mode 100644 java8/src/main/java/com/github/kuangcp/stream/collector/StatisticsAccumulator.java delete mode 100644 java8/src/test/java/com/github/kuangcp/stream/CollectorToMapTest.java create mode 100644 java8/src/test/java/com/github/kuangcp/stream/collector/CollectorToMapTest.java diff --git a/java8/src/main/java/com/github/kuangcp/stream/collector/DataPoint.java b/java8/src/main/java/com/github/kuangcp/stream/collector/DataPoint.java new file mode 100644 index 00000000..1c6436b3 --- /dev/null +++ b/java8/src/main/java/com/github/kuangcp/stream/collector/DataPoint.java @@ -0,0 +1,22 @@ + +package com.github.kuangcp.stream.collector; + +import java.time.LocalDateTime; + +public class DataPoint { + private LocalDateTime timestamp; + private Integer value; + + public DataPoint(LocalDateTime timestamp, Integer value) { + this.timestamp = timestamp; + this.value = value; + } + + public LocalDateTime getTimestamp() { + return timestamp; + } + + public Integer getValue() { + return value; + } +} diff --git a/java8/src/main/java/com/github/kuangcp/stream/collector/Statistics.java b/java8/src/main/java/com/github/kuangcp/stream/collector/Statistics.java new file mode 100644 index 00000000..7dd4b8fd --- /dev/null +++ b/java8/src/main/java/com/github/kuangcp/stream/collector/Statistics.java @@ -0,0 +1,37 @@ +package com.github.kuangcp.stream.collector; + +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.Optional; +import java.util.stream.Stream; + +@Data +public class Statistics { + private Optional maxTime; + private Double avgValue; + private Integer sumValue; + private Long count; + private Optional maxValue; + private Optional minValue; + + // 构造函数、getter、setter... + + public static Statistics combine(Statistics s1, Statistics s2) { + Statistics result = new Statistics(); + result.maxTime = s1.maxTime + .map(t1 -> s2.maxTime.filter(t2 -> !t1.isAfter(t2)).orElse(t1)); + result.sumValue = s1.sumValue + s2.sumValue; + result.count = s1.count + s2.count; + result.avgValue = result.count > 0 ? (double) result.sumValue / result.count : 0.0; + result.maxValue = Stream.of(s1.maxValue, s2.maxValue) + .filter(Optional::isPresent) + .map(Optional::get) + .max(Integer::compare); + result.minValue = Stream.of(s1.minValue, s2.minValue) + .filter(Optional::isPresent) + .map(Optional::get) + .min(Integer::compare); + return result; + } +} \ No newline at end of file diff --git a/java8/src/main/java/com/github/kuangcp/stream/collector/StatisticsAccumulator.java b/java8/src/main/java/com/github/kuangcp/stream/collector/StatisticsAccumulator.java new file mode 100644 index 00000000..730266ba --- /dev/null +++ b/java8/src/main/java/com/github/kuangcp/stream/collector/StatisticsAccumulator.java @@ -0,0 +1,38 @@ +package com.github.kuangcp.stream.collector; + +import java.time.LocalDateTime; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Stream; + +class StatisticsAccumulator { + LocalDateTime maxTime; + int sumValue; + long count; + int maxValue = Integer.MIN_VALUE; + int minValue = Integer.MAX_VALUE; + + StatisticsAccumulator combine(StatisticsAccumulator other) { + StatisticsAccumulator result = new StatisticsAccumulator(); + result.maxTime = Stream.of(this.maxTime, other.maxTime) + .filter(Objects::nonNull) + .max(LocalDateTime::compareTo) + .orElse(null); + result.sumValue = this.sumValue + other.sumValue; + result.count = this.count + other.count; + result.maxValue = Math.max(this.maxValue, other.maxValue); + result.minValue = Math.min(this.minValue, other.minValue); + return result; + } + + Statistics toStatistics() { + Statistics stats = new Statistics(); + stats.setMaxTime(Optional.ofNullable(maxTime)); + stats.setAvgValue(count > 0 ? (double) sumValue / count : 0.0); + stats.setSumValue(sumValue); + stats.setCount(count); + stats.setMaxValue(maxValue != Integer.MIN_VALUE ? Optional.of(maxValue) : Optional.empty()); + stats.setMinValue(minValue != Integer.MAX_VALUE ? Optional.of(minValue) : Optional.empty()); + return stats; + } + } \ No newline at end of file diff --git a/java8/src/test/java/com/github/kuangcp/stream/CollectorToMapTest.java b/java8/src/test/java/com/github/kuangcp/stream/CollectorToMapTest.java deleted file mode 100644 index 9ba146a1..00000000 --- a/java8/src/test/java/com/github/kuangcp/stream/CollectorToMapTest.java +++ /dev/null @@ -1,56 +0,0 @@ -package com.github.kuangcp.stream; - -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; -import lombok.Builder; -import lombok.Data; -import lombok.extern.slf4j.Slf4j; -import org.junit.Test; - -/** - * @author Kuangcp - */ -@Slf4j -public class CollectorToMapTest { - - /** - * 如果没有声明 重复key的策略, 就会直接抛出异常 - */ - @SuppressWarnings("ResultOfMethodCallIgnored") - @Test(expected = IllegalStateException.class) - public void testDuplicated() { - List phones = generatePhones(); - - phones.stream().collect(Collectors.toMap(Phone::getName, Phone::getPrice)); - } - - /** - * 发生重复后, 后来的值覆盖前面的值, 如果 (v1, v2) -> v1 则是忽略后来的值 - */ - @Test - public void testDuplicatedWithHandled() { - Map result = generatePhones().stream() - .collect(Collectors.toMap(Phone::getName, Phone::getPrice, (v1, v2) -> v2)); - - result.forEach((k, v) -> log.info("{} {}", k, v)); - } - - private List generatePhones() { - return Arrays.asList( - Phone.builder().name("a").price(1).build(), - Phone.builder().name("b").price(2).build(), - Phone.builder().name("a").price(3).build() - ); - } - - @Data - @Builder - static class Phone { - - String name; - int price; - } - -} diff --git a/java8/src/test/java/com/github/kuangcp/stream/collector/CollectorTest.java b/java8/src/test/java/com/github/kuangcp/stream/collector/CollectorTest.java index 4c255b8a..432d7139 100644 --- a/java8/src/test/java/com/github/kuangcp/stream/collector/CollectorTest.java +++ b/java8/src/test/java/com/github/kuangcp/stream/collector/CollectorTest.java @@ -1,9 +1,15 @@ package com.github.kuangcp.stream.collector; +import lombok.extern.slf4j.Slf4j; import org.junit.Test; +import java.time.LocalDateTime; +import java.util.ArrayList; import java.util.Comparator; +import java.util.List; +import java.util.Objects; import java.util.Optional; +import java.util.concurrent.ThreadLocalRandom; import java.util.stream.Stream; import static org.hamcrest.Matchers.equalTo; @@ -12,6 +18,7 @@ /** * @author Kuangcp on 2019-12-12 16:48 */ +@Slf4j public class CollectorTest { @Test @@ -24,4 +31,39 @@ public void testMergeSegment() { }); assertThat(result.get(), equalTo(-3)); } + + @Test + public void testCombine() throws Exception { + List dataPoints = new ArrayList<>(); + +// List dataPoints = Arrays.asList( +// new DataPoint(LocalDateTime.of(2023, 1, 1, 10, 0), 100), +// new DataPoint(LocalDateTime.of(2023, 1, 1, 11, 0), 200), +// new DataPoint(LocalDateTime.of(2023, 1, 1, 12, 0), 150) +// ); + LocalDateTime now = LocalDateTime.now(); + for (int i = 0; i < 3000; i++) { + LocalDateTime timestamp = now.plusDays(ThreadLocalRandom.current().nextInt(3000)); + DataPoint p = new DataPoint(timestamp, ThreadLocalRandom.current().nextInt(5000)); + dataPoints.add(p); + } + + Statistics stats = dataPoints.stream() + .reduce(new StatisticsAccumulator(), + (acc, point) -> { + acc.maxTime = Stream.of(acc.maxTime, point.getTimestamp()) + .filter(Objects::nonNull) + .max(LocalDateTime::compareTo) + .orElse(null); + acc.sumValue += point.getValue(); + acc.count++; + acc.maxValue = Math.max(acc.maxValue, point.getValue()); + acc.minValue = Math.min(acc.minValue, point.getValue()); + return acc; + }, + StatisticsAccumulator::combine + ) + .toStatistics(); + log.info("stats={}", stats); + } } diff --git a/java8/src/test/java/com/github/kuangcp/stream/collector/CollectorToMapTest.java b/java8/src/test/java/com/github/kuangcp/stream/collector/CollectorToMapTest.java new file mode 100644 index 00000000..741ecf2d --- /dev/null +++ b/java8/src/test/java/com/github/kuangcp/stream/collector/CollectorToMapTest.java @@ -0,0 +1,57 @@ +package com.github.kuangcp.stream.collector; + +import lombok.Builder; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * @author Kuangcp + */ +@Slf4j +public class CollectorToMapTest { + + /** + * 如果没有声明 重复key的策略, 就会直接抛出异常 + */ + @SuppressWarnings("ResultOfMethodCallIgnored") + @Test(expected = IllegalStateException.class) + public void testDuplicated() { + List phones = generatePhones(); + + phones.stream().collect(Collectors.toMap(Phone::getName, Phone::getPrice)); + } + + /** + * 发生重复后, 后来的值覆盖前面的值, 如果 (v1, v2) -> v1 则是忽略后来的值 + */ + @Test + public void testDuplicatedWithHandled() { + Map result = generatePhones().stream() + .collect(Collectors.toMap(Phone::getName, Phone::getPrice, (v1, v2) -> v2)); + + result.forEach((k, v) -> log.info("{} {}", k, v)); + } + + private List generatePhones() { + return Arrays.asList( + Phone.builder().name("a").price(1).build(), + Phone.builder().name("b").price(2).build(), + Phone.builder().name("a").price(3).build() + ); + } + + @Data + @Builder + static class Phone { + + String name; + int price; + } + +}