Java线程间的通信方式

    科技2026-09-24  22

    参考:Java并发编程的艺术

    线程间通信的方式有:

    通过volatile和synchronized关键字等待/通知机制管道输入/输出流Thread.join等待线程终止ThreadLocal线程本地变量

    1 通过 volatile 和 synchronized 关键字

    Java 支持多个线程同时访问一个对象或者对象的成员变量。

    每个线程在自己的本地内存中都有共享变量的一份拷贝。

    volatile

    volatile关键字用来修饰变量。任何对该变量的访问均需要从共享内存中获取,任何对该变量的修改必须同步刷新到共享内存中。保证了共享变量的 可见性、原子性。

    synchronized

    synchronized可以修饰方法或者代码块。主要确保同一时刻,只能有一个线程能够访问同步代码,保证了线程对共享变量的 可见性、排他性。

    2 等待/通知机制

    等待/通知相关的方法是任意Java对象都具备的。

    方法名称描述wait()调用该方法的线程进入WAITING状态,只有等待另一个线程的通知或被中断才会返回。调用wait()方法之后,会释放对象的锁。wait(long)超时等待一段时间,参数是毫秒。只有等待另一个线程的通知或被中断,或者超时,就返回。wait(long, int)超时等待一段时间,可以对超时时间更细粒度的控制,可以达到纳秒级。notify()通知一个在对象上等待的线程,使其从wait()方法返回。返回的前提是该线程获取到了对象的锁。notifyAll()通知所有等待在该对象上的线程。

    2.1 等待/通知的经典范式

    等待方(消费者)遵循如下原则:

    获取对象的锁。如果条件不满足,那么调用对象的wait()方法,被通知(notify)后仍要检查条件。条件满足则执行对应逻辑。 synchronized (对象) { while (条件不满足,需等待通知) { 对象.wait(); } 条件满足,对应处理逻辑 }

    通知方(生产者)准寻如下原则:

    获取对象的锁。改变条件。通知所有等待在对象上的线程。 synchronized (对象) { 改变条件 对象.notifyAll(); }

    注意:无论是等待方还是通知方,首先都要先获得对象的锁。

    3 管道输入/输出流

    管道的输入/输出流,主要用于 线程之间的数据传输,传输媒介为 内存。

    具体实现包括:

    面向字节的PipedOutputStream、PipedInputStream

    面向字符的PipedReader、PipedWriter

    例子:

    public class MainThread { public static void main(String[] args) throws IOException { PipedWriter writer = new PipedWriter(); PipedReader reader = new PipedReader(); // 将管道的输出流和输入流连接,否则在使用的时候会抛出IOException writer.connect(reader); // 启动子线程 Thread printThread = new Thread(new PrintThread(reader), "print-thread"); printThread.start(); // 主线程输出数据 char[] array = "Hello World.".toCharArray(); writer.write(array, 0, array.length); } } /** * 子线程 */ class PrintThread implements Runnable { /** * 管道输入流 */ private PipedReader reader; public PrintThread(PipedReader reader) { this.reader = reader; } @Override public void run() { int ch = 0; try { // 接收数据 while ((ch = reader.read()) != -1) { System.out.print((char) ch); } } catch (IOException e) { System.out.println(); e.printStackTrace(); } } }

    注意:对于Piped类型的流,必须先调用connect()方法将输入/输出流进行绑定。

    4 Thread.join 等待线程终止

    如果一个线程需要等待另一个线程执行一个任务之后再继续执行,可以使用Thread,join()方法。

    如果线程threadA调用了threadB.join()方法,那么线程threadA进入WAITING状态,需要等待线程threadB终止之后才能从threadB.join()处返回。

    下表整理了线程提供的join方法:

    方法说明join()等待线程终止后返回。join(long)超时等待线程终止后返回,如果超时时间到了,立刻返回。join(long, int)超时等待线程终止后返回,如果超时时间到了,立刻返回。纳秒级别的超时时间。

    例子:

    public class MainThread { public static void main(String[] args) throws IOException, InterruptedException { Thread joinThread = new Thread(new JoinThread(), "join-thread"); joinThread.start(); System.out.println("等待线程终止."); joinThread.join(); System.out.println("从join()返回."); } } /** * 子线程 */ class JoinThread implements Runnable { @Override public void run() { try { System.out.println(Thread.currentThread().getName() + " 执行任务..."); Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } }

    5 ThreadLocal 线程本地变量

    一个线程可以通过ThreadLocal变量查询到绑定在这个线程上的值。

    详细内容请查看:

    深入理解ThreadLocalInheritableThreadLocal传递父线程的线程本地变量给子线程
    Processed: 0.010, SQL: 9