c3 Threads - Thread Pools and Executors

来源:互联网 发布:马云的淘宝怎么赚钱的 编辑:程序博客网 时间:2024/05/29 16:32

是不是线程越多越好呢?

在有I/O瓶颈的程序里,比如大部分网络程序,使用多线程可以提高性能,在一个程序block时,其他程序继续执行,以使cpu别闲着。但是,是不是线程越多越好呢?不是!因为使用多线程本身也是有成本的:创建、销毁线程需要VM销毁资源,需要垃圾回收器忙活,当一个小服务器不停的创建销毁了成百上千个线程时,这本身就是巨大的资源消耗;其次,更重要的是,线程间的切换!如果是cpu-bound的程序,那少切换会使程序更快的执行完!再者,最重要的是,虽然线程可以提高cpu使用率,但是cpu资源也是有限的,如果一味的spawn多的线程,那MIPS和Memory资源就全部花在线程管理上了。


java.util.concurrent中的类Executor可以很容易的管理thread,只需要把Runnable对象丢给pool,然后会得到一个Future对象,可用来查看task执行情况。

看个例子,现在要用java.util.zip.GZIPOutputStream压缩给定目录下的文件。

一方面,需要频繁的I/O操作,read和write。另一方面,压缩时又是cpu-intensive,所以不能创建太多的线程,这里用线程池非常合适。

class GZipRunnable implements Runnable {  private final File input;  public GZipRunnable(File input) {    this.input = input;  }  @Override  public void run() {    // don't compress an already compressed file    if (!input.getName().endsWith(".gz")) {      File output = new File(input.getParent(), input.getName() + ".gz");      if (!output.exists()) { // Don't overwrite an existing file        try ( // with resources; requires Java 7          InputStream in = new BufferedInputStream(new FileInputStream(input));                OutputStream out = new BufferedOutputStream(                        new GZIPOutputStream(                          new FileOutputStream(output)));                       ) {                          int b;                          while ((b = in.read()) != -1) out.write(b);                          out.flush();                      } catch (IOException ex) {                        System.err.println(ex);                      }                    }                 }             }   }

注意jdk7的try-with-resource statement,inputstream和outputstream在try的开头声明,在try的结尾处自动close。

还有,将input和output都buffering!尤其对I/O-limited的网络程序,使用buffer的最坏结果是没有效果,最好结果是在性能上提高一个数量级。

 class GZipAllFiles {  public final static int THREAD_COUNT = 4;  public static void main(String[] args) {    ExecutorService pool = Executors.newFixedThreadPool(THREAD_COUNT);    for (String filename : args) {      File f = new File(filename);      if (f.exists()) {        if (f.isDirectory()) {          File[] files = f.listFiles();          for (int i = 0; i < files.length; i++) {            if (!files[i].isDirectory()) { // don't recurse directories              Runnable task = new GZipRunnable(files[i]);              pool.submit(task);            }          }        } else {          Runnable task = new GZipRunnable(f);          pool.submit(task);        }      }    }    pool.shutdown();  }}

注意到pool.shutdown(),在你将所有task都添加到pool后就可以调用该方法,调用后并不会丢弃没有执行的task,只是告诉pool,没有新任务加入了,在执行完已有task后就可以shutdown了。
















0 0