Java线程之间通信的几种方式详解

 更新时间:2025年03月21日 11:08:37   作者:yymagicer  
在 Java 中,线程之间的通信是通过共享内存模型来实现的,线程通过共享的对象和变量来交换数据,为了确保线程间通信的正确性,Java 提供了一系列机制来实现线程安全、同步和通信,以下是常用的几种线程间通信的方式,以及它们的使用方法和场景,需要的朋友可以参考下

1. 共享变量与同步机制

多个线程可以通过共享对象的变量进行通信,但为了避免数据不一致的问题,必须使用同步机制来控制对共享变量的访问。

  • 使用 synchronized 关键字: synchronized 确保在同一时刻只有一个线程可以执行同步代码块。它可以同步方法或代码块,用于保护共享数据。

    示例:

class Counter {
    private int count = 0;
 
    public synchronized void increment() {
        count++;
    }
 
    public synchronized int getCount() {
        return count;
    }
}
 
public class SyncExample {
    public static void main(String[] args) {
        Counter counter = new Counter();
 
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                counter.increment();
            }
        });
 
        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                counter.increment();
            }
        });
 
        t1.start();
        t2.start();
 
        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
 
        System.out.println("Final count: " + counter.getCount());
    }
}
  • 使用场景:用于保护共享资源,防止多个线程同时读写导致数据不一致的问题。

  • 使用 volatile 关键字: volatile 确保一个线程对变量的修改对其他线程立即可见。它适用于轻量级的变量同步,通常用于标志位控制。

    示例:

class Flag {
    private volatile boolean running = true;
 
    public void stop() {
        running = false;
    }
 
    public boolean isRunning() {
        return running;
    }
}
 
public class VolatileExample {
    public static void main(String[] args) {
        Flag flag = new Flag();
 
        Thread t1 = new Thread(() -> {
            while (flag.isRunning()) {
                System.out.println("Thread is running");
            }
            System.out.println("Thread stopped");
        });
 
        t1.start();
 
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
 
        flag.stop();
    }
}
  • 使用场景:用于控制线程的退出、开关操作等轻量级场景。

2. wait()、notify() 和 notifyAll()

Object 类的这三个方法用于在同步块内实现线程间的协作。线程可以通过 wait() 进入等待状态,直到另一个线程调用 notify() 或 notifyAll() 唤醒它们。

  • wait():当前线程进入等待状态,并释放锁。
  • notify():唤醒一个正在等待同一锁的线程。
  • notifyAll():唤醒所有等待同一锁的线程。

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

class SharedResource {
    private int value;
    private boolean hasValue = false;
 
    public synchronized void produce(int newValue) throws InterruptedException {
        while (hasValue) {
            wait(); // 等待消费者消费
        }
        value = newValue;
        hasValue = true;
        System.out.println("Produced: " + value);
        notify(); // 唤醒消费者
    }
 
    public synchronized void consume() throws InterruptedException {
        while (!hasValue) {
            wait(); // 等待生产者生产
        }
        System.out.println("Consumed: " + value);
        hasValue = false;
        notify(); // 唤醒生产者
    }
}
 
public class ProducerConsumerExample {
    public static void main(String[] args) {
        SharedResource resource = new SharedResource();
 
        Thread producer = new Thread(() -> {
            try {
                for (int i = 0; i < 5; i++) {
                    resource.produce(i);
                    Thread.sleep(100);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
 
        Thread consumer = new Thread(() -> {
            try {
                for (int i = 0; i < 5; i++) {
                    resource.consume();
                    Thread.sleep(200);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
 
        producer.start();
        consumer.start();
    }
}

使用场景:适用于线程间的协调工作,比如生产者-消费者模型,一个线程负责生产资源,另一个线程负责消费资源。

3. Lock 和 Condition 接口

相比 synchronizedLock 提供了更灵活的同步机制,而 Condition 可以替代 wait()notify() 和 notifyAll(),并支持多个等待条件。

  • ReentrantLock:常用于显式锁控制,可以提供公平锁机制(按获取锁的顺序进行调度)。
  • Condition:类似于 Object 的 wait()notify(),可以创建多个 Condition 来进行复杂的线程协调。

示例:

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
 
class BoundedBuffer {
    private final int[] buffer;
    private int count, in, out;
    private final Lock lock = new ReentrantLock();
    private final Condition notFull = lock.newCondition();
    private final Condition notEmpty = lock.newCondition();
 
    public BoundedBuffer(int size) {
        buffer = new int[size];
    }
 
    public void put(int value) throws InterruptedException {
        lock.lock();
        try {
            while (count == buffer.length) {
                notFull.await(); // 等待缓冲区未满
            }
            buffer[in] = value;
            in = (in + 1) % buffer.length;
            count++;
            notEmpty.signal(); // 唤醒消费线程
        } finally {
            lock.unlock();
        }
    }
 
    public int take() throws InterruptedException {
        lock.lock();
        try {
            while (count == 0) {
                notEmpty.await(); // 等待缓冲区非空
            }
            int value = buffer[out];
            out = (out + 1) % buffer.length;
            count--;
            notFull.signal(); // 唤醒生产线程
            return value;
        } finally {
            lock.unlock();
        }
    }
}
 
public class LockConditionExample {
    public static void main(String[] args) {
        BoundedBuffer buffer = new BoundedBuffer(5);
 
        Thread producer = new Thread(() -> {
            try {
                for (int i = 0; i < 10; i++) {
                    buffer.put(i);
                    System.out.println("Produced: " + i);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
 
        Thread consumer = new Thread(() -> {
            try {
                for (int i = 0; i < 10; i++) {
                    int value = buffer.take();
                    System.out.println("Consumed: " + value);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
 
        producer.start();
        consumer.start();
    }
}

使用场景:适用于需要显式锁定、更复杂的条件等待场景,比如多条件同步、多生产者-消费者模型。

4. java.util.concurrent 包的并发工具

Java 提供了 java.util.concurrent 包下的一系列高级并发工具类来简化线程间通信。

  • BlockingQueue:线程安全的队列,常用于生产者-消费者模式。
  • CountDownLatch:允许一个或多个线程等待其他线程完成某些操作。
  • CyclicBarrier:多个线程在某个点上相互等待,直到所有线程到达该点。
  • Semaphore:用于控制对资源的访问许可。
  • Exchanger:用于两个线程之间交换数据。

到此这篇关于Java线程之间通信的几种方式详解的文章就介绍到这了,更多相关Java线程之间通信内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • java时间戳与日期相互转换工具详解

    java时间戳与日期相互转换工具详解

    这篇文章主要为大家详细介绍了java各种时间戳与日期之间相互转换的工具,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-12-12
  • MyBatis-Plus多表关联分页方式

    MyBatis-Plus多表关联分页方式

    文章介绍了使用MyBatis-Plus的QueryWrapper进行多表关联查询的方法,通过select()方法定义需要的字段,将ID字段转化为名称字段,实现一次查询,这种方法简单便捷,但在使用where语句查询子表字段时有一定限制,作者建议建立数据库视图表作为更好的解决方案
    2026-05-05
  • Jmeter的接口测试详细步骤并实现业务闭环

    Jmeter的接口测试详细步骤并实现业务闭环

    这篇文章主要介绍了Jmeter的接口测试详细步骤并实现业务闭环,文章围绕主题展开详细的内容介绍,具有一定的参考价值,需要的小伙伴可以参考一下
    2022-08-08
  • 解决Java J2EE乱码问题的方法

    解决Java J2EE乱码问题的方法

    这篇文章主要为大家详细介绍了解决Java J2EE乱码问题的方法的相关资料,需要的朋友可以参考下
    2016-04-04
  • Java日志API管理最佳实践详解

    Java日志API管理最佳实践详解

    这篇文章主要介绍了Java日志API管理最佳实践详解,记录日志只是有效地利用日志的第一步,更重要的是如何对程序运行时产生的日志进行处理和分析。,需要的朋友可以参考下
    2019-06-06
  • Java深入讲解Object类常用方法的使用

    Java深入讲解Object类常用方法的使用

    前面java继承中说到的Object类是java中一个特殊的类,所有的类都是直接或者间接的继承自Object类,即如果某个类没有使用extends关键字则默认是java.lang.Object类的子类,所以所有的类都可以使用Object类中定义的方法,下面介绍Object类的常用方法
    2022-04-04
  • spring启动后保证创建的对象不被垃圾回收器回收

    spring启动后保证创建的对象不被垃圾回收器回收

    最近看到一个问题是,spring在启动后如何保证创建的对象不被垃圾回收器回收?。所以本文结合jvm的垃圾回收机制和spring中的源代码做出自己的一点猜测。有需要的朋友们可以参考借鉴。
    2016-09-09
  • Java多线程实现之Callable详解

    Java多线程实现之Callable详解

    这篇文章主要介绍了Java多线程实现之Callable详解,Callable是一个接口,用于实现多线程,与实现Runnable类似,但是功能更强大,通过实现Callable接口,我们需要重写call()方法,该方法可以在任务结束后提供一个返回值,需要的朋友可以参考下
    2023-08-08
  • springboot websocket简单入门示例

    springboot websocket简单入门示例

    这篇文章主要介绍了springboot websocket简单入门示例,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-08-08
  • Springboot 全局时间格式化三种方式示例详解

    Springboot 全局时间格式化三种方式示例详解

    时间格式化在项目中使用频率是非常高的,当我们的 API​ 接口返回结果,需要对其中某一个 date​ 字段属性进行特殊的格式化处理,通常会用到 SimpleDateFormat​ 工具处理,这篇文章主要介绍了3 种 Springboot 全局时间格式化方式,需要的朋友可以参考下
    2024-01-01

最新评论