一、进程通信方法
在说明线程通信前,有必要对进程通信进行说明;
进程间通信的方法主要有以下几种:
(1)管道(Pipe):管道可用于具有亲缘关系进程间的通信,允许一个进程和另一个与它有共同祖先的进程之间进行通信。 (2)命名管道(named pipe):命名管道克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允许无亲缘关 系 进程间的通信。命名管道在文件系统中有对应的文件名。命名管道通过命令mkfifo或系统调用mkfifo来创建。 (3)信号(Signal):信号是比较复杂的通信方式,用于通知接受进程有某种事件发生,除了用于进程间通信外,进程还可以发送 信号给进程本身;linux除了支持Unix早期信号语义函数sigal外,还支持语义符合Posix.1标准的信号函数sigaction(实际上,该 函数是基于BSD的,BSD为了实现可靠信号机制,又能够统一对外接口,用sigaction函数重新实现了signal函数)。 (4) 消息(Message)队列:消息队列是消息的链接表,包括Posix消息队列system V消息队列。有足够权限的进程可以向队列中添加消息,被赋予读权限的进程则可以读走队列中的消息。消息队列克服了信号承载信息量少,管道只能承载无格式字 节流以及缓冲区大小受限等缺 (5)共享内存:使得多个进程可以访问同一块内存空间,是最快的可用IPC形式。是针对其他通信机制运行效率较低而设计的。往往与其它通信机制,如信号量结合使用,来达到进程间的同步及互斥。 (6)内存映射(mapped memory):内存映射允许任何多个进程间通信,每一个使用该机制的进程通过把一个共享的文件映射到自己的进程地址空间来实现它。 (7)信号量(semaphore):主要作为进程间以及同一进程不同线程之间的同步手段。 (8)套接口(Socket):更为一般的进程间通信机制,可用于不同机器之间的进程间通信。起初是由Unix系统的BSD分支开发出来的,但现在一般可以移植到其它类Unix系统上:Linux和System V的变种都支持套接字。
二、线程通信方法
线程通信主要包括两种方法:
(1)共享内存
共享内存的方法在前面的("生产者消费者"相关地点都有介绍);下面介绍另外一种形式:
通过内部类实现线程的共享变量
/** * 通过内部类实现线程的共享变量 * */ public class Innersharethread { public static void main(String[] args) { Mythread mythread = new Mythread(); mythread.getThread().start(); mythread.getThread().start(); mythread.getThread().start(); mythread.getThread().start(); } } class Mythread { int index = 0; private class InnerThread extends Thread { public synchronized void run() { while (true) { System.out.println(Thread.currentThread().getName() + "is running and index is " + index++); } } } public Thread getThread() { return new InnerThread(); } } //在这其中内部类共享类公共类里面的index变量,并通过对公共类进行加锁达到方法同步的目的。
(2)管道
主要分为一下步骤:
首先建立管道流,并将管道流的输入输出对象进行链接;
将管道流加入到生产对象(线程)中;
通过管道流引出输入输出流,并在线程中对这些流进行操作;
注:管道流的的read的方法是一种阻塞方法;
public class CommunicateWhitPiping { public static void main(String[] args) { /** * 创建管道输出流 */ PipedOutputStream pos = new PipedOutputStream(); /** * 创建管道输入流 */ PipedInputStream pis = new PipedInputStream(); try { /** * 将管道输入流与输出流连接 此过程也可通过重载的构造函数来实现 */ pos.connect(pis); } catch (IOException e) { e.printStackTrace(); } /** * 创建生产者线程 */ Producer p = new Producer(pos); /** * 创建消费者线程 */ Consumer c = new Consumer(pis); /** * 启动线程 */ p.start(); c.start(); }}/** * 生产者线程(与一个管道输入流相关联) * */class Producer extends Thread { private PipedOutputStream pos; public Producer(PipedOutputStream pos) { this.pos = pos; } public void run() { int i = 8; try { pos.write(i); } catch (IOException e) { e.printStackTrace(); } }}/** * 消费者线程(与一个管道输入流相关联) * */class Consumer extends Thread { private PipedInputStream pis; public Consumer(PipedInputStream pis) { this.pis = pis; } public void run() { try { System.out.println(pis.read()); } catch (IOException e) { e.printStackTrace(); } }}
(3)通过调用线程的公共接口
上图所示为调用公共接口与Actor模型的区别,调用公共接口是线程A获取线程B的引用,并通过调用线程B的方法,想B中输入信息,从而达到线程消息传递的目的,它的缺陷也是显而易见的,那就是在B中滞留的问题(方法必须在B中返回,A的流程才能继续执行下去)!
三、其它
众所周知,在java的多线程体系中存在这诸多问题,通信过程的内存共享往往会导致意想不到的错误,而管道流的阻塞方法和调用接口方法的同步性也会是得有些时候并发编程有些困难;现在流线的多线程框架为ACTOR体系;在c++中已经相关包的实现( c++中ACTOR框架的具体介绍),而在java中的衍生语言(其实是基于jvm的语言)scala也提供了Actor机制,具体可进行相关的搜索