Java线程间的通信方式

参考:Java并发编程的艺术

线程间通信的方式有:

  • 通过volatilesynchronized关键字
  • 等待/通知机制
  • 管道输入/输出流
  • 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 等待/通知的经典范式

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

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

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

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

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

3 管道输入/输出流

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

具体实现包括:

  • 面向字节的PipedOutputStreamPipedInputStream

  • 面向字符的PipedReaderPipedWriter

例子:

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变量查询到绑定在这个线程上的值。

详细内容请查看:

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值