文档首页> Linux命令> Java线程通信详解

Java线程通信详解

发布时间:2025-06-30 00:35       

以下是Java线程通信的全面解析,基于Java 17 LTS版本的最佳实践,涵盖核心机制与实战要点:


一、线程通信的本质

多线程共享进程内存空间,通信即安全地交换数据与协调执行顺序。需解决两大核心问题:

  1. 竞态条件(非原子操作导致数据不一致)
  2. 可见性(线程缓存未及时刷新到主内存)

⚠️ 错误示例:

// 危险操作:多个线程同时修改共享变量
public class Counter {
    private int count = 0;
    public void increment() { count++; } // 非原子操作
}
​

二、8种线程通信机制详解

1. 同步锁(synchronized)

  • 原理:基于JVM内置锁(Monitor),保证代码块互斥执行
  • 适用场景:简单的临界区保护
public class SafeCounter {
    private int count = 0;
    public synchronized void increment() { 
        count++; // 原子操作
    }
}
​

2. wait()/notify() 机制

  • 核心要点:
    • 必须在 synchronized块内调用
    • wait()释放锁并进入等待
    • notify()唤醒单个等待线程
  • 经典生产者-消费者模型:
public class Buffer {
    private Queue<Integer> queue = new LinkedList<>();
    private int capacity = 10;

    public synchronized void produce(int value) throws InterruptedException {
        while (queue.size() == capacity) {
            wait(); // 缓冲区满时等待
        }
        queue.add(value);
        notifyAll(); // 唤醒消费者
    }

    public synchronized int consume() throws InterruptedException {
        while (queue.isEmpty()) {
            wait(); // 缓冲区空时等待
        }
        int value = queue.poll();
        notifyAll(); // 唤醒生产者
        return value;
    }
}
​

3. Lock 框架(java.util.concurrent.locks)

  • 优势:比 synchronized更灵活(可中断、超时、公平锁)
  • ReentrantLock 示例:
private final Lock lock = new ReentrantLock();
private final Condition notFull = lock.newCondition();
private final Condition notEmpty = lock.newCondition();

public void produce(int value) throws InterruptedException {
    lock.lock();
    try {
        while (queue.size() == capacity) {
            notFull.await(); // 指定条件等待
        }
        queue.add(value);
        notEmpty.signal(); // 精准唤醒消费者
    } finally {
        lock.unlock();
    }
}
​

4. 阻塞队列(BlockingQueue)

  • 最佳实践:直接使用线程安全容器
BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(10);

// 生产者
queue.put(1); // 阻塞直到空间可用

// 消费者
int value = queue.take(); // 阻塞直到元素可用
​

5. 信号量(Semaphore)

  • 场景:控制并发资源数量
Semaphore semaphore = new Semaphore(5); // 允许5个线程同时访问

void accessResource() throws InterruptedException {
    semaphore.acquire(); // 获取许可
    try {
        // 操作共享资源
    } finally {
        semaphore.release(); // 释放许可
    }
}
​

6. 栅栏(CyclicBarrier)

  • 场景:多阶段任务同步
CyclicBarrier barrier = new CyclicBarrier(3, () -> 
    System.out.println("所有线程到达屏障点"));

// 工作线程
public void run() {
    // 阶段1工作
    barrier.await(); // 等待其他线程
    // 阶段2工作
}
​

7. 倒计时锁(CountDownLatch)

  • 一次性场景:主线程等待所有子线程完成
CountDownLatch latch = new CountDownLatch(3);

// 工作线程
public void run() {
    // ... 执行任务
    latch.countDown(); // 计数器减1
}

// 主线程
latch.await(); // 阻塞直到计数器归零
​

8. 异步结果(Future & CompletableFuture)

  • Java 8+ 推荐:链式异步编程
CompletableFuture.supplyAsync(() -> fetchData())
    .thenApply(data -> processData(data))
    .thenAccept(result -> saveResult(result))
    .exceptionally(ex -> handleError(ex));
​

三、内存可见性解决方案

1. volatile 关键字

  • 作用:保证变量的可见性(不保证原子性)
  • 适用场景:单写多读状态标志
private volatile boolean running = true;

public void stop() { running = false; } // 写操作

public void run() { 
    while (running) { /* 读操作 */ } 
}
​

2. Happens-Before 原则

JVM规定的内存可见性保证(如锁释放先于后续获取)


四、通信机制对比表

机制 特点 适用场景
synchronized JVM内置,简单可靠 简单的临界区保护