如何在Java中实现线程间安全通信

来源:Golang编程网作者:天马头衔:网络博主
导读:本期聚焦于小伙伴创作的《如何在Java中实现线程间安全通信》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《如何在Java中实现线程间安全通信》有用,将其分享出去将是对创作者最好的鼓励。

在Java多线程开发中,多个线程往往需要协作完成同一个任务,这就需要线程之间进行通信,同时要保证通信过程的安全性,避免出现数据竞争、脏读等问题。Java提供了多种成熟的机制来实现线程间的安全通信,下面逐一介绍。

如何在Java中实现线程间安全通信

一、使用volatile关键字实现轻量级通信

volatile是Java提供的最轻量级的线程间通信方式,它可以保证变量的可见性,即一个线程修改了volatile修饰的变量,其他线程能立即感知到这个修改。

volatile适合用来传递简单的状态标记,比如控制线程的启动和停止。下面是一个示例:

public class VolatileCommunicationDemo {
    // 用volatile修饰状态变量,保证可见性
    private static volatile boolean isStop = false;

    public static void main(String[] args) throws InterruptedException {
        // 工作线程,循环检查isStop状态
        Thread workerThread = new Thread(() -> {
            System.out.println("工作线程启动,开始执行任务");
            while (!isStop) {
                // 模拟任务执行
            }
            System.out.println("工作线程收到停止信号,结束运行");
        });

        workerThread.start();
        // 主线程休眠1秒,让工作线程先运行
        Thread.sleep(1000);
        // 主线程修改isStop状态,工作线程会立即感知
        isStop = true;
        System.out.println("主线程发送停止信号");
    }
}

需要注意的是,volatile只能保证可见性,不能保证原子性,所以如果通信的内容是需要复合操作的数据,不适合单独使用volatile。

二、synchronized配合wait和notify机制

这是Java最传统的线程通信方式,基于对象的内置锁(监视器锁)实现,适合需要线程等待和唤醒的场景,比如生产者消费者模型。

核心方法是Object类的三个方法:wait()notify()notifyAll(),这三个方法必须在synchronized修饰的代码块或方法中调用,否则会抛出IllegalMonitorStateException

  • wait():当前线程释放锁,进入等待状态,直到被其他线程唤醒或者超时
  • notify():唤醒同一个锁对象上等待的一个线程,具体唤醒哪个线程由JVM决定
  • notifyAll():唤醒同一个锁对象上等待的所有线程

下面是一个简单的生产者消费者示例:

import java.util.LinkedList;
import java.util.Queue;

public class WaitNotifyDemo {
    // 共享队列,容量为5
    private static final Queue<Integer> queue = new LinkedList<>();
    private static final int MAX_SIZE = 5;
    // 共享锁对象
    private static final Object lock = new Object();

    // 生产者线程
    static class Producer extends Thread {
        @Override
        public void run() {
            int num = 0;
            while (true) {
                synchronized (lock) {
                    // 队列满了就等待
                    while (queue.size() == MAX_SIZE) {
                        try {
                            System.out.println("队列已满,生产者等待");
                            lock.wait();
                        } catch (InterruptedException e) {
                            Thread.currentThread().interrupt();
                        }
                    }
                    // 生产数据
                    queue.add(num);
                    System.out.println("生产数据:" + num);
                    num++;
                    // 唤醒消费者
                    lock.notifyAll();
                }
                // 模拟生产耗时
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        }
    }

    // 消费者线程
    static class Consumer extends Thread {
        @Override
        public void run() {
            while (true) {
                synchronized (lock) {
                    // 队列空了就等待
                    while (queue.isEmpty()) {
                        try {
                            System.out.println("队列为空,消费者等待");
                            lock.wait();
                        } catch (InterruptedException e) {
                            Thread.currentThread().interrupt();
                        }
                    }
                    // 消费数据
                    int val = queue.poll();
                    System.out.println("消费数据:" + val);
                    // 唤醒生产者
                    lock.notifyAll();
                }
                // 模拟消费耗时
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        }
    }

    public static void main(String[] args) {
        Producer producer = new Producer();
        Consumer consumer = new Consumer();
        producer.start();
        consumer.start();
    }
}

三、Lock配合Condition接口实现精准通信

JDK 1.5之后引入了Lock接口和Condition接口,相比传统的wait和notify机制,Condition可以实现更精准的线程唤醒,比如可以区分唤醒生产者还是消费者,不需要唤醒所有等待线程。

Conditionawait()方法对应wait()signal()对应notify()signalAll()对应notifyAll(),同样需要在获取锁之后调用。

下面是使用Condition改造的生产者消费者示例:

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

public class ConditionDemo {
    private static final Queue<Integer> queue = new LinkedList<>();
    private static final int MAX_SIZE = 5;
    private static final Lock lock = new ReentrantLock();
    // 生产者等待条件
    private static final Condition producerCondition = lock.newCondition();
    // 消费者等待条件
    private static final Condition consumerCondition = lock.newCondition();

    static class Producer extends Thread {
        @Override
        public void run() {
            int num = 0;
            while (true) {
                lock.lock();
                try {
                    while (queue.size() == MAX_SIZE) {
                        System.out.println("队列已满,生产者等待");
                        producerCondition.await();
                    }
                    queue.add(num);
                    System.out.println("生产数据:" + num);
                    num++;
                    // 精准唤醒消费者
                    consumerCondition.signal();
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                } finally {
                    lock.unlock();
                }
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        }
    }

    static class Consumer extends Thread {
        @Override
        public void run() {
            while (true) {
                lock.lock();
                try {
                    while (queue.isEmpty()) {
                        System.out.println("队列为空,消费者等待");
                        consumerCondition.await();
                    }
                    int val = queue.poll();
                    System.out.println("消费数据:" + val);
                    // 精准唤醒生产者
                    producerCondition.signal();
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                } finally {
                    lock.unlock();
                }
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        }
    }

    public static void main(String[] args) {
        new Producer().start();
        new Consumer().start();
    }
}

四、使用并发工具类实现线程通信

Java并发包java.util.concurrent中提供了很多工具类,也可以用来实现线程间的安全通信,常见的有以下几类:

1. CountDownLatch

适合一个线程等待多个线程完成任务的场景,比如主线程等待所有子线程执行完再继续。

import java.util.concurrent.CountDownLatch;

public class CountDownLatchDemo {
    public static void main(String[] args) throws InterruptedException {
        int threadCount = 3;
        CountDownLatch latch = new CountDownLatch(threadCount);
        for (int i = 0; i < threadCount; i++) {
            int taskId = i;
            new Thread(() -> {
                System.out.println("任务" + taskId + "开始执行");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
                System.out.println("任务" + taskId + "执行完成");
                // 计数减1
                latch.countDown();
            }).start();
        }
        // 主线程等待所有任务完成
        latch.await();
        System.out.println("所有子任务完成,主线程继续执行");
    }
}

2. CyclicBarrier

适合一组线程互相等待,直到所有线程都到达某个屏障点再继续执行的场景。

import java.util.concurrent.CyclicBarrier;

public class CyclicBarrierDemo {
    public static void main(String[] args) {
        int threadCount = 3;
        CyclicBarrier barrier = new CyclicBarrier(threadCount, () -> {
            System.out.println("所有线程都到达屏障,开始下一步操作");
        });
        for (int i = 0; i < threadCount; i++) {
            int threadId = i;
            new Thread(() -> {
                System.out.println("线程" + threadId + "执行第一阶段任务");
                try {
                    Thread.sleep(1000);
                    System.out.println("线程" + threadId + "到达屏障");
                    barrier.await();
                } catch (Exception e) {
                    Thread.currentThread().interrupt();
                }
                System.out.println("线程" + threadId + "执行第二阶段任务");
            }).start();
        }
    }
}

五、不同方式的适用场景对比

为了帮助开发者选择合适的通信方式,下面整理了不同方式的适用场景对比:

通信方式适用场景优点缺点
volatile传递简单状态标记轻量级,性能高不保证原子性,不适合复杂数据通信
synchronized+wait/notify传统线程协作,生产者消费者等场景无需额外引入依赖,使用简单唤醒不精准,容易唤醒无关线程
Lock+Condition需要精准唤醒特定类型线程的场景唤醒精准,灵活性高需要手动释放锁,使用复杂度稍高
并发工具类特定协作场景(如等待多个任务完成、线程互相等待)封装性好,无需手动处理锁和等待逻辑仅适用于特定场景,通用性稍弱

六、注意事项

在使用线程间通信时,需要注意以下几点:

  • 调用wait()await()方法时,一定要用while循环检查条件,而不是if,因为线程被唤醒后条件可能已经不满足了,避免虚假唤醒问题
  • 使用Lock时,一定要在finally块中释放锁,避免锁泄漏
  • 不要随意使用notifyAll()或者signalAll(),如果可以精准唤醒,优先选择精准唤醒,减少不必要的线程上下文切换
  • 共享数据的操作要保证原子性,必要时结合Atomic类或者锁来保证

Java线程安全线程通信volatilewait_notify修改时间:2026-07-13 08:06:41

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。