是不是线程越多越好呢?
在有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了。