@ngxtm/devkit 3.4.0 → 3.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -1
- package/rules/README.md +141 -0
- package/rules/dart/best-practices/SKILL.md +23 -0
- package/rules/dart/language/SKILL.md +52 -0
- package/rules/dart/tooling/SKILL.md +43 -0
- package/rules/dotnet/aspnet-core/SKILL.md +92 -0
- package/rules/dotnet/aspnet-core/references/REFERENCE.md +335 -0
- package/rules/dotnet/best-practices/SKILL.md +101 -0
- package/rules/dotnet/best-practices/references/REFERENCE.md +256 -0
- package/rules/dotnet/blazor/SKILL.md +146 -0
- package/rules/dotnet/blazor/references/REFERENCE.md +392 -0
- package/rules/dotnet/language/SKILL.md +82 -0
- package/rules/dotnet/language/references/REFERENCE.md +222 -0
- package/rules/dotnet/patterns.rule.md +388 -0
- package/rules/dotnet/razor-pages/SKILL.md +124 -0
- package/rules/dotnet/razor-pages/references/REFERENCE.md +321 -0
- package/rules/dotnet/security/SKILL.md +89 -0
- package/rules/dotnet/security/references/REFERENCE.md +295 -0
- package/rules/dotnet/tooling/SKILL.md +92 -0
- package/rules/dotnet/tooling/references/REFERENCE.md +300 -0
- package/rules/flutter/auto-route-navigation/SKILL.md +43 -0
- package/rules/flutter/auto-route-navigation/references/REFERENCE.md +19 -0
- package/rules/flutter/auto-route-navigation/references/router-config.md +62 -0
- package/rules/flutter/bloc-state-management/SKILL.md +64 -0
- package/rules/flutter/bloc-state-management/references/REFERENCE.md +20 -0
- package/rules/flutter/bloc-state-management/references/auth-bloc-example.md +52 -0
- package/rules/flutter/bloc-state-management/references/equatable-usage.md +56 -0
- package/rules/flutter/bloc-state-management/references/property-based-state.md +68 -0
- package/rules/flutter/bloc.rule.md +76 -0
- package/rules/flutter/cicd/SKILL.md +48 -0
- package/rules/flutter/cicd/references/advanced-workflow.md +66 -0
- package/rules/flutter/cicd/references/fastlane.md +139 -0
- package/rules/flutter/cicd/references/github-actions.md +59 -0
- package/rules/flutter/dependency-injection/SKILL.md +42 -0
- package/rules/flutter/dependency-injection/references/REFERENCE.md +15 -0
- package/rules/flutter/dependency-injection/references/modules.md +37 -0
- package/rules/flutter/error-handling/SKILL.md +32 -0
- package/rules/flutter/error-handling/references/REFERENCE.md +19 -0
- package/rules/flutter/error-handling/references/error-mapping.md +31 -0
- package/rules/flutter/feature-based-clean-architecture/SKILL.md +46 -0
- package/rules/flutter/feature-based-clean-architecture/references/REFERENCE.md +14 -0
- package/rules/flutter/feature-based-clean-architecture/references/folder-structure.md +36 -0
- package/rules/flutter/getx-navigation/SKILL.md +70 -0
- package/rules/flutter/getx-navigation/references/app-pages.md +40 -0
- package/rules/flutter/getx-navigation/references/middleware-example.md +29 -0
- package/rules/flutter/getx-state-management/SKILL.md +76 -0
- package/rules/flutter/getx-state-management/references/binding-example.md +32 -0
- package/rules/flutter/getx-state-management/references/reactive-vs-simple.md +39 -0
- package/rules/flutter/go-router-navigation/SKILL.md +57 -0
- package/rules/flutter/idiomatic-flutter/SKILL.md +20 -0
- package/rules/flutter/layer-based-clean-architecture/SKILL.md +50 -0
- package/rules/flutter/layer-based-clean-architecture/references/REFERENCE.md +60 -0
- package/rules/flutter/layer-based-clean-architecture/references/repository-mapping.md +50 -0
- package/rules/flutter/localization/SKILL.md +50 -0
- package/rules/flutter/localization/references/REFERENCE.md +48 -0
- package/rules/flutter/localization/references/sheet-loader.md +33 -0
- package/rules/flutter/navigator-v1-navigation/SKILL.md +71 -0
- package/rules/flutter/navigator-v1-navigation/references/on-generate-route.md +48 -0
- package/rules/flutter/performance/SKILL.md +24 -0
- package/rules/flutter/retrofit-networking/SKILL.md +51 -0
- package/rules/flutter/retrofit-networking/references/REFERENCE.md +19 -0
- package/rules/flutter/retrofit-networking/references/token-refresh.md +40 -0
- package/rules/flutter/riverpod-state-management/SKILL.md +53 -0
- package/rules/flutter/riverpod-state-management/references/architecture.md +124 -0
- package/rules/flutter/riverpod-state-management/references/best-practices.md +89 -0
- package/rules/flutter/riverpod-state-management/references/testing.md +73 -0
- package/rules/flutter/riverpod.rule.md +78 -0
- package/rules/flutter/security/SKILL.md +33 -0
- package/rules/flutter/security/references/REFERENCE.md +15 -0
- package/rules/flutter/security/references/network-security.md +28 -0
- package/rules/flutter/testing/SKILL.md +44 -0
- package/rules/flutter/testing/references/REFERENCE.md +21 -0
- package/rules/flutter/testing/references/bloc-testing.md +38 -0
- package/rules/flutter/testing/references/integration-testing.md +128 -0
- package/rules/flutter/testing/references/robot-pattern.md +82 -0
- package/rules/flutter/testing/references/unit-testing.md +130 -0
- package/rules/flutter/testing/references/widget-testing.md +120 -0
- package/rules/flutter/widgets/SKILL.md +37 -0
- package/rules/golang/chi-router/SKILL.md +219 -0
- package/rules/golang/chi-router/references/REFERENCE.md +13 -0
- package/rules/golang/chi-router/references/routing-patterns.md +205 -0
- package/rules/golang/cobra-cli/SKILL.md +227 -0
- package/rules/golang/cobra-cli/references/REFERENCE.md +13 -0
- package/rules/golang/cobra-cli/references/command-patterns.md +224 -0
- package/rules/golang/core/SKILL.md +210 -0
- package/rules/golang/core/references/REFERENCE.md +14 -0
- package/rules/golang/core/references/concurrency-patterns.md +114 -0
- package/rules/golang/core/references/error-handling.md +87 -0
- package/rules/golang/echo-framework/SKILL.md +215 -0
- package/rules/golang/echo-framework/references/REFERENCE.md +14 -0
- package/rules/golang/echo-framework/references/middleware-patterns.md +141 -0
- package/rules/golang/echo-framework/references/routing-patterns.md +140 -0
- package/rules/golang/ent-orm/SKILL.md +239 -0
- package/rules/golang/ent-orm/references/REFERENCE.md +13 -0
- package/rules/golang/ent-orm/references/schema-patterns.md +255 -0
- package/rules/golang/fiber-framework/SKILL.md +196 -0
- package/rules/golang/fiber-framework/references/REFERENCE.md +13 -0
- package/rules/golang/fiber-framework/references/routing-patterns.md +191 -0
- package/rules/golang/gin-framework/SKILL.md +205 -0
- package/rules/golang/gin-framework/references/REFERENCE.md +14 -0
- package/rules/golang/gin-framework/references/middleware-patterns.md +119 -0
- package/rules/golang/gorm-orm/SKILL.md +196 -0
- package/rules/golang/gorm-orm/references/REFERENCE.md +14 -0
- package/rules/golang/gorm-orm/references/model-definitions.md +167 -0
- package/rules/golang/gorm-orm/references/query-patterns.md +161 -0
- package/rules/golang/grpc/SKILL.md +231 -0
- package/rules/golang/grpc/references/REFERENCE.md +13 -0
- package/rules/golang/grpc/references/service-patterns.md +276 -0
- package/rules/golang/testify/SKILL.md +239 -0
- package/rules/golang/testify/references/REFERENCE.md +13 -0
- package/rules/golang/testify/references/assert-patterns.md +170 -0
- package/rules/golang/validator/SKILL.md +234 -0
- package/rules/golang/validator/references/REFERENCE.md +13 -0
- package/rules/golang/validator/references/validation-tags.md +211 -0
- package/rules/golang/viper-config/SKILL.md +244 -0
- package/rules/golang/viper-config/references/REFERENCE.md +13 -0
- package/rules/golang/viper-config/references/config-loading.md +181 -0
- package/rules/golang/wire-di/SKILL.md +243 -0
- package/rules/golang/wire-di/references/REFERENCE.md +13 -0
- package/rules/golang/wire-di/references/provider-patterns.md +193 -0
- package/rules/golang/zap-logging/SKILL.md +203 -0
- package/rules/golang/zap-logging/references/REFERENCE.md +13 -0
- package/rules/golang/zap-logging/references/logger-config.md +165 -0
- package/rules/java/build-gradle/SKILL.md +92 -0
- package/rules/java/build-gradle/references/REFERENCE.md +14 -0
- package/rules/java/build-gradle/references/kotlin-dsl.md +118 -0
- package/rules/java/build-gradle/references/task-configuration.md +132 -0
- package/rules/java/build-maven/SKILL.md +86 -0
- package/rules/java/build-maven/references/REFERENCE.md +14 -0
- package/rules/java/build-maven/references/dependency-management.md +111 -0
- package/rules/java/build-maven/references/lifecycle-phases.md +114 -0
- package/rules/java/graalvm-native/SKILL.md +105 -0
- package/rules/java/graalvm-native/references/REFERENCE.md +12 -0
- package/rules/java/java-collections-streams/SKILL.md +148 -0
- package/rules/java/java-collections-streams/references/REFERENCE.md +15 -0
- package/rules/java/java-collections-streams/references/collectors-patterns.md +178 -0
- package/rules/java/java-collections-streams/references/stream-pipelines.md +165 -0
- package/rules/java/java-concurrency/SKILL.md +187 -0
- package/rules/java/java-concurrency/references/REFERENCE.md +17 -0
- package/rules/java/java-concurrency/references/completable-future.md +165 -0
- package/rules/java/java-concurrency/references/executor-patterns.md +176 -0
- package/rules/java/java-concurrency/references/virtual-threads.md +190 -0
- package/rules/java/java-core-language/SKILL.md +121 -0
- package/rules/java/java-core-language/references/REFERENCE.md +15 -0
- package/rules/java/java-core-language/references/jvm-memory-model.md +160 -0
- package/rules/java/java-core-language/references/modern-java-features.md +168 -0
- package/rules/java/java-project-structure/SKILL.md +195 -0
- package/rules/java/java-project-structure/references/REFERENCE.md +15 -0
- package/rules/java/java-project-structure/references/maven-project-layout.md +199 -0
- package/rules/java/java-project-structure/references/module-system.md +159 -0
- package/rules/java/micronaut-core/SKILL.md +99 -0
- package/rules/java/micronaut-core/references/REFERENCE.md +12 -0
- package/rules/java/micronaut-reactive/SKILL.md +68 -0
- package/rules/java/micronaut-reactive/references/REFERENCE.md +12 -0
- package/rules/java/quarkus-core/SKILL.md +85 -0
- package/rules/java/quarkus-core/references/REFERENCE.md +12 -0
- package/rules/java/quarkus-reactive/SKILL.md +67 -0
- package/rules/java/quarkus-reactive/references/REFERENCE.md +12 -0
- package/rules/java/spring-batch/SKILL.md +102 -0
- package/rules/java/spring-batch/references/REFERENCE.md +12 -0
- package/rules/java/spring-boot-architecture/SKILL.md +206 -0
- package/rules/java/spring-boot-architecture/references/REFERENCE.md +15 -0
- package/rules/java/spring-boot-architecture/references/auto-configuration.md +158 -0
- package/rules/java/spring-boot-architecture/references/configuration-properties.md +202 -0
- package/rules/java/spring-boot-web/SKILL.md +217 -0
- package/rules/java/spring-boot-web/references/REFERENCE.md +17 -0
- package/rules/java/spring-cloud/SKILL.md +109 -0
- package/rules/java/spring-cloud/references/REFERENCE.md +13 -0
- package/rules/java/spring-data-jpa/SKILL.md +241 -0
- package/rules/java/spring-data-jpa/references/REFERENCE.md +16 -0
- package/rules/java/spring-security/SKILL.md +161 -0
- package/rules/java/spring-security/references/REFERENCE.md +16 -0
- package/rules/java/spring-security/references/jwt-auth-flow.md +213 -0
- package/rules/java/testing-junit-mockito/SKILL.md +135 -0
- package/rules/java/testing-junit-mockito/references/REFERENCE.md +15 -0
- package/rules/java/testing-junit-mockito/references/junit5-patterns.md +159 -0
- package/rules/java/testing-junit-mockito/references/mockito-patterns.md +148 -0
- package/rules/java/testing-junit-mockito/references/spring-boot-testing.md +152 -0
- package/rules/javascript/best-practices/SKILL.md +64 -0
- package/rules/javascript/best-practices/references/REFERENCE.md +91 -0
- package/rules/javascript/language/SKILL.md +71 -0
- package/rules/javascript/language/references/REFERENCE.md +106 -0
- package/rules/javascript/tooling/SKILL.md +60 -0
- package/rules/javascript/tooling/references/REFERENCE.md +107 -0
- package/rules/metadata.json +54 -0
- package/rules/nestjs/api-standards/SKILL.md +47 -0
- package/rules/nestjs/api-standards/references/pagination-wrapper.md +87 -0
- package/rules/nestjs/architecture/SKILL.md +68 -0
- package/rules/nestjs/architecture/references/dynamic-module.md +53 -0
- package/rules/nestjs/caching/SKILL.md +51 -0
- package/rules/nestjs/caching/references/REFERENCE.md +13 -0
- package/rules/nestjs/caching/references/cache-patterns.md +183 -0
- package/rules/nestjs/configuration/SKILL.md +41 -0
- package/rules/nestjs/configuration/references/REFERENCE.md +13 -0
- package/rules/nestjs/configuration/references/config-patterns.md +184 -0
- package/rules/nestjs/controllers-services/SKILL.md +63 -0
- package/rules/nestjs/controllers-services/references/REFERENCE.md +14 -0
- package/rules/nestjs/controllers-services/references/controller-patterns.md +119 -0
- package/rules/nestjs/controllers-services/references/service-patterns.md +129 -0
- package/rules/nestjs/database/SKILL.md +102 -0
- package/rules/nestjs/database/references/REFERENCE.md +14 -0
- package/rules/nestjs/database/references/typeorm-patterns.md +156 -0
- package/rules/nestjs/deployment/SKILL.md +36 -0
- package/rules/nestjs/deployment/references/REFERENCE.md +13 -0
- package/rules/nestjs/deployment/references/deployment-patterns.md +140 -0
- package/rules/nestjs/documentation/SKILL.md +64 -0
- package/rules/nestjs/documentation/references/REFERENCE.md +13 -0
- package/rules/nestjs/documentation/references/swagger-patterns.md +139 -0
- package/rules/nestjs/error-handling/SKILL.md +55 -0
- package/rules/nestjs/error-handling/references/REFERENCE.md +13 -0
- package/rules/nestjs/error-handling/references/exception-filters.md +152 -0
- package/rules/nestjs/file-uploads/SKILL.md +35 -0
- package/rules/nestjs/file-uploads/references/REFERENCE.md +13 -0
- package/rules/nestjs/file-uploads/references/upload-patterns.md +125 -0
- package/rules/nestjs/observability/SKILL.md +39 -0
- package/rules/nestjs/observability/references/REFERENCE.md +13 -0
- package/rules/nestjs/observability/references/logging-metrics.md +175 -0
- package/rules/nestjs/performance/SKILL.md +60 -0
- package/rules/nestjs/performance/references/REFERENCE.md +13 -0
- package/rules/nestjs/performance/references/performance-patterns.md +107 -0
- package/rules/nestjs/real-time/SKILL.md +45 -0
- package/rules/nestjs/real-time/references/REFERENCE.md +13 -0
- package/rules/nestjs/real-time/references/websocket-patterns.md +121 -0
- package/rules/nestjs/scheduling/SKILL.md +39 -0
- package/rules/nestjs/scheduling/references/REFERENCE.md +13 -0
- package/rules/nestjs/scheduling/references/scheduling-patterns.md +137 -0
- package/rules/nestjs/search/SKILL.md +41 -0
- package/rules/nestjs/search/references/REFERENCE.md +13 -0
- package/rules/nestjs/search/references/search-patterns.md +137 -0
- package/rules/nestjs/security/SKILL.md +87 -0
- package/rules/nestjs/security/references/REFERENCE.md +14 -0
- package/rules/nestjs/security/references/authentication.md +151 -0
- package/rules/nestjs/testing/SKILL.md +40 -0
- package/rules/nestjs/testing/references/REFERENCE.md +14 -0
- package/rules/nestjs/testing/references/unit-testing.md +179 -0
- package/rules/nestjs/transport/SKILL.md +45 -0
- package/rules/nestjs/transport/references/REFERENCE.md +13 -0
- package/rules/nestjs/transport/references/microservices-patterns.md +170 -0
- package/rules/nextjs/app-router/SKILL.md +46 -0
- package/rules/nextjs/app-router/references/REFERENCE.md +14 -0
- package/rules/nextjs/app-router/references/routing-patterns.md +182 -0
- package/rules/nextjs/architecture/SKILL.md +44 -0
- package/rules/nextjs/architecture/references/fsd-structure.md +77 -0
- package/rules/nextjs/authentication/SKILL.md +29 -0
- package/rules/nextjs/authentication/references/auth-implementation.md +73 -0
- package/rules/nextjs/caching/SKILL.md +66 -0
- package/rules/nextjs/caching/references/REFERENCE.md +13 -0
- package/rules/nextjs/caching/references/cache-strategies.md +168 -0
- package/rules/nextjs/data-access-layer/SKILL.md +33 -0
- package/rules/nextjs/data-access-layer/references/patterns.md +66 -0
- package/rules/nextjs/data-fetching/SKILL.md +59 -0
- package/rules/nextjs/data-fetching/references/REFERENCE.md +13 -0
- package/rules/nextjs/data-fetching/references/fetch-patterns.md +160 -0
- package/rules/nextjs/internationalization/SKILL.md +105 -0
- package/rules/nextjs/internationalization/references/REFERENCE.md +13 -0
- package/rules/nextjs/internationalization/references/i18n-patterns.md +180 -0
- package/rules/nextjs/optimization/SKILL.md +64 -0
- package/rules/nextjs/optimization/references/REFERENCE.md +13 -0
- package/rules/nextjs/optimization/references/optimization-patterns.md +190 -0
- package/rules/nextjs/rendering/SKILL.md +91 -0
- package/rules/nextjs/rendering/references/REFERENCE.md +13 -0
- package/rules/nextjs/rendering/references/rendering-modes.md +163 -0
- package/rules/nextjs/server-actions/SKILL.md +46 -0
- package/rules/nextjs/server-actions/references/REFERENCE.md +13 -0
- package/rules/nextjs/server-actions/references/action-patterns.md +188 -0
- package/rules/nextjs/server-components/SKILL.md +52 -0
- package/rules/nextjs/server-components/references/REFERENCE.md +13 -0
- package/rules/nextjs/server-components/references/component-patterns.md +175 -0
- package/rules/nextjs/state-management/SKILL.md +73 -0
- package/rules/nextjs/state-management/references/REFERENCE.md +13 -0
- package/rules/nextjs/state-management/references/state-patterns.md +218 -0
- package/rules/nextjs/styling/SKILL.md +31 -0
- package/rules/nextjs/styling/references/implementation.md +56 -0
- package/rules/react/component-patterns/SKILL.md +66 -0
- package/rules/react/component-patterns/references/REFERENCE.md +126 -0
- package/rules/react/hooks/SKILL.md +60 -0
- package/rules/react/hooks/references/REFERENCE.md +132 -0
- package/rules/react/hooks.rule.md +79 -0
- package/rules/react/performance/SKILL.md +69 -0
- package/rules/react/performance/references/REFERENCE.md +143 -0
- package/rules/react/security/SKILL.md +46 -0
- package/rules/react/security/references/REFERENCE.md +170 -0
- package/rules/react/state-management/SKILL.md +56 -0
- package/rules/react/state-management/references/REFERENCE.md +137 -0
- package/rules/react/testing/SKILL.md +45 -0
- package/rules/react/testing/references/REFERENCE.md +149 -0
- package/rules/react/tooling/SKILL.md +39 -0
- package/rules/react/typescript/SKILL.md +53 -0
- package/rules/rust/actix-web/SKILL.md +160 -0
- package/rules/rust/actix-web/references/REFERENCE.md +13 -0
- package/rules/rust/actix-web/references/handler-patterns.md +198 -0
- package/rules/rust/async-graphql/SKILL.md +228 -0
- package/rules/rust/async-graphql/references/REFERENCE.md +13 -0
- package/rules/rust/async-graphql/references/schema-patterns.md +215 -0
- package/rules/rust/axum/SKILL.md +161 -0
- package/rules/rust/axum/references/REFERENCE.md +14 -0
- package/rules/rust/axum/references/handler-patterns.md +97 -0
- package/rules/rust/bevy/SKILL.md +206 -0
- package/rules/rust/bevy/references/REFERENCE.md +13 -0
- package/rules/rust/bevy/references/ecs-patterns.md +226 -0
- package/rules/rust/clap/SKILL.md +217 -0
- package/rules/rust/clap/references/REFERENCE.md +13 -0
- package/rules/rust/clap/references/derive-patterns.md +205 -0
- package/rules/rust/core/SKILL.md +154 -0
- package/rules/rust/core/references/REFERENCE.md +14 -0
- package/rules/rust/core/references/error-handling.md +92 -0
- package/rules/rust/diesel-orm/SKILL.md +176 -0
- package/rules/rust/diesel-orm/references/REFERENCE.md +13 -0
- package/rules/rust/diesel-orm/references/schema-patterns.md +206 -0
- package/rules/rust/rocket/SKILL.md +182 -0
- package/rules/rust/rocket/references/REFERENCE.md +13 -0
- package/rules/rust/rocket/references/handler-patterns.md +209 -0
- package/rules/rust/sea-orm/SKILL.md +230 -0
- package/rules/rust/sea-orm/references/REFERENCE.md +13 -0
- package/rules/rust/sea-orm/references/entity-patterns.md +221 -0
- package/rules/rust/serde-serialization/SKILL.md +150 -0
- package/rules/rust/serde-serialization/references/REFERENCE.md +13 -0
- package/rules/rust/serde-serialization/references/serialization-patterns.md +199 -0
- package/rules/rust/sqlx-database/SKILL.md +140 -0
- package/rules/rust/sqlx-database/references/REFERENCE.md +13 -0
- package/rules/rust/sqlx-database/references/query-patterns.md +210 -0
- package/rules/rust/tauri/SKILL.md +180 -0
- package/rules/rust/tauri/references/REFERENCE.md +13 -0
- package/rules/rust/tauri/references/command-patterns.md +209 -0
- package/rules/rust/tokio-runtime/SKILL.md +167 -0
- package/rules/rust/tokio-runtime/references/REFERENCE.md +14 -0
- package/rules/rust/tokio-runtime/references/async-patterns.md +137 -0
- package/rules/rust/tokio-runtime/references/synchronization.md +152 -0
- package/rules/rust/tonic/SKILL.md +231 -0
- package/rules/rust/tonic/references/REFERENCE.md +13 -0
- package/rules/rust/tonic/references/service-patterns.md +213 -0
- package/rules/rust/tracing/SKILL.md +214 -0
- package/rules/rust/tracing/references/REFERENCE.md +13 -0
- package/rules/rust/tracing/references/instrumentation.md +187 -0
- package/rules/typescript/best-practices/SKILL.md +108 -0
- package/rules/typescript/best-practices/references/REFERENCE.md +68 -0
- package/rules/typescript/language/SKILL.md +72 -0
- package/rules/typescript/language/references/REFERENCE.md +67 -0
- package/rules/typescript/patterns.rule.md +85 -0
- package/rules/typescript/security/SKILL.md +59 -0
- package/rules/typescript/security/references/REFERENCE.md +113 -0
- package/rules/typescript/tooling/SKILL.md +52 -0
- package/rules/typescript/tooling/references/REFERENCE.md +110 -0
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
# Stream Pipelines
|
|
2
|
+
|
|
3
|
+
## Lazy Evaluation
|
|
4
|
+
|
|
5
|
+
Streams are lazy - intermediate operations don't execute until a terminal operation is called.
|
|
6
|
+
|
|
7
|
+
```java
|
|
8
|
+
// Nothing happens until collect() is called
|
|
9
|
+
Stream<String> lazy = items.stream()
|
|
10
|
+
.peek(i -> System.out.println("Processing: " + i)) // Not called yet
|
|
11
|
+
.filter(i -> i.length() > 3)
|
|
12
|
+
.map(String::toUpperCase);
|
|
13
|
+
|
|
14
|
+
// Terminal operation triggers execution
|
|
15
|
+
List<String> result = lazy.collect(Collectors.toList());
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Short-Circuit Operations
|
|
19
|
+
|
|
20
|
+
```java
|
|
21
|
+
// findFirst - stops at first match
|
|
22
|
+
Optional<User> first = users.stream()
|
|
23
|
+
.filter(User::isAdmin)
|
|
24
|
+
.findFirst();
|
|
25
|
+
|
|
26
|
+
// anyMatch - stops when condition is true
|
|
27
|
+
boolean hasAdmin = users.stream()
|
|
28
|
+
.anyMatch(User::isAdmin);
|
|
29
|
+
|
|
30
|
+
// limit - stops after n elements
|
|
31
|
+
List<User> topThree = users.stream()
|
|
32
|
+
.sorted(Comparator.comparing(User::getScore).reversed())
|
|
33
|
+
.limit(3)
|
|
34
|
+
.toList();
|
|
35
|
+
|
|
36
|
+
// takeWhile (Java 9+) - takes elements while predicate is true
|
|
37
|
+
List<Integer> untilNegative = numbers.stream()
|
|
38
|
+
.takeWhile(n -> n >= 0)
|
|
39
|
+
.toList();
|
|
40
|
+
|
|
41
|
+
// dropWhile (Java 9+) - drops elements while predicate is true
|
|
42
|
+
List<Integer> afterNegative = numbers.stream()
|
|
43
|
+
.dropWhile(n -> n < 0)
|
|
44
|
+
.toList();
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Advanced Mapping
|
|
48
|
+
|
|
49
|
+
```java
|
|
50
|
+
// flatMap for one-to-many
|
|
51
|
+
List<Tag> allTags = posts.stream()
|
|
52
|
+
.flatMap(post -> post.getTags().stream())
|
|
53
|
+
.distinct()
|
|
54
|
+
.toList();
|
|
55
|
+
|
|
56
|
+
// mapMulti (Java 16+) - more efficient flatMap alternative
|
|
57
|
+
List<Integer> doubled = numbers.stream()
|
|
58
|
+
.<Integer>mapMulti((num, consumer) -> {
|
|
59
|
+
consumer.accept(num);
|
|
60
|
+
consumer.accept(num * 2);
|
|
61
|
+
})
|
|
62
|
+
.toList();
|
|
63
|
+
|
|
64
|
+
// Conditional mapping
|
|
65
|
+
List<String> processed = items.stream()
|
|
66
|
+
.map(item -> switch (item.getType()) {
|
|
67
|
+
case A -> processTypeA(item);
|
|
68
|
+
case B -> processTypeB(item);
|
|
69
|
+
default -> item.getName();
|
|
70
|
+
})
|
|
71
|
+
.toList();
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Reducing Operations
|
|
75
|
+
|
|
76
|
+
```java
|
|
77
|
+
// Simple reduce
|
|
78
|
+
int sum = numbers.stream()
|
|
79
|
+
.reduce(0, Integer::sum);
|
|
80
|
+
|
|
81
|
+
// Reduce with identity, accumulator, combiner (for parallel)
|
|
82
|
+
int total = orders.parallelStream()
|
|
83
|
+
.reduce(
|
|
84
|
+
0, // identity
|
|
85
|
+
(acc, order) -> acc + order.getTotal(), // accumulator
|
|
86
|
+
Integer::sum // combiner (for parallel)
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
// Collect vs Reduce
|
|
90
|
+
// Use collect for mutable reduction (building collections)
|
|
91
|
+
List<String> list = stream.collect(Collectors.toList());
|
|
92
|
+
|
|
93
|
+
// Use reduce for immutable reduction (computing single value)
|
|
94
|
+
Optional<BigDecimal> total = amounts.stream()
|
|
95
|
+
.reduce(BigDecimal::add);
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Parallel Streams
|
|
99
|
+
|
|
100
|
+
```java
|
|
101
|
+
// When to use parallel streams:
|
|
102
|
+
// 1. Large dataset (> 10,000 elements)
|
|
103
|
+
// 2. CPU-bound operations (not I/O)
|
|
104
|
+
// 3. Independent operations (no shared mutable state)
|
|
105
|
+
// 4. Splittable data source (ArrayList, arrays)
|
|
106
|
+
|
|
107
|
+
// Good use case
|
|
108
|
+
long count = hugeList.parallelStream() // ArrayList, large
|
|
109
|
+
.filter(this::cpuIntensiveCheck) // CPU-bound
|
|
110
|
+
.count();
|
|
111
|
+
|
|
112
|
+
// Bad use cases
|
|
113
|
+
// - LinkedList (not easily splittable)
|
|
114
|
+
// - I/O operations (blocking, not CPU-bound)
|
|
115
|
+
// - Small datasets (overhead > benefit)
|
|
116
|
+
// - Operations with side effects
|
|
117
|
+
|
|
118
|
+
// Custom thread pool for parallel streams
|
|
119
|
+
ForkJoinPool customPool = new ForkJoinPool(4);
|
|
120
|
+
List<Result> results = customPool.submit(() ->
|
|
121
|
+
items.parallelStream()
|
|
122
|
+
.map(this::process)
|
|
123
|
+
.toList()
|
|
124
|
+
).get();
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Infinite Streams
|
|
128
|
+
|
|
129
|
+
```java
|
|
130
|
+
// Generate infinite stream
|
|
131
|
+
Stream<UUID> uuids = Stream.generate(UUID::randomUUID);
|
|
132
|
+
|
|
133
|
+
// Iterate with seed
|
|
134
|
+
Stream<Integer> evens = Stream.iterate(0, n -> n + 2);
|
|
135
|
+
|
|
136
|
+
// Iterate with predicate (Java 9+)
|
|
137
|
+
Stream<Integer> limited = Stream.iterate(1, n -> n < 100, n -> n * 2);
|
|
138
|
+
|
|
139
|
+
// Use with limit
|
|
140
|
+
List<UUID> fiveUuids = Stream.generate(UUID::randomUUID)
|
|
141
|
+
.limit(5)
|
|
142
|
+
.toList();
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Debugging Streams
|
|
146
|
+
|
|
147
|
+
```java
|
|
148
|
+
// peek for debugging (remove in production)
|
|
149
|
+
List<String> result = items.stream()
|
|
150
|
+
.peek(i -> log.debug("Before filter: {}", i))
|
|
151
|
+
.filter(Item::isActive)
|
|
152
|
+
.peek(i -> log.debug("After filter: {}", i))
|
|
153
|
+
.map(Item::getName)
|
|
154
|
+
.peek(n -> log.debug("After map: {}", n))
|
|
155
|
+
.toList();
|
|
156
|
+
|
|
157
|
+
// Breakpoint-friendly version
|
|
158
|
+
List<String> result = items.stream()
|
|
159
|
+
.filter(item -> {
|
|
160
|
+
boolean active = item.isActive(); // Set breakpoint here
|
|
161
|
+
return active;
|
|
162
|
+
})
|
|
163
|
+
.map(Item::getName)
|
|
164
|
+
.toList();
|
|
165
|
+
```
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Java Concurrency
|
|
3
|
+
description: Thread management, Executor framework, CompletableFuture, synchronization, and virtual threads.
|
|
4
|
+
metadata:
|
|
5
|
+
labels: [java, concurrency, threads, async]
|
|
6
|
+
triggers:
|
|
7
|
+
files: ['**/*.java']
|
|
8
|
+
keywords: [Thread, Runnable, Executor, CompletableFuture, synchronized, Lock, Atomic, Future, Callable, ExecutorService]
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Java Concurrency Standards
|
|
12
|
+
|
|
13
|
+
## Executor Framework
|
|
14
|
+
|
|
15
|
+
```java
|
|
16
|
+
// Fixed thread pool - bounded, predictable
|
|
17
|
+
ExecutorService executor = Executors.newFixedThreadPool(
|
|
18
|
+
Runtime.getRuntime().availableProcessors()
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
// Cached thread pool - unbounded, use carefully
|
|
22
|
+
ExecutorService cached = Executors.newCachedThreadPool();
|
|
23
|
+
|
|
24
|
+
// Scheduled executor
|
|
25
|
+
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);
|
|
26
|
+
scheduler.scheduleAtFixedRate(task, 0, 1, TimeUnit.SECONDS);
|
|
27
|
+
|
|
28
|
+
// Virtual threads (Java 21+)
|
|
29
|
+
ExecutorService virtual = Executors.newVirtualThreadPerTaskExecutor();
|
|
30
|
+
|
|
31
|
+
// Custom thread pool
|
|
32
|
+
ThreadPoolExecutor custom = new ThreadPoolExecutor(
|
|
33
|
+
4, // core pool size
|
|
34
|
+
8, // max pool size
|
|
35
|
+
60, TimeUnit.SECONDS, // keep alive
|
|
36
|
+
new LinkedBlockingQueue<>(100), // work queue
|
|
37
|
+
new ThreadPoolExecutor.CallerRunsPolicy() // rejection handler
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
// Always shutdown executors
|
|
41
|
+
try {
|
|
42
|
+
executor.shutdown();
|
|
43
|
+
if (!executor.awaitTermination(30, TimeUnit.SECONDS)) {
|
|
44
|
+
executor.shutdownNow();
|
|
45
|
+
}
|
|
46
|
+
} catch (InterruptedException e) {
|
|
47
|
+
executor.shutdownNow();
|
|
48
|
+
Thread.currentThread().interrupt();
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## CompletableFuture
|
|
53
|
+
|
|
54
|
+
```java
|
|
55
|
+
// Async execution
|
|
56
|
+
CompletableFuture<User> userFuture = CompletableFuture
|
|
57
|
+
.supplyAsync(() -> userService.findById(id), executor);
|
|
58
|
+
|
|
59
|
+
// Chaining
|
|
60
|
+
CompletableFuture<String> result = userFuture
|
|
61
|
+
.thenApply(User::getEmail)
|
|
62
|
+
.thenApply(String::toLowerCase)
|
|
63
|
+
.exceptionally(ex -> "unknown@example.com");
|
|
64
|
+
|
|
65
|
+
// Combine multiple futures
|
|
66
|
+
CompletableFuture<UserProfile> profile = CompletableFuture
|
|
67
|
+
.allOf(userFuture, ordersFuture, preferencesFuture)
|
|
68
|
+
.thenApply(v -> new UserProfile(
|
|
69
|
+
userFuture.join(),
|
|
70
|
+
ordersFuture.join(),
|
|
71
|
+
preferencesFuture.join()
|
|
72
|
+
));
|
|
73
|
+
|
|
74
|
+
// Either/race
|
|
75
|
+
CompletableFuture<String> fastest = CompletableFuture
|
|
76
|
+
.anyOf(primaryService, fallbackService)
|
|
77
|
+
.thenApply(result -> (String) result);
|
|
78
|
+
|
|
79
|
+
// Timeout (Java 9+)
|
|
80
|
+
CompletableFuture<User> withTimeout = userFuture
|
|
81
|
+
.orTimeout(5, TimeUnit.SECONDS)
|
|
82
|
+
.exceptionally(ex -> defaultUser);
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Synchronization
|
|
86
|
+
|
|
87
|
+
```java
|
|
88
|
+
// synchronized block - prefer over method
|
|
89
|
+
private final Object lock = new Object();
|
|
90
|
+
|
|
91
|
+
public void update(String value) {
|
|
92
|
+
synchronized (lock) {
|
|
93
|
+
// critical section
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// ReentrantLock - more flexible
|
|
98
|
+
private final ReentrantLock lock = new ReentrantLock();
|
|
99
|
+
|
|
100
|
+
public void process() {
|
|
101
|
+
lock.lock();
|
|
102
|
+
try {
|
|
103
|
+
// critical section
|
|
104
|
+
} finally {
|
|
105
|
+
lock.unlock();
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// ReadWriteLock - multiple readers, single writer
|
|
110
|
+
private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
|
|
111
|
+
|
|
112
|
+
public String read() {
|
|
113
|
+
rwLock.readLock().lock();
|
|
114
|
+
try {
|
|
115
|
+
return data;
|
|
116
|
+
} finally {
|
|
117
|
+
rwLock.readLock().unlock();
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
public void write(String value) {
|
|
122
|
+
rwLock.writeLock().lock();
|
|
123
|
+
try {
|
|
124
|
+
data = value;
|
|
125
|
+
} finally {
|
|
126
|
+
rwLock.writeLock().unlock();
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## Atomic Classes
|
|
132
|
+
|
|
133
|
+
```java
|
|
134
|
+
// Atomic primitives
|
|
135
|
+
AtomicInteger counter = new AtomicInteger(0);
|
|
136
|
+
counter.incrementAndGet();
|
|
137
|
+
counter.compareAndSet(expected, newValue);
|
|
138
|
+
|
|
139
|
+
// Atomic reference
|
|
140
|
+
AtomicReference<Config> configRef = new AtomicReference<>(initialConfig);
|
|
141
|
+
configRef.updateAndGet(config -> config.withNewValue(value));
|
|
142
|
+
|
|
143
|
+
// LongAdder for high contention
|
|
144
|
+
LongAdder adder = new LongAdder();
|
|
145
|
+
adder.increment();
|
|
146
|
+
long sum = adder.sum();
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## Virtual Threads (Java 21+)
|
|
150
|
+
|
|
151
|
+
```java
|
|
152
|
+
// Simple virtual thread
|
|
153
|
+
Thread.startVirtualThread(() -> {
|
|
154
|
+
// blocking I/O is fine
|
|
155
|
+
String data = httpClient.get(url);
|
|
156
|
+
process(data);
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
// With executor
|
|
160
|
+
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
|
|
161
|
+
List<Future<Result>> futures = tasks.stream()
|
|
162
|
+
.map(task -> executor.submit(task::execute))
|
|
163
|
+
.toList();
|
|
164
|
+
|
|
165
|
+
for (Future<Result> future : futures) {
|
|
166
|
+
results.add(future.get());
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Don't use with CPU-bound tasks
|
|
171
|
+
// Don't use synchronized for long operations (use ReentrantLock)
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## Best Practices
|
|
175
|
+
|
|
176
|
+
1. **Prefer high-level constructs** (Executor, CompletableFuture) over raw threads
|
|
177
|
+
2. **Size thread pools** based on task type (CPU-bound: cores, I/O-bound: higher)
|
|
178
|
+
3. **Always handle InterruptedException** - restore interrupt status
|
|
179
|
+
4. **Use virtual threads** for I/O-bound tasks (Java 21+)
|
|
180
|
+
5. **Prefer Atomic classes** over synchronized for simple counters
|
|
181
|
+
6. **Immutability** is the best synchronization
|
|
182
|
+
|
|
183
|
+
## References
|
|
184
|
+
|
|
185
|
+
- [Executor Patterns](references/executor-patterns.md) - Thread pool configuration, shutdown
|
|
186
|
+
- [CompletableFuture](references/completable-future.md) - Async composition patterns
|
|
187
|
+
- [Virtual Threads](references/virtual-threads.md) - Java 21+ patterns
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Java Concurrency References
|
|
2
|
+
|
|
3
|
+
## References
|
|
4
|
+
|
|
5
|
+
- [**Executor Patterns**](executor-patterns.md) - Thread pool sizing, configuration, graceful shutdown
|
|
6
|
+
- [**CompletableFuture**](completable-future.md) - Async composition, error handling, timeouts
|
|
7
|
+
- [**Virtual Threads**](virtual-threads.md) - Java 21+ patterns, when to use, pitfalls
|
|
8
|
+
|
|
9
|
+
## Quick Checks
|
|
10
|
+
|
|
11
|
+
- [ ] Use ExecutorService over raw Thread creation
|
|
12
|
+
- [ ] Size thread pools appropriately (CPU-bound: cores, I/O-bound: higher)
|
|
13
|
+
- [ ] Always shutdown executors in finally/try-with-resources
|
|
14
|
+
- [ ] Handle InterruptedException - restore interrupt status
|
|
15
|
+
- [ ] Use virtual threads for I/O-bound tasks (Java 21+)
|
|
16
|
+
- [ ] Prefer immutability over synchronization
|
|
17
|
+
- [ ] Use AtomicXxx for simple counters, Lock for complex critical sections
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
# CompletableFuture Patterns
|
|
2
|
+
|
|
3
|
+
## Basic Composition
|
|
4
|
+
|
|
5
|
+
```java
|
|
6
|
+
// Async execution with custom executor
|
|
7
|
+
CompletableFuture<User> userFuture = CompletableFuture
|
|
8
|
+
.supplyAsync(() -> userRepository.findById(id), ioExecutor);
|
|
9
|
+
|
|
10
|
+
// Transform result
|
|
11
|
+
CompletableFuture<String> emailFuture = userFuture
|
|
12
|
+
.thenApply(User::getEmail);
|
|
13
|
+
|
|
14
|
+
// Async transformation
|
|
15
|
+
CompletableFuture<Profile> profileFuture = userFuture
|
|
16
|
+
.thenApplyAsync(user -> profileService.load(user), ioExecutor);
|
|
17
|
+
|
|
18
|
+
// Consume result
|
|
19
|
+
userFuture.thenAccept(user -> log.info("Loaded user: {}", user.getName()));
|
|
20
|
+
|
|
21
|
+
// Run after completion (no access to result)
|
|
22
|
+
userFuture.thenRun(() -> metrics.increment("user.loaded"));
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Combining Futures
|
|
26
|
+
|
|
27
|
+
```java
|
|
28
|
+
// Combine two futures
|
|
29
|
+
CompletableFuture<UserWithOrders> combined = userFuture
|
|
30
|
+
.thenCombine(ordersFuture, (user, orders) -> new UserWithOrders(user, orders));
|
|
31
|
+
|
|
32
|
+
// Chain dependent futures
|
|
33
|
+
CompletableFuture<List<OrderDetails>> details = userFuture
|
|
34
|
+
.thenCompose(user -> orderService.findByUser(user.getId()));
|
|
35
|
+
|
|
36
|
+
// Wait for all
|
|
37
|
+
CompletableFuture<Void> all = CompletableFuture.allOf(
|
|
38
|
+
future1, future2, future3
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
// Get results after allOf
|
|
42
|
+
CompletableFuture<List<Result>> results = all.thenApply(v -> List.of(
|
|
43
|
+
future1.join(),
|
|
44
|
+
future2.join(),
|
|
45
|
+
future3.join()
|
|
46
|
+
));
|
|
47
|
+
|
|
48
|
+
// First to complete (race)
|
|
49
|
+
CompletableFuture<String> fastest = CompletableFuture.anyOf(
|
|
50
|
+
primaryService.call(),
|
|
51
|
+
backupService.call()
|
|
52
|
+
).thenApply(result -> (String) result);
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Error Handling
|
|
56
|
+
|
|
57
|
+
```java
|
|
58
|
+
// Recover from exception
|
|
59
|
+
CompletableFuture<User> withFallback = userFuture
|
|
60
|
+
.exceptionally(ex -> {
|
|
61
|
+
log.warn("Failed to load user: {}", ex.getMessage());
|
|
62
|
+
return User.guest();
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// Handle both success and failure
|
|
66
|
+
CompletableFuture<Result> handled = userFuture
|
|
67
|
+
.handle((user, ex) -> {
|
|
68
|
+
if (ex != null) {
|
|
69
|
+
return Result.failure(ex);
|
|
70
|
+
}
|
|
71
|
+
return Result.success(user);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// Async exception handling
|
|
75
|
+
CompletableFuture<User> recovered = userFuture
|
|
76
|
+
.exceptionallyAsync(ex -> loadFromCache(id), cacheExecutor);
|
|
77
|
+
|
|
78
|
+
// whenComplete - observe without changing result
|
|
79
|
+
userFuture.whenComplete((user, ex) -> {
|
|
80
|
+
if (ex != null) {
|
|
81
|
+
metrics.increment("user.load.failed");
|
|
82
|
+
} else {
|
|
83
|
+
metrics.increment("user.load.success");
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Timeouts (Java 9+)
|
|
89
|
+
|
|
90
|
+
```java
|
|
91
|
+
// Timeout with exception
|
|
92
|
+
CompletableFuture<User> withTimeout = userFuture
|
|
93
|
+
.orTimeout(5, TimeUnit.SECONDS);
|
|
94
|
+
|
|
95
|
+
// Timeout with fallback value
|
|
96
|
+
CompletableFuture<User> withFallback = userFuture
|
|
97
|
+
.completeOnTimeout(User.guest(), 5, TimeUnit.SECONDS);
|
|
98
|
+
|
|
99
|
+
// Manual timeout (pre-Java 9)
|
|
100
|
+
CompletableFuture<User> manual = new CompletableFuture<>();
|
|
101
|
+
scheduler.schedule(
|
|
102
|
+
() -> manual.completeExceptionally(new TimeoutException()),
|
|
103
|
+
5, TimeUnit.SECONDS
|
|
104
|
+
);
|
|
105
|
+
userFuture.thenAccept(manual::complete);
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Parallel Execution
|
|
109
|
+
|
|
110
|
+
```java
|
|
111
|
+
// Execute multiple independent tasks in parallel
|
|
112
|
+
public CompletableFuture<Dashboard> loadDashboard(String userId) {
|
|
113
|
+
CompletableFuture<User> userF = loadUser(userId);
|
|
114
|
+
CompletableFuture<List<Order>> ordersF = loadOrders(userId);
|
|
115
|
+
CompletableFuture<Preferences> prefsF = loadPreferences(userId);
|
|
116
|
+
CompletableFuture<Recommendations> recsF = loadRecommendations(userId);
|
|
117
|
+
|
|
118
|
+
return CompletableFuture.allOf(userF, ordersF, prefsF, recsF)
|
|
119
|
+
.thenApply(v -> new Dashboard(
|
|
120
|
+
userF.join(),
|
|
121
|
+
ordersF.join(),
|
|
122
|
+
prefsF.join(),
|
|
123
|
+
recsF.join()
|
|
124
|
+
));
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Collect results from list of futures
|
|
128
|
+
public <T> CompletableFuture<List<T>> allOf(List<CompletableFuture<T>> futures) {
|
|
129
|
+
return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
|
|
130
|
+
.thenApply(v -> futures.stream()
|
|
131
|
+
.map(CompletableFuture::join)
|
|
132
|
+
.toList());
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## Best Practices
|
|
137
|
+
|
|
138
|
+
```java
|
|
139
|
+
// 1. Always specify executor for async operations
|
|
140
|
+
CompletableFuture.supplyAsync(task, executor); // Good
|
|
141
|
+
CompletableFuture.supplyAsync(task); // Uses ForkJoinPool.commonPool() - careful
|
|
142
|
+
|
|
143
|
+
// 2. Use join() in composition, get() when you need checked exceptions
|
|
144
|
+
results.stream()
|
|
145
|
+
.map(CompletableFuture::join) // In stream context
|
|
146
|
+
.toList();
|
|
147
|
+
|
|
148
|
+
try {
|
|
149
|
+
result.get(5, TimeUnit.SECONDS); // When timeout/exception handling needed
|
|
150
|
+
} catch (TimeoutException | ExecutionException e) {
|
|
151
|
+
// handle
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// 3. Don't block in async chains
|
|
155
|
+
userFuture
|
|
156
|
+
.thenApply(user -> {
|
|
157
|
+
// DON'T: orderService.findByUser(user.getId()).get(); // Blocks!
|
|
158
|
+
// DO: return orderService.findByUser(user.getId());
|
|
159
|
+
return user.getName();
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
// 4. Use thenCompose for dependent async operations
|
|
163
|
+
userFuture.thenCompose(user -> loadOrders(user.getId())); // Good
|
|
164
|
+
// Not: userFuture.thenApply(user -> loadOrders(user.getId()).join()); // Blocks
|
|
165
|
+
```
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# Executor Patterns
|
|
2
|
+
|
|
3
|
+
## Thread Pool Sizing
|
|
4
|
+
|
|
5
|
+
```java
|
|
6
|
+
// CPU-bound tasks: cores or cores + 1
|
|
7
|
+
int cpuBound = Runtime.getRuntime().availableProcessors();
|
|
8
|
+
|
|
9
|
+
// I/O-bound tasks: cores * (1 + wait/compute ratio)
|
|
10
|
+
// If tasks spend 80% waiting: cores * 5
|
|
11
|
+
int ioBound = cpuBound * 5;
|
|
12
|
+
|
|
13
|
+
// Mixed workloads: separate pools
|
|
14
|
+
ExecutorService cpuPool = Executors.newFixedThreadPool(cpuBound);
|
|
15
|
+
ExecutorService ioPool = Executors.newFixedThreadPool(ioBound);
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Custom Thread Pool
|
|
19
|
+
|
|
20
|
+
```java
|
|
21
|
+
// Production-ready configuration
|
|
22
|
+
ThreadPoolExecutor executor = new ThreadPoolExecutor(
|
|
23
|
+
4, // corePoolSize
|
|
24
|
+
16, // maximumPoolSize
|
|
25
|
+
60L, TimeUnit.SECONDS, // keepAliveTime
|
|
26
|
+
new LinkedBlockingQueue<>(1000), // workQueue with bounded capacity
|
|
27
|
+
new ThreadFactoryBuilder()
|
|
28
|
+
.setNameFormat("worker-%d")
|
|
29
|
+
.setUncaughtExceptionHandler((t, e) -> log.error("Thread {} failed", t, e))
|
|
30
|
+
.build(),
|
|
31
|
+
new ThreadPoolExecutor.CallerRunsPolicy() // backpressure
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
// Allow core threads to timeout (saves resources when idle)
|
|
35
|
+
executor.allowCoreThreadTimeOut(true);
|
|
36
|
+
|
|
37
|
+
// Pre-start core threads (faster initial response)
|
|
38
|
+
executor.prestartAllCoreThreads();
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Rejection Policies
|
|
42
|
+
|
|
43
|
+
```java
|
|
44
|
+
// AbortPolicy (default) - throws RejectedExecutionException
|
|
45
|
+
new ThreadPoolExecutor.AbortPolicy()
|
|
46
|
+
|
|
47
|
+
// CallerRunsPolicy - caller thread executes the task (backpressure)
|
|
48
|
+
new ThreadPoolExecutor.CallerRunsPolicy()
|
|
49
|
+
|
|
50
|
+
// DiscardPolicy - silently discards
|
|
51
|
+
new ThreadPoolExecutor.DiscardPolicy()
|
|
52
|
+
|
|
53
|
+
// DiscardOldestPolicy - discards oldest waiting task
|
|
54
|
+
new ThreadPoolExecutor.DiscardOldestPolicy()
|
|
55
|
+
|
|
56
|
+
// Custom policy
|
|
57
|
+
RejectedExecutionHandler custom = (r, executor) -> {
|
|
58
|
+
log.warn("Task rejected, queue full: {}", executor.getQueue().size());
|
|
59
|
+
// Maybe save to database for later processing
|
|
60
|
+
taskRepository.save(r);
|
|
61
|
+
};
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Graceful Shutdown
|
|
65
|
+
|
|
66
|
+
```java
|
|
67
|
+
public void shutdown(ExecutorService executor) {
|
|
68
|
+
executor.shutdown(); // No new tasks accepted
|
|
69
|
+
|
|
70
|
+
try {
|
|
71
|
+
// Wait for existing tasks to complete
|
|
72
|
+
if (!executor.awaitTermination(30, TimeUnit.SECONDS)) {
|
|
73
|
+
log.warn("Executor did not terminate in time, forcing shutdown");
|
|
74
|
+
List<Runnable> pending = executor.shutdownNow();
|
|
75
|
+
log.warn("{} tasks were cancelled", pending.size());
|
|
76
|
+
|
|
77
|
+
// Wait again for tasks to respond to interrupt
|
|
78
|
+
if (!executor.awaitTermination(10, TimeUnit.SECONDS)) {
|
|
79
|
+
log.error("Executor did not terminate");
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
} catch (InterruptedException e) {
|
|
83
|
+
executor.shutdownNow();
|
|
84
|
+
Thread.currentThread().interrupt();
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// With lifecycle hook (Spring)
|
|
89
|
+
@PreDestroy
|
|
90
|
+
public void cleanup() {
|
|
91
|
+
shutdown(executor);
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Scheduled Executor
|
|
96
|
+
|
|
97
|
+
```java
|
|
98
|
+
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);
|
|
99
|
+
|
|
100
|
+
// One-time delay
|
|
101
|
+
scheduler.schedule(() -> sendReminder(), 1, TimeUnit.HOURS);
|
|
102
|
+
|
|
103
|
+
// Fixed rate (starts every N seconds, regardless of duration)
|
|
104
|
+
scheduler.scheduleAtFixedRate(
|
|
105
|
+
() -> collectMetrics(),
|
|
106
|
+
0, // initial delay
|
|
107
|
+
30, TimeUnit.SECONDS // period
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
// Fixed delay (N seconds after previous completion)
|
|
111
|
+
scheduler.scheduleWithFixedDelay(
|
|
112
|
+
() -> processQueue(),
|
|
113
|
+
0, // initial delay
|
|
114
|
+
5, TimeUnit.SECONDS // delay between end and next start
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
// Handle exceptions in scheduled tasks
|
|
118
|
+
scheduler.scheduleAtFixedRate(() -> {
|
|
119
|
+
try {
|
|
120
|
+
riskyOperation();
|
|
121
|
+
} catch (Exception e) {
|
|
122
|
+
log.error("Scheduled task failed", e);
|
|
123
|
+
// Don't rethrow - task would stop repeating
|
|
124
|
+
}
|
|
125
|
+
}, 0, 1, TimeUnit.MINUTES);
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Fork/Join Pool
|
|
129
|
+
|
|
130
|
+
```java
|
|
131
|
+
// For recursive divide-and-conquer tasks
|
|
132
|
+
ForkJoinPool forkJoinPool = new ForkJoinPool(
|
|
133
|
+
Runtime.getRuntime().availableProcessors()
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
class SumTask extends RecursiveTask<Long> {
|
|
137
|
+
private final long[] array;
|
|
138
|
+
private final int start, end;
|
|
139
|
+
private static final int THRESHOLD = 10_000;
|
|
140
|
+
|
|
141
|
+
@Override
|
|
142
|
+
protected Long compute() {
|
|
143
|
+
if (end - start <= THRESHOLD) {
|
|
144
|
+
return computeDirectly();
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
int mid = (start + end) / 2;
|
|
148
|
+
SumTask left = new SumTask(array, start, mid);
|
|
149
|
+
SumTask right = new SumTask(array, mid, end);
|
|
150
|
+
|
|
151
|
+
left.fork(); // async
|
|
152
|
+
long rightResult = right.compute(); // sync
|
|
153
|
+
long leftResult = left.join(); // wait
|
|
154
|
+
|
|
155
|
+
return leftResult + rightResult;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
Long result = forkJoinPool.invoke(new SumTask(array, 0, array.length));
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## Monitoring
|
|
163
|
+
|
|
164
|
+
```java
|
|
165
|
+
// Get metrics from ThreadPoolExecutor
|
|
166
|
+
ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(4);
|
|
167
|
+
|
|
168
|
+
int activeCount = executor.getActiveCount();
|
|
169
|
+
long completedTasks = executor.getCompletedTaskCount();
|
|
170
|
+
int queueSize = executor.getQueue().size();
|
|
171
|
+
int poolSize = executor.getPoolSize();
|
|
172
|
+
|
|
173
|
+
// Expose via metrics library
|
|
174
|
+
meterRegistry.gauge("executor.active", executor, ThreadPoolExecutor::getActiveCount);
|
|
175
|
+
meterRegistry.gauge("executor.queue.size", executor, e -> e.getQueue().size());
|
|
176
|
+
```
|