代码优化:CompletableFuture + countDownLatch + Vector

需求:根据查询出来的一批合同编号,查询每个合同对应流程节点的最后审批人,最后导出合同编号及其对应的最后审批人。

本次代码优化总共分为以下四步骤:

  1. 遍历查询数据库
  2. 使用CompletableFuture将串行改为并行
  3. 简化代码,并引出线程安全问题
  4. CompletableFuture + countDownLatch + Vector

在这里插入图片描述
最终版代码如下:

public List<ContractMan> selectContractMan() throws InterruptedException{
   
   
    CountDownLatch countDownLatch = new CountDownLatch(5);
    List<String> contractNumers= zhbExcelMapper.selectContractNumer();
    Vector<ContractMan> man=new Vector<>();

    List<List<String>> lists = averageAssign(contractNumers, 5);

    for(int i=0
以下是针对 **Java并发编程领域20道高频面试题** 的标准答案详解,内容符合中高级开发工程师在真实面试中的回答规范:逻辑清晰、原理深入、关键词突出、语言专业。每道题均包含 **核心概念解释 + 实现机制 + 使用场景 + 注意事项**,便于应试者全面掌握。 --- ### 1. 线程和进程的区别? | 区别点 | 进程(Process) | 线程(Thread) | |--------|------------------|----------------| | 定义 | 操作系统资源分配的基本单位 | CPU调度的基本单位 | | 内存空间 | 拥有独立的地址空间(堆、栈、代码段等) | 共享所属进程的内存空间 | | 资源开销 | 创建/销毁开销大,资源隔离性强 | 开销小,轻量级执行单元 | | 通信方式 | 需要IPC(管道、消息队列、共享内存等) | 可直接通过共享变量通信 | | 独立性 | 一个进程崩溃不影响其他进程 | 一个线程崩溃可能导致整个进程终止 | | 数量限制 | 较少 | 单个进程可创建数百甚至上千线程 | > ✅ 总结:线程是“轻量级进程”,多个线程共享进程资源,提高了并发效率,但也带来线程安全问题。 --- ### 2. Java中如何创建线程? Java中有四种常见方式创建线程: #### (1)继承 `Thread` 类 ```java class MyThread extends Thread { public void run() { System.out.println("通过继承Thread运行"); } } // 使用 new MyThread().start(); ``` #### (2)实现 `Runnable` 接口(推荐) ```java class Task implements Runnable { public void run() { System.out.println("通过Runnable运行"); } } // 使用 new Thread(new Task()).start(); ``` #### (3)实现 `Callable` 接口 + `FutureTask` ```java class CallTask implements Callable<String> { public String call() throws Exception { return "任务完成"; } } // 使用 FutureTask<String> ft = new FutureTask<>(new CallTask()); new Thread(ft).start(); String result = ft.get(); // 获取返回值 ``` #### (4)使用线程池(最常用) ```java ExecutorService pool = Executors.newFixedThreadPool(4); pool.submit(() -> System.out.println("线程池执行任务")); ``` > ✅ 推荐使用 Runnable 或线程池方式,避免类单继承局限,解耦任务与线程。 --- ### 3. Java线程的状态有哪些? Java中 `Thread.State` 枚举定义了6种状态: | 状态 | 说明 | |------|------| | `NEW` | 线程刚创建,未调用 start() | | `RUNNABLE` | 正在JVM中运行(可能正在运行或等待CPU) | | `BLOCKED` | 等待获取监视器锁(进入synchronized块时) | | `WAITING` | 调用 `wait()`、`join()`、`LockSupport.park()` 进入无限等待 | | `TIMED_WAITING` | 调用 `sleep(long)`、`wait(timeout)`、`join(timeout)` 等有限时等待 | | `TERMINATED` | 线程执行完毕或异常退出 | > 🔁 状态转换图: - `NEW → RUNNABLE`:调用 `start()` - `RUNNABLE ↔ BLOCKED`:竞争锁失败/释放锁 - `RUNNABLE → WAITING/TIMED_WAITING`:调用阻塞方法 - `WAITING → RUNNABLE`:被 `notify()` 唤醒或 `join()` 结束 --- ### 4. 什么是线程安全? **线程安全**指多线程环境下,某个类的行为在并发访问时仍能保持正确性(即结果一致、无数据污染)。 > 核心目标:保证 **原子性、可见性、有序性** #### 常见线程不安全场景: - 多线程同时修改共享变量(如 i++) - 使用非线程安全集合(如 ArrayList、HashMap) #### 如何实现线程安全? - 使用 `synchronized` 或 `ReentrantLock` 加锁 - 使用 `volatile` 保证可见性 - 使用 `AtomicInteger` 等原子类 - 使用线程安全容器(如 ConcurrentHashMap) - 设计不可变对象(Immutable) > ✅ 示例:`i++` 不是原子操作(读→改→写),需加锁或使用 `AtomicInteger.incrementAndGet()` --- ### 5. synchronized关键字的作用? `synchronized` 是Java内置的互斥同步机制,用于保证同一时刻只有一个线程可以执行某段代码。 #### 三种用法: - **修饰实例方法**:锁当前对象实例(this) - **修饰静态方法**:锁该类的Class对象(`.class`) - **修饰代码块**:指定锁对象(如 `synchronized(obj)`) ```java public synchronized void method() { ... } // 锁 this public static synchronized void staticMethod() { ... } // 锁 Class synchronized (lock) { ... } // 自定义锁对象 ``` #### 底层原理: - JVM基于 **Monitor(监视器锁)** 实现 - 每个对象都有一个 Monitor,持有者才能进入临界区 - 支持可重入、不可中断 > ✅ 特性:自动获取/释放锁、防止重入、支持 wait/notify 机制 > ⚠️ 缺点:粒度粗、无法超时、不支持公平锁(默认非公平) --- ### 6. volatile关键字的作用? `volatile` 是一种轻量级同步机制,用于修饰变量,确保其: 1. **可见性**:一个线程修改后,其他线程立即可见 2. **禁止指令重排序**:防止编译器/CPU优化导致顺序错乱 > ❗注意:**不保证原子性**!如 `volatile int i; i++` 仍线程不安全 #### 应用场景: - 状态标志位(如 `volatile boolean running = true;`) - 双重检查单例中的 instance 字段 - 作为轻量级“信号”通知机制 ```java private volatile static Singleton instance; ``` > 🔍 原理:写操作刷新到主内存,读操作从主内存加载;通过内存屏障(Memory Barrier)禁止重排 --- ### 7. Java中线程池的种类有哪些? JDK 提供 `Executors` 工厂类创建常见线程池: | 线程池类型 | 描述 | 适用场景 | |-----------|------|----------| | `newFixedThreadPool(int nThreads)` | 固定大小线程池,核心=最大线程数 | 负载较重但可控的任务 | | `newCachedThreadPool()` | 缓存线程池,空闲线程存活60秒,按需创建 | 短期异步任务多、负载波动大 | | `newSingleThreadExecutor()` | 单线程池,保证顺序执行 | 需要串行处理的任务 | | `newScheduledThreadPool(int corePoolSize)` | 支持定时/周期性任务 | 定时任务调度(替代Timer) | | `newWorkStealingPool()`(JDK8+) | 使用ForkJoinPool,工作窃取算法 | 并行计算密集型任务 | > ⚠️ 生产环境建议:**手动创建 ThreadPoolExecutor**,避免OOM风险(如CachedPool无限扩张) --- ### 8. Java中ThreadLocal的原理? `ThreadLocal` 提供线程本地变量副本,每个线程对该变量有独立的拷贝,避免共享冲突。 ```java private static ThreadLocal<Integer> threadId = new ThreadLocal<Integer>() { @Override protected Integer initialValue() { return Counter.next(); } }; ``` #### 核心原理: - 每个 `Thread` 对象内部维护一个 `ThreadLocalMap`(键为ThreadLocal,值为变量副本) - `get()/set()` 操作基于当前线程的 map 进行读写 #### 内存泄漏问题: - Key 是弱引用(WeakReference),防止内存泄漏 - 但 Value 是强引用,若线程长期运行且未调用 `remove()`,会导致内存泄漏 > ✅ 最佳实践:每次使用完务必调用 `threadLocal.remove();` > 🎯 典型应用:数据库连接、Session管理、用户上下文传递(如 MDC 日志追踪) --- ### 9. 什么是死锁?如何避免? **死锁**是指两个或多个线程因争夺资源而相互等待,导致永久阻塞的现象。 #### 死锁四大必要条件(必须同时满足): 1. **互斥条件**:资源一次只能被一个线程占用 2. **占有并等待**:线程持有资源又申请新资源 3. **不可剥夺**:资源不能被强制释放 4. **循环等待**:形成闭环等待链 #### 示例: ```java // 线程1 synchronized (A) { synchronized (B) { ... } } // 线程2 synchronized (B) { synchronized (A) { ... } } ``` #### 避免策略: - **固定加锁顺序**:所有线程按相同顺序获取锁 - **使用超时锁**:`tryLock(timeout)` - **检测与恢复**:定期检查死锁并中断 - **避免嵌套锁** > ✅ 推荐工具:`jstack` 查看线程堆栈,定位死锁 --- ### 10. Java中如何实现线程间通信? 线程间通信主要通过以下机制实现: #### (1)`wait()` / `notify()` / `notifyAll()` - 必须在 `synchronized` 块中调用 - `wait()`:释放锁并进入等待队列 - `notify()`:唤醒一个等待线程 - `notifyAll()`:唤醒所有等待线程 ```java synchronized (lock) { while (!condition) { lock.wait(); } // 执行任务 lock.notifyAll(); } ``` #### (2)`Condition`(更灵活) ```java Lock lock = new ReentrantLock(); Condition cond = lock.newCondition(); cond.await(); cond.signal(); ``` #### (3)BlockingQueue(生产者-消费者模型) ```java BlockingQueue<String> queue = new LinkedBlockingQueue<>(10); queue.put(item); // 满则阻塞 queue.take(); // 空则阻塞 ``` #### (4)CountDownLatch / CyclicBarrier / Exchanger > ✅ 推荐:优先使用高级并发工具类而非原始 wait/notify --- ### 11. Java中CAS原理? **CAS(Compare and Swap)** 是一种无锁算法,用于实现原子操作。 #### 原理: - 包含三个操作数:内存位置 V、旧值 A、新值 B - 当前值等于预期值 A 时,将 V 更新为 B,否则不做操作 - 整个过程是原子的(由CPU指令支持,如 `cmpxchg`) ```java // AtomicInteger 内部实现 unsafe.compareAndSwapInt(this, valueOffset, expect, update); ``` #### 优点: - 无锁,性能高 - 适用于低竞争场景 #### 缺点: - **ABA问题**:值从A→B→A,CAS成功但实际已变化(可用 `AtomicStampedReference` 解决) - **自旋开销**:高并发下可能持续重试 - **只能保证单个变量的原子性** > ✅ 应用:`AtomicInteger`、`ConcurrentHashMap`、`AQS`底层 --- ### 12. Java中AQS的原理? **AQS(AbstractQueuedSynchronizer)** 是 Java 并发包的核心框架,用于构建锁和同步器(如 ReentrantLock、Semaphore、CountDownLatch)。 #### 核心思想: - 使用一个 `volatile int state` 表示同步状态 - 维护一个 **双向FIFO等待队列(CLH变种)** - 子类通过重写 `tryAcquire()` 和 `tryRelease()` 控制获取/释放逻辑 #### 模式: - **独占模式**:ReentrantLock - **共享模式**:Semaphore、CountDownLatch #### 流程简述: 1. 尝试获取锁(tryAcquire) 2. 失败则构造 Node 加入等待队列 3. 自旋尝试获取,直到被前驱节点唤醒 > ✅ 优势:高度抽象、复用性强、支持可中断、超时、条件等待 --- ### 13. ReentrantLock与synchronized的区别? | 特性 | synchronized | ReentrantLock | |------|---------------|----------------| | 实现层级 | JVM内置(Monitor) | JDK层面(AQS) | | 灵活性 | 低(自动加锁/释放) | 高(手动控制 lock/unlock) | | 公平性 | 非公平(默认) | 可设置公平锁 | | 锁获取方式 | 不可中断、不可超时 | 支持 tryLock(timeout)、可中断 | | 条件等待 | 只能使用 wait/notify | 支持多个 Condition | | 多条件 | 不支持 | 支持多个 await/signal | | 性能 | JDK1.6+优化后接近 | 高并发下略优 | > ✅ 推荐:简单同步用 `synchronized`,复杂控制用 `ReentrantLock` --- ### 14. JavaCountDownLatch和CyclicBarrier的区别? | 特性 | CountDownLatch | CyclicBarrier | |------|----------------|----------------| | 计数方向 | 向下减到0 | 到达阈值后重置 | | 是否可重用 | 否(一次性) | 是(可重复使用) | | 触发动作 | 等待方被唤醒 | 所有等待线程继续执行 | | 构造参数 | 计数值 | 计数值 + 可选 Runnable(屏障动作) | | 应用场景 | 主线程等待多个子任务完成 | 多个线程互相等待到达某点再继续 | | 底层实现 | AQS共享模式 | ReentrantLock + Condition | ```java // CountDownLatch:主线程等5个线程完成 CountDownLatch latch = new CountDownLatch(5); latch.countDown(); // 每个线程完成后调用 latch.await(); // 主线程等待 // CyclicBarrier:5个线程互相等待 CyclicBarrier barrier = new CyclicBarrier(5, () -> System.out.println("满员发车")); barrier.await(); // 每个线程到达后等待 ``` --- ### 15. Java中Future和Callable的区别? | 特性 | Callable | Future | |------|---------|--------| | 定义 | 返回结果且可能抛异常的任务接口 | 表示异步计算的结果 | | 方法 | `call()` 返回泛型结果 | `get()` 获取结果、`isDone()` 判断是否完成 | | 关系 | `Callable` 是任务 | `Future` 是任务提交后的返回句柄 | | 使用方式 | 提交给线程池执行 | 通过 `submit(callable)` 返回 `Future<T>` | ```java ExecutorService pool = Executors.newSingleThreadExecutor(); Future<String> future = pool.submit(() -> "Hello"); String result = future.get(); // 阻塞获取结果 ``` > ✅ `Future` 缺点:不能组合、回调难,推荐使用 `CompletableFuture` --- ### 16. JavaCompletableFuture的作用? `CompletableFuture` 是 JDK8 引入的异步编程工具,支持**链式调用、组合、回调、异常处理**。 ```java CompletableFuture.supplyAsync(() -> fetchPrice("item")) .thenApply(price -> price * 0.9) // 打折 .exceptionally(ex -> getDefaultPrice()) .thenAccept(System.out::println); ``` #### 核心能力: - 异步执行:`supplyAsync()` / `runAsync()` - 串行化:`thenApply()` / `thenRun()` - 组合:`thenCombine()` / `allOf()` / `anyOf()` - 回调:`whenComplete()` / `handle()` - 自定义线程池支持 > ✅ 优势:解决“回调地狱”,提升异步代码可读性和维护性 --- ### 17. Java中并发集合类有哪些? JDK 提供多种线程安全集合: | 类型 | 非线程安全 | 线程安全替代方案 | |------|------------|------------------| | List | ArrayList | CopyOnWriteArrayList | | Map | HashMap | ConcurrentHashMap | | Set | HashSet | CopyOnWriteArraySet / Collections.synchronizedSet | | Queue | LinkedList | ArrayBlockingQueue / LinkedBlockingQueue / ConcurrentLinkedQueue | | Deque | —— | ConcurrentLinkedDeque | #### 特点对比: - `ConcurrentHashMap`:分段锁(JDK7)→ CAS + synchronized(JDK8+) - `CopyOnWriteArrayList`:写时复制,适合读多写少 - `BlockingQueue`:支持阻塞插入/移除,用于生产者-消费者 > ✅ 原则:不要使用 `Vector`、`Hashtable`、`Collections.synchronizedXxx()` 在高性能场景 --- ### 18. Java中线程池的核心参数? `ThreadPoolExecutor` 七大核心参数: ```java new ThreadPoolExecutor( corePoolSize, // 核心线程数(常驻) maximumPoolSize, // 最大线程数 keepAliveTime, // 非核心线程空闲存活时间 unit, // 时间单位 workQueue, // 任务队列(BlockingQueue) threadFactory, // 线程创建工厂(可定制名称) handler // 拒绝策略 ); ``` #### 拒绝策略(RejectedExecutionHandler): - `AbortPolicy`(默认):抛出异常 - `CallerRunsPolicy`:由调用者线程执行 - `DiscardPolicy`:静默丢弃 - `DiscardOldestPolicy`:丢弃队列最老任务 > ✅ 设计建议: - IO密集型:线程数 ≈ 2 * CPU核数 - CPU密集型:线程数 ≈ CPU核数 + 1 - 队列选择:有界队列防OOM --- ### 19. Java中Executor框架的使用? **Executor 框架** 是 Java 并发编程的顶层设计,统一任务提交与执行分离。 #### 核心接口: - `Executor`:最基础,仅支持 execute(Runnable) - `ExecutorService`:扩展支持 submit(Callable)、shutdown、Future 返回 - `ScheduledExecutorService`:支持定时/周期任务 - `ThreadPoolExecutor`:具体实现类 ```java ExecutorService pool = new ThreadPoolExecutor( 2, 4, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(10), Executors.defaultThreadFactory(), new ThreadPoolExecutor.CallerRunsPolicy() ); pool.submit(() -> System.out.println("任务执行")); pool.shutdown(); ``` > ✅ 优势:解耦任务与执行策略,支持资源控制、生命周期管理 --- ### 20. Java中并发工具类的使用? 常用并发工具类及其用途: | 工具类 | 用途 | 示例 | |-------|------|------| | `CountDownLatch` | 一个线程等待多个线程完成 | 主线程等N个下载线程结束 | | `CyclicBarrier` | 多个线程互相等待至某点再继续 | 多玩家准备就绪开始游戏 | | `Semaphore` | 控制并发访问数量(信号量) | 数据库连接池限流 | | `Exchanger` | 两个线程交换数据 | 生产者与消费者交换缓冲区 | | `Phaser`(JDK7+) | 更灵活的栅栏,支持动态注册 | 分阶段任务协调 | ```java // Semaphore 示例:限制同时访问资源的线程数 Semaphore sem = new Semaphore(3); sem.acquire(); // 获取许可 try { // 访问资源 } finally { sem.release(); // 释放许可 } ``` > ✅ 优势:简化复杂同步逻辑,提升代码可读性和可靠性 ---
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值