參考:《Java 網絡編程》。
有很多數據結構可以作爲池,不過隊列可能是確保任務以先進先出順序來執行的最有效的一種數據結構。但是,不管哪種數據結構實現池,都必須要非常注意同步的問題。
因爲會有很多線程同時與之交互。避免這個問題最簡單的方式就是使用java.util.Vector或者Java集合中API中的同步Collection。ArrayList:List list = Collections.synchronizedList(new ArrayList());
例如我們希望使用GZIPOutputStream壓縮當前牡蠣中的每一個文件。一方面,IO的操作繁重,每一個文件都必須進行讀寫,另一方面,數據壓縮是“CPU密集”度很高的操作。
每個客戶端線程將壓縮文件,主線程主要是確定哪些文件需要壓縮,然後加入到池中。主線程的速度可能會大大快於壓縮線程,因爲主線程的任務就是列出目錄文件。因此先填充線程池。
GZipThread類,包含一個自由字段pool,它包含對池的引用。這個字段爲List,run()方法從池中刪除File對象,壓縮每一個文件。當線程準備從池中獲得新文件是,如果吃爲空,線程就會等待pool對象。pool.wait()。被喚醒是notifyAll()。
import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.List; import java.util.zip.GZIPOutputStream; /** * 一)實現線程池最簡單的方法是在第一次創建池時分配固定數量的線程。 * 當池空時,每個線程都在池中等待。當向池中添加一項任務時,所有等待的線程都得到通知。 * 當一個線程結束其分配的任務時,它就再回到池中獲取新的任務。 * 如果沒有得到,他就等待,直到新的任務添加到池中位置。 * * 二)另一種方法就是將線程本身放到池中,讓主程序從池中取出線程,爲其分配任務。 * 如果必須要完成一項任務,而池中沒有(可用的)線程時,主程序就生成一個新的線程。 * 每個線程結束後都會返回到池中。 * (把這種機制想象爲一個聯盟大廳, * 只有在當前所有成員都被僱傭時, * 纔會有新的工作人員加入聯盟。) * * @author 00 * */ public class GZipThread extends Thread{ private List pool; private static int filesCompressed = 0; public GZipThread(List pool){ this.pool = pool; } private static synchronized void incrementFilesCompressed(){ filesCompressed ++; } public void run(){ while(filesCompressed != GZipAllFiles.getNumberOfFilesToBeCompressed()){ File input = null; synchronized (pool){ while(pool.isEmpty()){ if(filesCompressed == GZipAllFiles.getNumberOfFilesToBeCompressed()){ System.out.println("Thresd ending..."); return; } try{ pool.wait(); }catch(InterruptedException e){ } } input = (File)pool.remove(pool.size() - 1); incrementFilesCompressed(); } //不壓縮已經壓縮過的文件 if(!input.getName().endsWith(".gz")){ try { InputStream in = new FileInputStream(input); in = new BufferedInputStream(in); File output = new File(input.getParent(), input.getName() + ".gz"); if(!output.exists()){ //不覆蓋已經存在的文件 OutputStream out = new FileOutputStream(output); out = new GZIPOutputStream(out); out = new BufferedOutputStream(out); int b; while((b = in.read()) != -1){ out.write(b); } out.flush(); out.close(); in.close(); } } catch (IOException e){ System.err.println(e); } } } } }
GZipAllFiles是主線程,構造了線程池Vector對象,將其傳遞給新高早的GZipThread對象,啓動了四個線程,處理文件和目錄。這些文件會被添加到池中,最後會有這四個線程來處理。
import java.io.File;
import java.util.Vector;
public class GZipAllFiles {
public final static int THREAD_COUNT = 4;
private static int filesToBeCompressed = -1;
public static void main(String[] args){
String[] filesName = {"D://outdata"};
Vector pool = new Vector();
GZipThread[] threads = new GZipThread[THREAD_COUNT];
for(int i=0; i<threads.length; i++){
threads[i] = new GZipThread(pool);
threads[i].start();
}
int totalFiles = 0;
for(int i=0; i<filesName.length; i++){
File f = new File(filesName[i]);
if(f.exists()){
if(f.isDirectory()){
File[] files = f.listFiles();
for(int j=0; j<files.length; j++){
totalFiles ++;
synchronized(pool){
pool.add(0, files[j]);
pool.notifyAll();
}
}
}
}
else {
totalFiles ++;
synchronized (pool) {
pool.add(0, f);
pool.notifyAll();
}
}
}
filesToBeCompressed = totalFiles;
//確保讓所有等待線程知道沒有更多的文件會添加到線程池中;
for (int i=0; i<threads.length; i++) {
threads[i].interrupt();
}
}
public static int getNumberOfFilesToBeCompressed() {
return filesToBeCompressed;
}
}