暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Java如何进行线程通讯

一十二章经 2021-07-03
431

在操作系统中,进程间的通讯有4种,分别是信号量、管道、消息队列、共享内存。通过这4种方式,多个线程就能相互传输数据。本篇主要介绍这4种方式在Java层面是如何实现的

上图是CSAPP书中对于IPC(Interprocess Communication)实现方式的描述。虽然书中描写的是进程间的通讯而不是线程,但Java将进程间的通讯思想也运用在了Java线程中。

共享内存、信号量、消息队列

共享内存的实现是最好理解的一种,可能平时在编写多线程代码时都忽略了这一点。多个线程对同一个Java对象操作就是操作共享内存。

信号量是实现线程间同步的手段,控制进入管程的线程数量,关于线程同步可以看<Java线程同步>。虽然JUC包下有个Semaphore工具类,翻译过来也叫信号量, 但操作系统的信号量的涉及范围会更加广,Java的 wait() .notify()都算是信号量的的实现。简单点说,就是涉及到线程同步的都属于信号量。

一说到消息队列很多人第一反应就是RabbitMQ这些中间件,确实采用这些中间件同样可以达到Java线程通讯,一个线程push数据进MQ,另一个监听MQ并取数据。但其实JUC包下的那些线程安全的阻塞队列都可以视为消息队列。我们可以采用生产消费模式,一个线程制造数据push进阻塞队列中,另一个线程从阻塞队列take数据消费。

管道

Linux系统的”|“操作符就是利用了管道进行进程间的通讯。比如下面查询当前系统中正在运行的Redis的进程信息的命令,管道将 ps 进程输出的数据,做为 grep 命令的输入数据。

ps -ef | grep redis 

Java提供了PipedOutputStream、PipedInputStream这两个类实现通过管道的方式进行IPC

@Slf4j(topic = "c.PipeStreamDemo")
public class PipeStreamDemo {
    public static void main(String[] args) {
        try {
           // 创建线程对象Sender
            Sender sender = new Sender();
           // 创建线程对象Receiver
            Receiver receiver = new Receiver();
           // 写入管道
            PipedOutputStream out = sender.getOutputStream();
           // 从管道读出数据
            PipedInputStream in = receiver.getInputStream();
           // 连通输出输入Pipe
           out.connect(in);

           // 启动线程
            sender.start();
            receiver.start();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
@Slf4j(topic = "c.Sender")
class Sender extends Thread {
    private PipedOutputStream out = new PipedOutputStream();

    public PipedOutputStream getOutputStream() {
        return out;
    }
    public void run() {
        Thread.currentThread().setName("发送线程");
        String s = "Receiver,你好!";
        try {
         log.info("发送了:"+s);
            out.write( s.getBytes());
            out.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
@Slf4j(topic = "c.Receiver")
class Receiver extends Thread {
    private PipedInputStream in = new PipedInputStream();

    public PipedInputStream getInputStream() {
        return in;
    }
    public void run() {
        Thread.currentThread().setName("接收线程");
        String s;
        byte[] b = new byte[1024];
        try {
            int len = in.read(b);
            s = new String(b, 0, len);
            log.info("收到了以下信息:" + s);
            in.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

控制台输出:
c.Sender   [发送线程] - 发送了:Receiver,你好!
c.Receiver [接收线程] - 收到了以下信息:Receiver,你好!

Java通过管道进行线程间的通讯方式很像网络通讯,只不过Java的IPC不需要制定IP、端口只需将PipedOutputStream和PipedInputStream调用.connect()方法连通即可。

总结

CSAPP描述IPC是以进程的方式描述的,站在计算机中各个不同的程序间数据相传输、操作的角度设计了4种通讯方式。而Java的JVM就好比一台计算机,运行在JVM中的各个线程就好比真实机器上的进程,因此Java线程间的通讯处处可以看到IPC的身影。


文章转载自一十二章经,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论