一、共享内存(基础方式:synchronized / 显式锁)

核心原理

多个线程访问同一个内存区域(成员变量、静态变量),通过 synchronized(内置锁)或 Lock(显式锁)保证「同一时间只有一个线程读写共享数据」,避免竞态条件(数据错乱)。这是 Java 最基础的线程通信方式。

实现示例 1:synchronized 实现共享计数器

public class SharedMemoryDemo {
    // 共享变量
    private static int sharedCounter = 0;

    // 同步方法:内置锁保护共享变量
    private static synchronized void increment() {
        sharedCounter++;
    }

    private static synchronized void decrement() {
        sharedCounter--;
    }

    public static void main(String[] args) throws InterruptedException {
        // 线程1:累加
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 100000; i++) {
                increment();
            }
        });

        // 线程2:递减
        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 100000; i++) {
                decrement();
            }
        });

        t1.start();
        t2.start();
        // 等待线程执行完毕
        t1.join();
        t2.join();

        // 无锁会随机出错,加锁后结果必为0
        System.out.println("最终共享变量值:" + sharedCounter);
    }
}

实现示例 2:Lock 显式锁(更灵活)

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class LockSharedMemoryDemo {
    private static int sharedCounter = 0;
    // 显式锁(可重入锁)
    private static final Lock lock = new ReentrantLock();

    private static void increment() {
        lock.lock(); // 获取锁
        try {
            sharedCounter++;
        } finally {
            lock.unlock(); // 必须在finally释放锁,避免死锁
        }
    }

    private static void decrement() {
        lock.lock();
        try {
            sharedCounter--;
        } finally {
            lock.unlock();
        }
    }

    // main方法和示例1一致,此处省略
}

适用场景

  • 简单数据共享(计数器、状态标记:isRunning = true/false);
  • 低频率、短时间的临界区操作。

优缺点

  • ✅ 优点:实现简单、无额外依赖,JVM 原生支持;
  • ❌ 缺点:仅能保证数据安全,无法主动「唤醒 / 等待」线程,需配合其他方式实现复杂协作。

二、wait ()/notify ()/notifyAll ()(基于 Object 监视器)

核心原理

这是 Java 最经典的线程协作方式,依赖 Object 类的内置监视器(必须在 synchronized 块中使用):

  1. wait():释放当前持有的锁,让线程进入「等待队列」,直到被唤醒;
  2. notify():随机唤醒一个等待该对象锁的线程;
  3. notifyAll():唤醒所有等待该对象锁的线程(推荐优先使用,避免线程饿死)。

实现示例:生产者 - 消费者模型

public class WaitNotifyDemo {
    // 共享缓冲区
    private static final Object lock = new Object();
    private static int count = 0; // 缓冲区数据量
    private static final int MAX_CAPACITY = 5; // 缓冲区最大容量

    // 生产者线程:生产数据
    static class Producer implements Runnable {
        @Override
        public void run() {
            while (true) {
                synchronized (lock) {
                    // 缓冲区满,等待消费者消费
                    while (count == MAX_CAPACITY) {
                        try {
                            lock.wait(); // 释放锁,进入等待
                        } catch (InterruptedException e) {
                            Thread.currentThread().interrupt();
                            return;
                        }
                    }
                    // 生产数据
                    count++;
                    System.out.println(Thread.currentThread().getName() + " 生产,当前容量:" + count);
                    lock.notifyAll(); // 唤醒消费者
                }
                // 模拟生产耗时
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        }
    }

    // 消费者线程:消费数据
    static class Consumer implements Runnable {
        @Override
        public void run() {
            while (true) {
                synchronized (lock) {
                    // 缓冲区空,等待生产者生产
                    while (count == 0) {
                        try {
                            lock.wait();
                        } catch (InterruptedException e) {
                            Thread.currentThread().interrupt();
                            return;
                        }
                    }
                    // 消费数据
                    count--;
                    System.out.println(Thread.currentThread().getName() + " 消费,当前容量:" + count);
                    lock.notifyAll(); // 唤醒生产者
                }
                // 模拟消费耗时
                try {
                    Thread.sleep(800);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        }
    }

    public static void main(String[] args) {
        new Thread(new Producer(), "生产者1").start();
        new Thread(new Consumer(), "消费者1").start();
    }
}

关键注意事项

  • 必须在 synchronized 块中调用 wait()/notify(),否则抛 IllegalMonitorStateException
  • wait() 会释放锁,而 sleep() 不会释放锁;
  • 推荐用 while 循环判断条件(而非 if),防止「虚假唤醒」(线程被唤醒后条件仍不满足)。

适用场景

  • 经典的生产者 - 消费者、生产者 - 多个消费者等协作场景;
  • 需要手动控制线程等待 / 唤醒的基础场景。

优缺点

  • ✅ 优点:JVM 原生支持,无需额外依赖;
  • ❌ 缺点:唤醒线程随机(notify)、只能唤醒同一锁的线程、代码嵌套较复杂。

三、Condition(基于 Lock 的灵活协作)

核心原理

ConditionLock 的配套工具(替代 wait()/notify()),通过 Lock.newCondition() 创建,支持「分组唤醒」(精准唤醒指定线程),比 wait/notify 更灵活。

实现示例:精准唤醒生产者 / 消费者

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ConditionDemo {
    private static int count = 0;
    private static final int MAX_CAPACITY = 5;
    private static final Lock lock = new ReentrantLock();
    // 分别创建生产者、消费者的Condition
    private static final Condition producerCondition = lock.newCondition();
    private static final Condition consumerCondition = lock.newCondition();

    static class Producer implements Runnable {
        @Override
        public void run() {
            while (true) {
                lock.lock();
                try {
                    while (count == MAX_CAPACITY) {
                        producerCondition.await(); // 生产者等待
                    }
                    count++;
                    System.out.println(Thread.currentThread().getName() + " 生产,当前容量:" + count);
                    consumerCondition.signalAll(); // 只唤醒消费者
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                } finally {
                    lock.unlock();
                }
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        }
    }

    static class Consumer implements Runnable {
        @Override
        public void run() {
            while (true) {
                lock.lock();
                try {
                    while (count == 0) {
                        consumerCondition.await(); // 消费者等待
                    }
                    count--;
                    System.out.println(Thread.currentThread().getName() + " 消费,当前容量:" + count);
                    producerCondition.signalAll(); // 只唤醒生产者
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                } finally {
                    lock.unlock();
                }
                try {
                    Thread.sleep(800);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        }
    }

    public static void main(String[] args) {
        new Thread(new Producer(), "生产者1").start();
        new Thread(new Consumer(), "消费者1").start();
    }
}

适用场景

  • 需要「精准唤醒」指定类型线程的场景(如多生产者多消费者);
  • 复杂的线程协作(多个条件等待 / 唤醒)。

优缺点

  • ✅ 优点:分组唤醒、灵活可控,支持超时等待;
  • ❌ 缺点:代码稍复杂,需手动管理 Lock 和 Condition。

四、BlockingQueue(阻塞队列:高并发首选)

核心原理

BlockingQueue 是 Java 并发包提供的「线程安全阻塞队列」,内置了 put()(队列满则阻塞)、take()(队列空则阻塞)等方法,无需手动加锁 / 唤醒,是生产者 - 消费者模型的最优解。

实现示例:简化版生产者 - 消费者

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

public class BlockingQueueDemo {
    // 容量为5的阻塞队列
    private static final BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(5);

    static class Producer implements Runnable {
        @Override
        public void run() {
            int i = 0;
            while (true) {
                try {
                    queue.put(i); // 队列满则阻塞
                    System.out.println(Thread.currentThread().getName() + " 生产:" + i);
                    i++;
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        }
    }

    static class Consumer implements Runnable {
        @Override
        public void run() {
            while (true) {
                try {
                    Integer data = queue.take(); // 队列空则阻塞
                    System.out.println(Thread.currentThread().getName() + " 消费:" + data);
                    Thread.sleep(800);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        }
    }

    public static void main(String[] args) {
        new Thread(new Producer(), "生产者1").start();
        new Thread(new Consumer(), "消费者1").start();
    }
}

常用 BlockingQueue 实现

实现类 特点 适用场景
ArrayBlockingQueue 数组实现、有界、公平 / 非公平 固定容量的生产消费
LinkedBlockingQueue 链表实现、默认无界 高吞吐量的生产消费
SynchronousQueue 无存储、直接传递 线程间一对一数据交换

适用场景

  • 高并发的生产者 - 消费者模型;
  • 线程间安全的消息传递(如任务队列)。

优缺点

  • ✅ 优点:无需手动处理锁和唤醒、代码简洁、性能优异;
  • ❌ 缺点:仅适用于队列式数据交换,灵活性稍低。

五、并发工具类(CountDownLatch/CyclicBarrier/Semaphore)

这类工具类并非「直接通信」,而是通过「协调线程执行时机」实现间接通信,是 Java 高级线程协作方式。

1. CountDownLatch(倒计时门闩)

  • 原理:让一个 / 多个线程等待其他线程完成操作后再执行;
  • 示例:主线程等待 3 个工作线程完成任务:
import java.util.concurrent.CountDownLatch;

public class CountDownLatchDemo {
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch latch = new CountDownLatch(3); // 计数3

        // 工作线程
        for (int i = 0; i < 3; i++) {
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + " 执行任务");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
                latch.countDown(); // 计数减1
            }, "工作线程" + i).start();
        }

        latch.await(); // 主线程等待计数为0
        System.out.println("所有工作线程完成,主线程继续执行");
    }
}

2. CyclicBarrier(循环屏障)

  • 原理:让多个线程到达「屏障点」后再一起执行(可循环使用);
  • 适用场景:多线程分批执行任务(如分阶段计算)。

3. Semaphore(信号量)

  • 原理:控制同时访问某个资源的线程数(通过许可数实现);
  • 适用场景:限流(如最多允许 5 个线程访问数据库连接池)。

总结

关键点回顾

  1. 基础通信:共享内存(synchronized/Lock)保证数据安全,但需配合 wait/notify 实现协作;
  2. 经典协作:wait/notify(Object 监视器)是基础,Condition(Lock 配套)更灵活,支持精准唤醒;
  3. 高并发首选:BlockingQueue 无需手动处理锁和唤醒,是生产者 - 消费者模型的最优解;
  4. 高级协作:CountDownLatch/CyclicBarrier/Semaphore 用于协调线程执行时机,实现间接通信。

选型建议

  • 简单数据共享:用 synchronized/Lock;
  • 生产消费模型:优先用 BlockingQueue;
  • 精准线程唤醒:用 Condition;
  • 线程执行时机协调:用 CountDownLatch/CyclicBarrier/Semaphore。
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐