使用RandomAccessFile在两个java进程之间传递数据

大部分情况下,我们面对在两个java进程只见传递数据的问题时,第一个想到的就是开server,然后通过socket收发消息。这方面有大量的框架可用,就不细说了。
但如果两个进程是在一台机器上,那么还可以使用另一种方式来传递数据,那就是使用RandomAccessFile的文件映射模式。
RandomAccessFile的map方法把文件映射到内存中进行快速读写。因此可以通过把消息包写入到文件中,然后另一个进程读取出来的方式来完成数据传递。
在这个过程中,重点是要考虑什么时候写入完成,只有写入完成后,读取进程才可以去读数据,否则就是错误的数据。具体方案如下:
保留文件的首个byte作为标志位,1表示A进程写入完毕,0表示B进程读取完毕。
步骤1:A进程开始写入时,先跳过首字节,然后将数据长度,数据内容写入到文件。写入完毕后,将文件的首字节置为1
步骤2:B进程定时刷新文件到内存中,读取首字节,如果是0则跳出,等待下次刷新。如果是1表示有新数据,则加载。加载完毕后设置首字节为0.表示已经读取完毕。
步骤3:A进程定时刷新文件到内存,读取首字节,如果是0,表示可以写入,继续步骤1的流程。如果是1,则表示数据还没被读取,不可写入。等待下次刷新。
经过以上3个步骤,完成数据的进程间传递。此模式也可以用于将数据传递到非java进程。
代码如下:

public class TestRandomAccessFile {
    private static String FileName="aa.a";
    private static volatile Linux1 lx1;
    private static volatile Linux2 lx2;
    private static long from;
    
    private static void close(Closeable c){
        try{
            c.close();
        }catch(IOException ie){
            ie.printStackTrace();
        }
    }
    
    public static class Linux1{
        MappedByteBuffer buf;
        RandomAccessFile raf;
        
        public Linux1(){
            try{
                raf = new RandomAccessFile(FileName, "rw");
                buf = raf.getChannel().map(MapMode.READ_WRITE, 0, 1024*1024);
            }catch(IOException ie){
                ie.printStackTrace();
            }
            new Thread(()->{
                try{
                    while(true){
                        readAndWrite();
                    }
                }catch(IOException e){
                    e.printStackTrace();
                }
            }).start();
            
        }
        
        private void readAndWrite() throws IOException {
            BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
            String s = reader.readLine();
            if(s != null){
                if("exit".equalsIgnoreCase(s)){
                    exitAll();
                }else{
                    write(s);
                }
            }
        }
        
        private void write(String s){
            if(s.length() == 0)return;
            buf.load();
            buf.position(0);
            byte mark = buf.get();
            while(mark != 0){
                buf.load();
                mark = buf.get();
            }
            if(s != null && s.length() > 0){
                buf.putShort((short)s.length());
                for (int i = 0; i< s.length(); i++){
                    buf.putChar(s.charAt(i));
                }
                buf.position(0);
                buf.put((byte)1); //加可读标记
                buf.force();
                from = System.nanoTime();
            }
        }
        
        public void exit(){
            close(raf);
        }
    }
    
    public static class Linux2{
        MappedByteBuffer buf;
        RandomAccessFile raf;
        public Linux2(){
            try{
                raf = new RandomAccessFile(FileName, "rw");
                buf = raf.getChannel().map(MapMode.READ_WRITE, 0, 1024*1024);
            }catch(IOException e){
                e.printStackTrace();
            }
            
            new Thread(()->{
                while(true){
                    checkAndRead();
                    if(from > 0){
                        long t = (System.nanoTime()-from);
                        System.out.println("time=" + t);
                        from = -1;
                    }
                    try{
                        TimeUnit.NANOSECONDS.sleep(1);
                    }catch(Exception e){
                        e.printStackTrace();
                    }
                }
            }).start();
        }
        
        private void checkAndRead(){
            buf.load();
            if(buf.remaining() == 0)return;
            buf.position(0);
            byte mark = buf.get();
            if(mark == 0)return;
            int len = buf.getShort();
            if(len > 0){
                char[] cs = new char[len];
                for (int i = 0; i< len; i++){
                    cs[i] = buf.getChar();
                }
//                /System.out.println("*****  " + new String(cs));
            }
            buf.position(0);
            buf.put((byte)0);
        }
        
        public void exit(){
            close(raf);
        }
    }
    
    public static void exitAll(){
        lx1.exit();
        lx2.exit();
        System.exit(0);
    }
    
    public static void main(String[] args){
        lx1 = new Linux1();
        lx2 = new Linux2();
    }
}

 

测试下来,使用netty走socket传数据,时间在0.5-1ms之间。用RandomAccessFile模式传数据,则依赖于轮询快慢。由于java定时器的误差,及时是用Thread.sleep(0,1)的方式,我们也只能做到平均1ms左右的延迟。但如果是不sleep(现实不能如此,会导致单核被完全占用),那么延迟可以在0.001ms。所以如果能找到一种更有效的轮询方法,那么使用RandomAccessFile进行进程间数据传输的效率会更高,如果不能,那还不如就Socket模式吧,更通用。

posted @ 2017-04-26 12:12  xuheng  阅读(1360)  评论(1编辑  收藏  举报