一、什么是JUC(java.util.concurrent)

1.定义

JUC = Java并发工具包,是JDK5引入的一整套高性能并发解决方案。在多线程环境下,安全、高效、可控地操作共享资源。

2.JUC解决了什么问题?

问题传统方式JUC方式
线程安全synchronizedLock / 原子类
线程通信wait / notifyCondition
线程池手写Executor
并发容器VectorConcurrentHashMap
并发协作无CountDownLatch等

二、进程 VS 线程

1.进程

  • 资源分配的最小单位
  • 拥有独立的内存空间、文件描述符、堆、方法区

2.线程

  • CPU调度的最小单位
  • 线程之间共享进程资源
  • 共享:
    (1)堆
    (2)方法区
  • 独享:
    (1)栈
    (2)程序计数器
    进程负责资源,线程负责执行

三、Lock锁

1.synchronized 的问题

  • 只有一把锁
  • 无法中断
  • 无法公平
  • 锁粒度粗
  • 无法尝试加锁

2.Lock接口(核心:ReentrantLock)

2.1 基本使用

Lock lock = new ReentrantLock();

lock.lock();
try {
    // 临界区
} finally {
    lock.unlock();
}

2.2 相比 synchronized 的优势

能力Locksynchronized
是否可中断是否
是否公平锁是否
是否可以尝试加锁tryLock否
是否多条件Condition否

2.3 公平锁 vs 非公平锁

new ReentrantLock(true);  // 公平锁
new ReentrantLock(false); // 非公平锁(默认)
  • 公平锁:按等待时间顺序
  • 非公平锁:吞吐量更高

2.4 Condition(精确唤醒)

Condition condition = lock.newCondition();

condition.await();
condition.signal();

替代wait / notify,支持多个条件队列
PS:synchronized 是 JVM 层锁,Lock 是 JDK 层锁,Lock 更灵活、功能更强,生产推荐。

四、生产者-消费者问题(并发通信核心)

1.经典问题

  • 生产者生产数据
  • 消费者消费数据
  • 中间容器有容量限制

2.使用Lock + Condition实现

class Resource {
    private int count = 0;
    private Lock lock = new ReentrantLock();
    private Condition notEmpty = lock.newCondition();
    private Condition notFull = lock.newCondition();

    public void produce() throws InterruptedException {
        lock.lock();
        try {
            while (count == 1) {
                notFull.await();
            }
            count++;
            notEmpty.signal();
        } finally {
            lock.unlock();
        }
    }

    public void consume() throws InterruptedException {
        lock.lock();
        try {
            while (count == 0) {
                notEmpty.await();
            }
            count--;
            notFull.signal();
        } finally {
            lock.unlock();
        }
    }
}

关键点:while防止虚假唤醒(if会导致虚假唤醒),精准唤醒

五、8锁现象

本质:多个synchronized方法,锁的是谁?

  • 普通方法 -> 锁的是对象
  • static方法 -> 锁的是Class
  • 两者互不影响
  • 不同对象互不影响

synchronized 锁的是对象监视器(Monitor)

六、集合类不安全问题

1.问题示例

List<String> list = new ArrayList<>();
list.add("a"); // 多线程会抛异常

常见异常:

  • ConcurrentModificationException
  • 数据覆盖
  • 数据丢失

2.解决方案对比

方案是否推荐
Vector否
Collections.synchronizedList否
CopyOnWriteArrayList是
List<String> list = new CopyOnWriteArrayList<>();

原理:

  • 写时复制
  • 读无锁
  • 适合:读多写少

3.Map解决方案

ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>();

七、Callable(有返回值的线程)

1.Runnable vs Callable

Runnablecallable
是否有返回值否是
是否有异常否是
配合使用ThreadFutureTask

2.使用方式

Callable<Integer> task = () -> 100;
FutureTask<Integer> future = new FutureTask<>(task);

new Thread(future).start();
Integer result = future.get();

八、JUC常用辅助类

1.CountDownLatch(倒计时)

等所有线程执行完,再执行主线程

CountDownLatch latch = new CountDownLatch(3);
latch.await();

场景:服务启动校验、并行任务聚合

2.CyclicBarrier(循环屏障)

一组线程互相等待

CyclicBarrier barrier = new CyclicBarrier(3);
barrier.await();

场景:多线程分阶段计算

3.Semaphore(信号量)

Semaphore semaphore = new Semaphore(3);

场景:

  • 限流
  • 连接池
  • 线程并发控制

九、读写锁(ReentrantReadWriteLock)

1.核心思想

  • 读读并发
  • 读写互斥
  • 写写互斥
ReadWriteLock rwLock = new ReentrantReadWriteLock();

场景:缓存系统、配置读取

十、阻塞队列(重点)

1.什么是阻塞队列

  • 生产者放满 -> 阻塞
  • 消费者取空 -> 阻塞
    天然实现生产者 - 消费者

2.四组API

方法组行为
add / remove抛异常
offer / poll返回值
put / task阻塞
offee(timeout)超时

3.常见实现

队列场景
ArrayBlockingQueue有界
LinkedBlockingQueue高并发
SynchronousQueue直接交付
DelayQueue延迟任务

线程池底层就是BlockingQueue

十一、应用场景总结

技术实现场景
ReentrantLock复杂并发控制
Condition精准线程通信
ConcurrentHashMap高并发缓存
CountDownLatch启动依赖
Semaphore限流
ReadWriteLock配置/缓存
BlockingQueueMQ、线程池

十二、总结

1.synchronized 和 Lock 区别?

Lock更灵活,支持公平锁、中断、条件队列,推荐Lock

2.为什么要用while而不是if

防止虚假唤醒,确保线程安全

3.ConcurrentHashMap 为什么线程安全?

JDK8 使用 CAS + synchronized + Node 锁,锁粒度更细

4.BlockingQueue 的作用?

解耦生产消费,避免手写 wait/notify

5.读写锁适合什么场景?

读多写少的共享资源,如缓存

6.虚假唤醒

线程在没有满足唤醒条件的情况下,被从 wait / await 状态中唤醒,这种现象就叫虚假唤醒。
注意:不是 notify/ signal 发错了,而是“线程醒了,但条件并不成立”
1.虚假唤醒发生在哪里?

  • Object.wait()
  • Condition.await()
  • LockSupport.park()
Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐