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并发之同步器设计,本文以记录方式并发编程中同步器设计的一些共性特征。并简单介绍了Java中的AQS,需要的朋友可以参考一下文章的详细内容
    2021-10-10
  • java中Optional的使用详细解析

    java中Optional的使用详细解析

    这篇文章主要介绍了java新特性之Optional的详细解析,文中有非常详细的代码示例,对正在学习java的小伙伴们有非常好的帮助,需要的朋友可以参考下
    2021-04-04
  • 聊聊Spring AOP @Before @Around @After等advice的执行顺序

    聊聊Spring AOP @Before @Around @After等advice的执行顺序

    这篇文章主要介绍了聊聊Spring AOP @Before @Around @After等advice的执行顺序,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-02-02
  • Thread类interrupt interrupted及isInterrupted区别

    Thread类interrupt interrupted及isInterrupted区别

    这篇文章主要为大家介绍了Thread类interrupt interrupted及isInterrupted区别,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-10-10
  • Spring-ImportSelector接口功能使用案例

    Spring-ImportSelector接口功能使用案例

    ImportSelector接口是至spring中导入内部类或者外部类的核心接口,只需要其定义的方法内返回需要创建bean的class字符串就好了,这篇文章主要介绍了Spring-ImportSelector接口功能介绍,需要的朋友可以参考下
    2023-09-09
  • Spring @Async无法实现异步的解决方案

    Spring @Async无法实现异步的解决方案

    这篇文章主要介绍了Spring @Async无法实现异步的解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-10-10
  • SpringBoot之Order注解启动顺序说明

    SpringBoot之Order注解启动顺序说明

    这篇文章主要介绍了SpringBoot之Order注解启动顺序说明,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-09-09
  • Java中的对象和对象引用实例浅析

    Java中的对象和对象引用实例浅析

    这篇文章主要介绍了Java中的对象和对象引用,实例分析了对象与对象引用的概念与相关使用技巧,需要的朋友可以参考下
    2015-05-05
  • 简单实现java抽奖系统

    简单实现java抽奖系统

    这篇文章主要教大家如何简单实现java抽奖系统,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-01-01
  • Java解析XML的四种方法详解

    Java解析XML的四种方法详解

    XML现在已经成为一种通用的数据交换格式,平台的无关性使得很多场合都需要用到XML。本文将详细介绍用Java解析XML的四种方法
    2012-10-10

最新评论