Java优化编程笔记——Java核心类与性能优化_JAVA_编程开发_程序员俱乐部

中国优秀的程序员网站程序员频道CXYCLUB技术地图
热搜:
更多>>
 
您所在的位置: 程序员俱乐部 > 编程开发 > JAVA > Java优化编程笔记——Java核心类与性能优化

Java优化编程笔记——Java核心类与性能优化

 2013/8/11 19:07:39  EalayKing  程序员俱乐部  我要评论(0)
  • 摘要:1.散列表核心类Vector——线程安全,ArrayList——线程不安全Hashtable——线程安全,HashMap——线程不安全·VectorVector类中的方法(除构造方法)都是线程安全的,故在要求线程安全的场合下调用Vector类的下列方法,不需要考虑线程安全的问题,如:publicsynchronizedvoidaddObj2Vector(Objectobj){vector.addElement(obj);}或,publicvoidaddObj2Vector(Objectobj)
  • 标签:笔记 Java 性能优化 编程 优化
1. 散列表核心类
Vector——线程安全,ArrayList——线程不安全
Hashtable——线程安全,HashMap——线程不安全

· Vector
  Vector类中的方法(除构造方法)都是线程安全的,故在要求线程安全的场合下调用Vector类的下列方法,不需要考虑线程安全的问题,如:
class="java" name="code">
  public synchronized void addObj2Vector(Object obj) {
      vector.addElement(obj);
  }

或,
  public void addObj2Vector(Object obj) {
      synchronized {
          vector.addElement(obj);
      }
  }

  上面的代码主动为Vector对象添加了线程同步的约束语句来同步多线程环境,其实是多余的,只会降低系统性能,因为Vector对象本身就是线程安全的。修正上述代码:
  public void addObj2Vector(Object obj) {
      vector.addElement(obj);
  }

  在多线程环境下使用Vector类十分简洁、安全。但在某些场景中,线程并不需要写(更新)共享资源数据,而仅仅是读取、使用,此时开发人员就不需要考虑多线程的同步问题。

同步方法的意义
  防止多线程的运行环境里,两个或多个线程同时访问受保护的资源,而导致资源数据不完整,破坏数据资源,程序无法达到预期目的。
  
· ArrayList
  ArrayList类中的方法是非线程安全的,尽管如此,在多线程环境下要求线程安全,也可以使用ArrayList等非线程安全的散列表核心类,可以通过下面的方法:
  List list = Collections.synchronizedList(new ArrayList());

  通过这个方法可将ArrayList标识为线程安全(thread-safe)的对象。
  
  在合适的场合,建议尽量使用ArrayList,而不建议使用Vector。
  
与LinkedList比较:
  ArrayList是通过内部数组结构Object[]实现的,而LinkedList通过将一系列的内部记录连接在一起实现的。即ArrayList对象持有的数据是顺序存储,而LinkedList对象则是链式存储。
  
String类
  String可以存储16位的Unicode字符序列及其长度,一旦被创建,这个字符串就是恒定不变。

字符串累加
下面的代码:
  String s = "first string";
  s = s + “second string”;

类似于下面的代码:
  String s = "first string";
  StringBuffer tmp = new StringBuffer(s);
  tmp.append("second string");
  s = tmp.toString();

以上代码的意思:将两个字符串串联后拷贝到一个临时字符串缓冲区变量中,如果这个被拷贝的字符串较长,那么这个拷贝操作就非常耗费资源。

有2种方式可提高字符串累加的性能:
1) 通过StringBuffer来构建一个字符串对象
  例:
  String s = "";
  for (int i = 1; i <= N; i++) {
  s = s + "*";
  }

  修改为:
  StringBuffer sb = new StringBuffer();
  for (int i = 1; i <= N; i++) {
  sb.append("*");
  }
  String s = sb.toString();


2) 通过char[]数组
  先创建一个字符数组,然后向其中添加字符,达到累加的目的,返回最终生成的字符串。

遍历字符
  若字符串特别长,又需要逐一获取字符串特定位置的字符,则通过调用toCharArray()转化为字符串数组,再由数组索引值获取指定位置的字符,而不是直接调用charAt(int index)。
  例:
  ...
  char[] ss = s.toCharArray();
  for (int i = 0; i < ss.length; i++) {
      char c = ss[i];
      ...
  }
  ...

  
3. 系统I/O类
  Java中输入、输出流种类繁多,但按照它们所处理的数据流的类型可将其分为两类:二进制数据输入/输出流(处理二进制数据,),与字符数据输入/输出流(处理字符数据)。InputStream与OutputStream是用来处理二进制数据流的高层接口,Reader与Writer是用来处理字符数据的高层接口。
  
  Java中一般的输入与输出流类都是采用单字节的读取方法,进行数据I/O操作的,即每次只读取或写入一个字节的数据,这种方法显然烦琐低效。

单字节读取/写入过程

1) 通过系统缓冲流类提高I/O操作效率

采用数据缓冲类读取/写入过程
例:
  InputStream is = null;
  OutputStream os = null;
  try {
      is = new FileInputStream(fileFrom);
      os = new FileInputStream(fileTo);
      while (true) {
            int bytedata = is.read();
          if (bytedata == -1) {
                break;
          }
          os.write(bytedata);
      }
  } catch (Exception e) {
      ...
  } finally {
      if (is != null) {
          is.close();
      }
      if (os != null) {
          os.close();
      }
  }

可修改上面的代码改用系统缓冲流类来提高I/O效率:
  InputStream is = null;
  OutputStream os = null;
  try {
      is = new BufferedInputStream(new FileInputStream(fileFrom));
      os = new BufferedInputStream(new FileInputStream(fileTo));
      while (true) {
          int bytedata = is.read();
          if (bytedata == -1) {
              break;
          }
          os.write(bytedata);
      }
  } catch (Exception e) {
      ...
  } finally {
      if (is != null) {
          is.close();
      }
      if (os != null) {
          os.close();
      }
  }

2) 通过自定义缓冲区提高I/O操作效率
例:
  InputStream is = null;
  OutputStream is = null;
  try {
      is = new FileInputStream(fileFrom);
      os = new FileInputStream(fileTo);
      byte[] totalBytes = new byte[is.available()];
      is.read(totalBytes);
      os.write(totalBytes);
  } catch (Exception e) {
      ...
  } finally {
      if (is != null) {
          is.close();
      }
      if (os != null) {
          os.close();
      }
  }

    不是数据缓冲越大,系统性能就越高,缓冲区的大小应该根据实际情况定义一个合理的值(最好是512的倍数,这样运算速度要快一些)。通常情况下需要权衡两个方面的因素:
  a) 读取与写入的文件的大小
  b) 应用运行的硬件环境的内存资源

  总之,得到下面的结论:
  · 采用默认的数据输入/输出方式(直接读取与写入)将导致系统性能下降
  · 采用系统数据缓冲流类读取与写入数据,将提升系统性能
  · 采用自定义合理缓冲区读取与写入数据,将更大程度地提升系统性能
  
3) 通过压缩流提供I/O操作效率
   java.util.zip包存在ZipInputStream与ZipOutputStream,可通过它们实现压缩流的读取与写入。
例:
public void zip(String fileFrom, String fileTo) throws IOException {
    ZipOutputSream zos = new ZipOutputSream(new FileOutputStream(fileTo));
    zos.putNextEntry(new ZipEntry(fileTo));

    FileInputStream fis = new FileInputStream(fileFrom);
    byte[] buf = new byte[1024];
    int n;
    while ((n = fis.read(buf)) > -1) {
        zos.write(buf, 0, n);
  }
  buf = null;
  fis.close();
  fis = null;
  
  zos.flush();
  zos.closeEntry();
  zos.close();
  zos = null;
}

public void upzip(String fileFrom, String fileTo) throws IOException {
    ZipInputStream zis = new ZipInputStream(new FileInputStream(fileFrom));
    if (zis.getNextEntry() != null) {
        FileOutputStream fos = new FileOutputStream(fileTo);
        int n;
        byte[] buf = new byte[1024];
        while ((n = zis.read(buf)) > -1) {
            fos.write(buf, 0, n);
     }
     buf = null;
     fos.flush();
     fos.close();
     fos = null; 
    }
    
    zis.closeEntry();
    zis.close();
    zis = null;
}

  除了在文件读写方面可以采用压缩流提高系统性能外,在网络数据传输中也可以通过压缩流节省网络资源,提高传输效率。

4) 通过非阻塞I/O优化应用性能
  JDK1.4中退出的java.nio(新输入/输出)软件包,NIO包设计更清晰、简单、高效,能帮助开发者在数据处理时取得更强的性能及更佳的扩展性
  
  在NIO包中引入了四个关键的抽象数据类型,它们共同解决传统的I/O类中的一些问题
  a) Buffer:包含数据且用于读写的线性表结构。其中还提供了一个特殊类用于内存映射文件的I/O操作
  b) Charset:提供Unicode字符串映射到字节序列以及逆映射的操作
  c) Channel:包含socket、file和pipe三种管道,它实际上是双向交流的通道
  d) Selector:它将多元异步I/O操作集中到一个或多个线程中(它可被看成UNIX中select()或Win32中WaitForSingleEvent()的面向对象版本

  NIO API引入一种称为通道的新型原始I/O提取方法。通道表示到实体(如硬件设备、文件、网络套接字或可执行一个或多个诸如读写I/O操作的程序组件)的开放连接。通道可以处于打开或关闭状态,既是可异步关闭的,又是可中断的。
  
  有几个接口对Channel接口进行了扩展,每个接口指定一个新的I/O操作。以下是其中几个接口的信息:
  ReadableByteChannel指定一个将字节从通道读入缓冲器的read()
  WritableByteChannel指定一个将字节从通道写入缓冲器的write()
  ScatteringByteChannel和GatheringByteChannel分别扩展ReadableByteChannel和WritableByteChannel,采用缓冲器序列而非一个单独缓冲器增加read()和write()
  FileChannel支持从连接到文件的通道读取字节或向其写入字节,及查询和修改当前的文件位置和将文件调整为指定大小等常见操作
  
使用FileChannel读写文件,例:
public void copyFile(String fileFrom, String fileTo) throws IOException {
    FileInputStream inStream = new FileInputStream(fileFrom);
  FileOutputStream outStream = new FileOutputStream(fileTo);
  FileChannel inChannel = inStream.getChannel();
  FileChannel outChannel = outStream.getChannel();
  
  ByteBuffer byteBuf = ByteBuffer.allocate(512);
  while (true) {
      byteBuf.clear();
      int n = inChannel.read(byteBuf);
      if (n == 0 || n ==1) {
          break;
      }
      byteBuf.flip();
      outChannel.write(byteBuf);
  }
  
  byteBuf = null;
  inChannel.close();
  inChannel = null;
  outChannel.close();
  outChannel = null;
  inStream.close();
  inStream = null;
  outStream.close();
  outStream = null;
}

使用FileChannel读取文本文件内容,例:
  public void readFile(String file, String charset) throws IOException {
        FileInputStream inStream = new FileInputStream(file);
        FileChannel inChannel = inStream.getChannel();
        
        ByteBuffer byteBuf = ByteBuffer.allocate(512);
        String content;
        while (true) {
            byteBuf.clear();
            int n = inChannel.read(byteBuf);
            if (n == 0 || n == -1) {
                break;
            }
            byteBuf.flip();
            content = new String(byteBuf.array(), charset);
            System.out.print(content);
        }
        byteBuf = null;
        content = null;
        inChannel.close();
        inChannel = null;
        inStream.close();
        inStream = null;
    }

   非阻塞操作是NIO最常见与最广泛应用的功能。需要调用SelectableChannel中的configureBlocking()方法配置阻塞或非阻塞操作(在阻塞方式下调用读、写或其他操作,直到操作完成才能返回)。
   
  非阻塞I/O最为显著的性能优势包括:
  · 可以进行非阻塞异步输入/输出
  · 能锁定整个或部分文件

  在非阻塞方式下,SelectableChannel对象通过register()向Selector对象注册,然后签署特殊事件。在指定操作发生时,Selector对象就会发出通知。这些事件由SelectionKey的域反映。
例:
    …
    InetSocketAddress addr = new InetSocketAddress(hostname, port);
    SocketChannel channel = SocketChannel.open();
    channel.configureBlocking(false);
    channel.connect(addr);
    …

  在connect()后,它将立即返回执行情况。然后,你必须指出如何处理这样的通道连接,这时有:
  …
  Selector selector = Selector.open();
  channel.register(selector, SelectionKey.OP_CONNECT | SelectionKey.OP_READ);
  …

在收到通知后,又有:
    …
    while (selector.select(500) > 0) {
      Set<SelectionKey> readyKeys = selector.selectedKeys();
      Iterator<SelectionKey> iterator = readyKeys.iterator();
      SelectionKey key;
      while (iterator.hasNext()) {
          key = iterator.next();
          iterator.remove();
          
          SocketChannel chan = (SocketChannel) key.channel();
          if (key.isConnectable()) {
              …
          } else if (key.isReadable()) {
              …
          }
      }
    }
    …
  • 大小: 758 Bytes
  • 大小: 1.1 KB
  • 查看图片附件
发表评论
用户名: 匿名