尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Java开发实战:集合框架、并发编程与JVM调优

Java开发实战:集合框架、并发编程与JVM调优 1. Java笔记5概述作为Java系列笔记的第五篇这篇文章将延续前四篇的实用风格重点聚焦Java开发中的核心知识点和实战技巧。不同于入门教程的泛泛而谈本笔记更倾向于解决实际开发中遇到的典型问题特别适合已经掌握Java基础但需要提升实战能力的开发者。在多年的Java开发实践中我发现很多中级开发者容易在集合框架、并发编程和JVM调优这几个关键领域遇到瓶颈。本文将围绕这些痛点通过代码示例和原理分析帮助读者建立系统化的知识体系。2. 集合框架深度解析2.1 HashMap底层实现原理HashMap是Java集合框架中最常用的数据结构之一但很多开发者对其实现机制理解不够深入。JDK8之后HashMap的实现有了重大改进// HashMap在JDK8中的节点定义 static class NodeK,V implements Map.EntryK,V { final int hash; final K key; V value; NodeK,V next; // ... }当链表长度超过8时会转换为红黑树结构这个设计将最坏情况下的时间复杂度从O(n)降低到O(log n)。但要注意这种转换只有在数组长度达到64时才会真正执行。实际开发中建议在创建HashMap时就预估好初始容量避免频繁扩容。计算公式为预期元素数量 / 负载因子(默认0.75) 1。2.2 ConcurrentHashMap的并发策略与Hashtable的全表锁不同ConcurrentHashMap采用了更精细的锁机制JDK7使用分段锁(Segment)JDK8改为CASsynchronized锁单个桶size()方法在JDK8中改为估计值避免全局锁// JDK8中的putVal方法片段 final V putVal(K key, V value, boolean onlyIfAbsent) { if (key null || value null) throw new NullPointerException(); int hash spread(key.hashCode()); int binCount 0; for (NodeK,V[] tab table;;) { // ...使用CAS操作保证线程安全 } }3. 并发编程实战技巧3.1 ThreadLocal的内存泄漏问题ThreadLocal是解决线程安全问题的利器但使用不当会导致内存泄漏public class ThreadLocalDemo { private static final ThreadLocalSimpleDateFormat dateFormatHolder ThreadLocal.withInitial(() - new SimpleDateFormat(yyyy-MM-dd)); // 使用后必须调用remove() public static void cleanup() { dateFormatHolder.remove(); } }内存泄漏的根本原因是ThreadLocalMap的Entry继承自WeakReference但只有key是弱引用value仍然是强引用。当线程池复用线程时value会一直存在。3.2 CompletableFuture的组合操作Java8引入的CompletableFuture大大简化了异步编程CompletableFuture.supplyAsync(() - fetchOrderFromDB(orderId)) .thenApply(order - calculateDiscount(order)) .thenCombine(fetchInventoryAsync(), (order, inventory) - checkStock(order, inventory)) .exceptionally(ex - handleError(ex)) .thenAccept(this::sendNotification);关键点supplyAsync用于启动异步任务thenApply处理结果转换thenCombine合并多个Futureexceptionally处理异常所有回调方法都返回新的CompletableFuture4. JVM性能调优4.1 垃圾收集器选择策略不同场景下的GC选择建议场景推荐GC组合参数示例适用版本Web服务Parallel Scavenge Parallel Old-XX:UseParallelGCJava 8低延迟系统G1-XX:UseG1GCJava 9大内存系统ZGC-XX:UseZGCJava 15云原生Shenandoah-XX:UseShenandoahGCJava 124.2 内存泄漏排查步骤使用jps获取Java进程IDjmap -histo:live 查看对象分布jstat -gcutil 1000 观察GC情况如果怀疑堆内存泄漏使用jmap -dump:formatb,fileheap.hprof 导出堆转储用MAT或VisualVM分析dump文件5. 开发中的常见陷阱5.1 BigDecimal的等值比较// 错误做法 if (bigDecimal1.equals(bigDecimal2)) { ... } // 正确做法 if (bigDecimal1.compareTo(bigDecimal2) 0) { ... }equals()方法会同时比较值和精度(scale)而compareTo()只比较数值大小。5.2 Lambda表达式中的变量捕获ListString names Arrays.asList(Alice, Bob); String prefix Mr. ; names.forEach(name - System.out.println(prefix name)); // 正确 // 错误在lambda中修改捕获的变量 names.forEach(name - { prefix Ms. ; // 编译错误 System.out.println(prefix name); });lambda表达式只能捕获final或等效final的局部变量。6. 工具链推荐6.1 诊断工具Arthas阿里开源的Java诊断工具实时查看方法调用参数/返回值监控方法执行耗时动态修改日志级别# 查看最繁忙的线程 thread -n 3JProfiler商业性能分析工具内存分配热点分析线程状态监控CPU使用率采样6.2 代码质量工具SpotBugs静态代码分析检测潜在的空指针异常发现资源未关闭问题识别低效的集合用法JaCoCo代码覆盖率生成单元测试覆盖率报告与CI/CD集成支持分支覆盖率统计7. 新版本特性实践7.1 Java17中的密封类public sealed class Shape permits Circle, Square, Rectangle { ... } public final class Circle extends Shape { ... } public final class Square extends Shape { ... } public non-sealed class Rectangle extends Shape { ... }密封类通过permits子句明确指定允许的子类增强了代码的可维护性。7.2 记录类(Record)的使用public record User(Long id, String name, Integer age) { // 编译器自动生成构造方法、equals、hashCode等 } // 使用示例 User user new User(1L, Alice, 30); System.out.println(user.name()); // 自动生成访问器记录类简化了纯数据载体的定义适合DTO、VO等场景。8. 性能优化实战8.1 字符串拼接优化// 低效做法产生多个中间字符串 String result ; for (String str : strings) { result str; } // 高效做法 StringBuilder builder new StringBuilder(); for (String str : strings) { builder.append(str); } String result builder.toString(); // Java8更简洁的写法 String result String.join(, strings);8.2 避免自动装箱开销// 不好的写法产生大量Integer对象 Integer sum 0; for (int i 0; i 1000000; i) { sum i; // 自动装箱 } // 优化后 int sum 0; for (int i 0; i 1000000; i) { sum i; // 使用基本类型 }9. 设计模式应用实例9.1 策略模式在支付系统中的应用public interface PaymentStrategy { void pay(BigDecimal amount); } public class AlipayStrategy implements PaymentStrategy { ... } public class WechatPayStrategy implements PaymentStrategy { ... } public class BankTransferStrategy implements PaymentStrategy { ... } public class PaymentService { private PaymentStrategy strategy; public void setStrategy(PaymentStrategy strategy) { this.strategy strategy; } public void processPayment(BigDecimal amount) { strategy.pay(amount); } }9.2 观察者模式实现事件总线public class EventBus { private final MapClass?, ListConsumer? listeners new ConcurrentHashMap(); public T void subscribe(ClassT eventType, ConsumerT listener) { listeners.computeIfAbsent(eventType, k - new CopyOnWriteArrayList()) .add(listener); } public T void publish(T event) { ListConsumer? eventListeners listeners.get(event.getClass()); if (eventListeners ! null) { eventListeners.forEach(listener - { SuppressWarnings(unchecked) ConsumerT consumer (ConsumerT) listener; consumer.accept(event); }); } } }10. 测试相关技巧10.1 参数化测试示例ParameterizedTest CsvSource({ 1, 2, 3, 5, 5, 10, 10, -5, 5 }) void testAddition(int a, int b, int expected) { assertEquals(expected, a b); }10.2 使用Mockito进行行为验证Test void testOrderService() { // 创建mock对象 PaymentGateway gateway mock(PaymentGateway.class); OrderService service new OrderService(gateway); // 设置桩方法 when(gateway.processPayment(any())).thenReturn(true); // 执行测试 boolean result service.placeOrder(new Order()); // 验证行为 assertTrue(result); verify(gateway, times(1)).processPayment(any()); }11. 编码规范建议异常处理原则捕获具体异常而非Exception不要吞掉异常至少记录日志使用try-with-resources管理资源// 好的做法 try (InputStream is new FileInputStream(file.txt)) { // 使用资源 } catch (FileNotFoundException e) { log.error(File not found, e); throw new BusinessException(File not found, e); }日志记录规范使用SLF4J门面合理选择日志级别DEBUG用于调试INFO记录重要事件避免字符串拼接使用参数化日志// 推荐写法 log.debug(User {} logged in at {}, userId, LocalDateTime.now()); // 不推荐即使日志级别高于DEBUG也会执行字符串拼接 log.debug(User userId logged in at LocalDateTime.now());12. 常见面试问题解析12.1 JVM内存模型程序计数器线程私有记录当前线程执行的字节码行号虚拟机栈线程私有存储栈帧局部变量表、操作数栈等本地方法栈为Native方法服务堆所有线程共享存放对象实例方法区存储类信息、常量、静态变量等12.2 volatile关键字volatile保证了两点可见性写操作会立即刷新到主内存禁止指令重排序通过内存屏障实现但不保证原子性比如count这种操作仍需使用AtomicInteger。13. 微服务相关技术13.1 Spring Cloud熔断器配置Configuration public class HystrixConfig { Bean public HystrixCommand.Setter config() { return HystrixCommand.Setter .withGroupKey(HystrixCommandGroupKey.Factory.asKey(OrderService)) .andCommandPropertiesDefaults(HystrixCommandProperties.Setter() .withExecutionTimeoutInMilliseconds(3000) .withCircuitBreakerErrorThresholdPercentage(50) .withCircuitBreakerSleepWindowInMilliseconds(5000)); } }13.2 Feign客户端最佳实践FeignClient(name inventory-service, configuration FeignConfig.class, fallback InventoryServiceFallback.class) public interface InventoryServiceClient { GetMapping(/api/inventory/{sku}) InventoryInfo getInventory(PathVariable(sku) String sku); PostMapping(/api/inventory/deduct) ResultBoolean deductStock(RequestBody StockDeduction deduction); }14. 数据库访问优化14.1 MyBatis批量插入Insert(script insert into user (name, age) values foreach collectionlist itemitem separator, (#{item.name}, #{item.age}) /foreach /script) void batchInsert(Param(list) ListUser users);14.2 JPA查询优化技巧使用EntityGraph解决N1问题对分页查询实现count查询优化使用投影(DTO)减少数据传输量public interface UserRepository extends JpaRepositoryUser, Long { EntityGraph(attributePaths roles) ListUser findAllWithRolesBy(); Query(select new com.example.UserDto(u.id, u.name) from User u) ListUserDto findAllUserDtos(); }15. 安全编程要点15.1 密码存储规范public class PasswordUtil { private static final int ITERATIONS 10000; private static final int KEY_LENGTH 256; private static final SecureRandom RANDOM new SecureRandom(); public static String hashPassword(String password) { byte[] salt new byte[16]; RANDOM.nextBytes(salt); PBEKeySpec spec new PBEKeySpec( password.toCharArray(), salt, ITERATIONS, KEY_LENGTH ); // ...使用PBKDF2WithHmacSHA256算法 } }15.2 SQL注入防护永远不要拼接SQL语句使用预编译语句(PreparedStatement)MyBatis中使用#{}而非${}对用户输入进行白名单验证// 安全做法 Query(SELECT u FROM User u WHERE u.username :username) User findByUsername(Param(username) String username); // 危险做法不要这样做 Query(SELECT u FROM User u WHERE u.username #{username} ) User findByUsernameUnsafe(Param(username) String username);16. 响应式编程基础16.1 Reactor核心概念Mono0-1个结果的异步序列Flux0-N个结果的异步序列操作符map同步转换flatMap异步转换filter过滤元素zip组合多个流Flux.interval(Duration.ofMillis(100)) .take(10) .map(i - item- i) .filter(s - !s.endsWith(5)) .subscribe(System.out::println);16.2 WebFlux控制器示例RestController RequestMapping(/api/users) public class UserController { GetMapping(/{id}) public MonoUser getUser(PathVariable String id) { return userRepository.findById(id); } GetMapping public FluxUser listUsers(RequestParam(required false) String name) { return name null ? userRepository.findAll() : userRepository.findByNameContaining(name); } }17. 容器化部署实践17.1 Dockerfile优化# 多阶段构建减小镜像体积 FROM maven:3.8.4-openjdk-17 as builder WORKDIR /app COPY pom.xml . RUN mvn dependency:go-offline COPY src/ /app/src/ RUN mvn package FROM openjdk:17-jdk-slim WORKDIR /app COPY --frombuilder /app/target/myapp.jar . EXPOSE 8080 ENTRYPOINT [java, -jar, myapp.jar]17.2 Kubernetes部署文件apiVersion: apps/v1 kind: Deployment metadata: name: order-service spec: replicas: 3 selector: matchLabels: app: order-service template: metadata: labels: app: order-service spec: containers: - name: order-service image: myrepo/order-service:1.0.0 ports: - containerPort: 8080 resources: limits: memory: 512Mi cpu: 500m livenessProbe: httpGet: path: /actuator/health port: 8080 initialDelaySeconds: 30 periodSeconds: 1018. 监控与诊断18.1 Micrometer指标收集Bean public MeterRegistryCustomizerMeterRegistry metricsCommonTags() { return registry - registry.config().commonTags( application, order-service, region, System.getenv(REGION) ); } RestController public class OrderController { private final Counter orderCounter; public OrderController(MeterRegistry registry) { this.orderCounter registry.counter(orders.created); } PostMapping(/orders) public Order createOrder() { orderCounter.increment(); // ... } }18.2 分布式追踪配置# application.yml spring: sleuth: sampler: probability: 1.0 zipkin: base-url: http://zipkin-server:9411 sender: type: web19. 代码重构技巧19.1 使用Optional避免NPE// 传统写法 public String getUserName(User user) { if (user ! null user.getName() ! null) { return user.getName(); } return default; } // 使用Optional public String getUserName(User user) { return Optional.ofNullable(user) .map(User::getName) .orElse(default); }19.2 用记录类简化DTO// 旧写法 public class UserDto { private Long id; private String name; // getters/setters/equals/hashCode/toString... } // 新写法 public record UserDto(Long id, String name) {}20. 持续集成实践20.1 GitHub Actions配置示例name: Java CI on: [push, pull_request] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkoutv2 - name: Set up JDK 17 uses: actions/setup-javav2 with: java-version: 17 distribution: temurin - name: Build with Maven run: mvn -B package --file pom.xml - name: Run tests run: mvn test - name: Upload coverage uses: codecov/codecov-actionv120.2 SonarQube质量门禁# sonar-project.properties sonar.projectKeymy-java-project sonar.projectNameMy Java Project sonar.projectVersion1.0 sonar.sourcessrc/main/java sonar.testssrc/test/java sonar.java.binariestarget/classes sonar.junit.reportPathstarget/surefire-reports sonar.jacoco.reportPathstarget/jacoco.exec
返回列表