Java多线程环境下如何高效安全处理数据(输入输出流、文件、网络等)(一)

转载 2013年12月05日 12:38:02

      本博客属原创文章,欢迎转载!转载请务必注明出处:http://guoyunsky.iteye.com/blog/867469

    本博客已迁移本人独立博客: http://www.yun5u.com/

   

  这个标题可能有些歧义,我也不知道该取什么标题,知道的同学帮忙取下.同时这只是我平时的一个总结,
问题估计会有很多,大家帮忙指正,谢谢!
这里先说下应用场景,比如:
   1)需要一直处理一个文件目录,处理里面的文件.文件过多,单线程恐怕速度跟不上,于是使用多线程.
    2)网络下载,需要下载很多URL.单线程也是速度跟不上,于是一个URL用一个线程去下载并处理(如爬虫,这也是我写爬虫的总结).
 
再说下如何高效安全:
    1)以上不可能每一个文件,每一个URL就开一个线程去处理.肯定是先初始化一个线程池,然后将文件、URL放入一个容器(比如队列),然后线程从容器里获取数据去处理,处理完了,就再获取,如此直到处理完毕.
    2)可能文件或者URL的数据会很大,足让你内存溢出.或者多个线程的数据加起来也足以让你内存爆掉.那肯定要设置内存装载的数据大小限制,也就是所谓的缓存。当缓存写满了,再考虑写入文件.
    3)由于线程固定,缓存也是固定,写入的文件也是固定.那这些都是可循环利用的对象。不可能每一次处理都是new,那是极大的浪费。所以可以固定线程的个数,缓存的大小(可以控制内存大小),甚至那个备份文件也是一直可以循环所有的对象
  
所需要的东西:
    1)干活的线程
    2)接活的容器
    3)线程池
    4)可将数据放入内存达到一定阀值后再写入文件的类,同时提供返回数据的功能(内存和文件里的数据都得返回).返回数据也有多种形式,字符串、流?同时也得考虑循环利用,毕竟也是固定的
   5)附加功能:
         a.内容可以指纹化(MD5或SHA1)
         b.可以如InputStream的mark,reset等.毕竟这一切都可以当做输入输出流来处理,我接下来的代码也是
         c.可以控制处理的速度,比如这个场景是下载URL(网络爬虫),我不想下载速度过快.
         d.待补充

 

大概的设计:
   1)干活的线程 MyThread
   2)接活的容器 具体看你的应用,例子里有
    3)线程池   具体也看你的应用,我这里只是测试代码里弄个线程组
    4)处理数据的类:
          a.读数据到内存或文件中的类:MyOutputStream
          b.MyOutputStream里面又要返回数据的类:MyInputStream

 

接下来开始贴代码了.

1)可以读取数据,如果数据过多达到缓存,可以写入文件的类

Java代码  收藏代码
  1. import java.io.File;  
  2. import java.io.FileOutputStream;  
  3. import java.io.IOException;  
  4. import java.io.OutputStream;  
  5.   
  6. public class MyOutputStream extends OutputStream {  
  7.     private boolean isOpen;         // 是否已经打开  
  8.     private long size;              // 数据总大小  
  9.     private String backedFileName;  // 超出缓存,要写入到的文件名  
  10.     private OutputStream diskStream;// 超出缓存,写入到文件的OutputStream  
  11.     private byte[] buffer;          // 缓存  
  12.     private long position;          // 当前位置  
  13.     private boolean recording;      // 是否记录数据中  
  14.       
  15.       
  16.     public MyOutputStream(int bufferSize,String backedFileName){  
  17.         this.buffer=new byte[bufferSize];  
  18.         this.backedFileName=backedFileName;  
  19.         this.recording=true;  
  20.     }  
  21.       
  22.     public void open() throws IOException{  
  23.         if(isOpen()){  
  24.             throw new IOException("MyOutputStream already open for ".concat(Thread.currentThread().getName()));  
  25.         }  
  26.         isOpen=true;  
  27.         this.position=0;  
  28.         this.size=0;  
  29.         this.recording=true;  
  30.           
  31.         closeDiskStream();  
  32.           
  33.         this.diskStream=new FileOutputStream(this.backedFileName);  
  34.           
  35.     }  
  36.       
  37.     private void closeDiskStream() throws IOException{  
  38.         if(this.diskStream!=null){  
  39.             diskStream.close();  
  40.             diskStream=null;  
  41.         }  
  42.     }  
  43.       
  44.     public void closeRecorder() throws IOException{  
  45.         recording=false;  
  46.         closeDiskStream();  
  47.         if(this.size==0){  
  48.             this.size=position;  
  49.         }  
  50.     }  
  51.       
  52.     public boolean isOpen(){  
  53.         return isOpen;  
  54.     }  
  55.     // 记录一个字节  
  56.     private void record(int b) throws IOException{  
  57.         if(this.position>=this.buffer.length){  
  58.             this.diskStream.write((byte)b);  
  59.         }else{  
  60.             buffer[(int)position]=(byte)b;  
  61.         }  
  62.         this.position++;  
  63.     }  
  64.     // 记录多个字节  
  65.     private void record(byte[] b,int off,int len) throws IOException{  
  66.         if(position>=this.buffer.length){   // 如果缓存已经满了,则写入硬盘  
  67.             if(this.diskStream==null){  
  68.                 throw new IOException("diskStream is null for ".concat(Thread.currentThread().getName()));  
  69.             }  
  70.             this.diskStream.write(b, off, len); // 写入硬盘  
  71.             this.position+=len; // 位置增加  
  72.         }else{  // 没满,则写入缓存.如果此时缓存写满了,则再写入磁盘  
  73.             int toCopy=Math.min(this.buffer.length-(int)this.position, len);    // 计算要写入缓存的长度,不让缓存爆掉  
  74.             System.arraycopy(b, off, this.buffer, (int)this.position, len);     // 拷贝到缓存  
  75.             this.position+=toCopy;  
  76.               
  77.             if(toCopy<len){ // 如果缓存已满,则将剩下的数据写入硬盘  
  78.                 //this.diskStream.write(b,off+toCopy,len-toCopy);       
  79.                 record(b,off+toCopy,len-toCopy);    // 为什么不直接用上一行代码?需要验证diskStream  
  80.             }  
  81.         }  
  82.     }  
  83.     // 写入数据  
  84.     @Override  
  85.     public void write(int b) throws IOException {  
  86.         if(recording){  
  87.             record(b);  
  88.         }  
  89.     }  
  90.     // 写入数据  
  91.     @Override  
  92.     public void write(byte[] b, int off, int len) throws IOException {  
  93.        if(recording){  
  94.            record(b,off,len);  
  95.        }  
  96.     }  
  97.     // 写入数据  
  98.     @Override  
  99.     public void write(byte[] b) throws IOException {  
  100.         if(recording){  
  101.             record(b,0,b.length);  
  102.         }  
  103.     }  
  104.      
  105.     // 关闭,关闭了才能获得长度  
  106.     @Override  
  107.     public void close() throws IOException {  
  108.         isOpen=false;  
  109.         closeRecorder();  
  110.     }  
  111.       
  112.       
  113.      
  114.     // 刷新  
  115.     @Override  
  116.     public void flush() throws IOException {  
  117.        if(this.diskStream!=null){  
  118.            this.diskStream.flush();  
  119.        }  
  120.     }  
  121.     // 获得数据大小  
  122.     public long getSize() {  
  123.         return size;  
  124.     }  
  125.       
  126.     public static void main(String[] args) {  
  127.         String dir=new File("").getAbsolutePath().concat(File.separator);  
  128.         String fileMemory=dir.concat("fileMemory.txt");  
  129.         String fileDisk=dir.concat("fileDisk.txt");  
  130.         int bufferSize=5;  
  131.         MyOutputStream mosMemory=null;  
  132.         MyOutputStream mosDisk=null;  
  133.         try {  
  134.             mosMemory=new MyOutputStream(bufferSize,fileMemory);  
  135.             mosMemory.open();  
  136.               
  137.             mosDisk=new MyOutputStream(bufferSize,fileDisk);  
  138.             mosDisk.open();  
  139.               
  140.             for(int i=0;i<100;i++){  
  141.                 if(i<bufferSize){  
  142.                     mosMemory.write(i);  
  143.                 }  
  144.                 mosDisk.write(i);  
  145.             }  
  146.               
  147.             mosMemory.close();  
  148.             mosDisk.close();  
  149.               
  150.             System.out.println("mosMemory length:"+mosMemory.getSize());  
  151.             System.out.println("mosDisk length:"+mosDisk.getSize());  
  152.               
  153.         } catch (IOException e) {  
  154.             // TODO Auto-generated catch block  
  155.             e.printStackTrace();  
  156.         }finally{  
  157.               
  158.         }  
  159.     }  
  160.   
  161. }

相关文章推荐

【C/C++学院】0826-文件重定向/键盘输入流/屏幕输出流/字符串输入输出/文件读写简单操作/字符文件读写二进制与文本差别/get与getline挖掘数据/二进制与文本差别/随机位置/多线程初级

文件重定向 [java] view plaincopy #include   using namespace std;  ...

Java学习之输入输出流/文件读写

周六在学校图书馆实在没有什么事做,敲了一下午的代码,稍微搞懂了点输入输出流与文件读写方面的知识。接下来直接上代码:package liyangfile;import java.io.File; imp...

Java-IO框架-文件输入输出流

数据流 BufferedReader BufferedWriter FileOutputStream FileInputStream InputStreamReader OutputStreamWr...

java输入输出流及文件操作

*Author:Yuanhonglong  *Date:2013-11-29  *1948281915 package mine.file.Read_Write; import java.io...

JAVA之编码/解码-文件输入输出流

问题一:在java中读取文件时应该采用什么编码? Java读取文件的方式总体可以分为两类:按字节读取和按字符读取。按字节读取就是采用InputStream.read()方法来读取字节,然后保存到一个...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)