在這邊將提到的是Guarded suspension設計模式,將excel檔的資料透過一thread負責讀取,再
利用三支thread來進行寫入的動作至資料庫。如此一來,比起單一一支程式先讀取再寫入的
動作將也許將會更有效率來達成目的。
拜讀結城浩先生的Java多執行緒與平行處理,在看到第三章的Guarded Suspension Pattern後,
就自行修改他裡面提到的概念,應用在excel讀檔及寫入資料庫的動作,發現雖然同時一讀一
寫可以提高效率,不過由於在setData及getData的method都有加上synchronized的同步處理(會
降低效率),因此在針對write thread提高為三支時,整個執行時間才有顯著的減少,比起先
讀後寫的方式處理。
PS. 在此不會說明thread的概念,網路上資源很豐富
接下來,就以程式來做簡要的說明Guarded Suspension目的
1. Main.java
在主程式內會使用到三個類別,分別為
XlsDataQueue - 定義容器(Queue)、getXlsData、setXlsData等method
ReadXlsThread - 讀取Excel檔的Thread,用到setXlsData method
WriteXlsThread - 寫入資料到DB的Thread,用到getXlsData method
初始化XlsDataQueue,並且開始一個ReadXlsThread及三個WriteXlsThread的動作
2. XlsDataQueue.java
XlsDataQueue初始化帶入當下開始的時間點,而getXlsData為WriteXlsThread呼叫使用,會
判斷當下的queue是否還有資料,若沒有資料的話會進行防衛的等待,並且進入wait的動作;
如果不進入迴圈的話,則get第一筆資料。
PS. 當迴圈內的isReadEnd()成立,表示read的動作已經結束了,因此直接break,避免該
WriteThread繼續等待! 因此最後回傳null,在WriteThread程式那將結束動作!(一般為無窮迴圈)
另setXlsData為ReadThread呼叫來set一筆資料至queue內,並且做notifyAll的動作來解除所有
thread的wait。
3. ReadXlsThread
在此用jxl套件來寫一簡單的讀取excel檔資料的動作。最後當迴圈執行為後,設定一
readEnd的flag表示讀取動作已結束!
PS. XlsData為一Java Bean,定義了兩個欄位分別為id, name
4. WriteXlsThread
在此主要的code為while迴圈內所執行的動作,當執行queue.getXlsData(),若回傳的為null,
即表示當下readThread已結束動作,此時就跳出無窮迴圈!
最後執行結果如下:
在此讀取的檔案為近5000筆資料,共兩欄
整體看起來,利用three thread做寫入的一方,整個執行時間比讀取完再寫入快一秒鐘左右。
由於當一write thread在執行synchronized method時,其它的write thread無法進入,且要達到
synchronized也應該有一些檢查機制在,多多少少有一些影響吧!
不過,如果資料處理比較複雜的話(excel欄位多,資料量大),寫入時還要做計算處理等!也
許時間處理上會差更多也說不定。
PS. 在此說明的程式為"利用同時讀取寫入執行",另"利用讀取完再寫入執行"的程式主要為
雙方的主要動作合併在一起先後執行,在此就省略。
利用三支thread來進行寫入的動作至資料庫。如此一來,比起單一一支程式先讀取再寫入的
動作將也許將會更有效率來達成目的。
拜讀結城浩先生的Java多執行緒與平行處理,在看到第三章的Guarded Suspension Pattern後,
就自行修改他裡面提到的概念,應用在excel讀檔及寫入資料庫的動作,發現雖然同時一讀一
寫可以提高效率,不過由於在setData及getData的method都有加上synchronized的同步處理(會
降低效率),因此在針對write thread提高為三支時,整個執行時間才有顯著的減少,比起先
讀後寫的方式處理。
PS. 在此不會說明thread的概念,網路上資源很豐富
接下來,就以程式來做簡要的說明Guarded Suspension目的
1. Main.java
在主程式內會使用到三個類別,分別為
XlsDataQueue - 定義容器(Queue)、getXlsData、setXlsData等method
ReadXlsThread - 讀取Excel檔的Thread,用到setXlsData method
WriteXlsThread - 寫入資料到DB的Thread,用到getXlsData method
初始化XlsDataQueue,並且開始一個ReadXlsThread及三個WriteXlsThread的動作
XlsDataQueue xlsQueue = new XlsDataQueue(System.currentTimeMillis());
new ReadXlsThread(xlsQueue, "Reader").start();
new WriteXlsThread(xlsQueue, "##Writer1").start();
new WriteXlsThread(xlsQueue, "##Writer2").start();
new WriteXlsThread(xlsQueue, "##Writer3").start();
2. XlsDataQueue.java
public class XlsDataQueue {
private final LinkedList<XlsData> queue = new LinkedList<XlsData>();
private boolean readEnd;
private long startTime;
public XlsDataQueue(long time){
startTime = time;
}
public synchronized XlsData getXlsData(){
while(queue.size() <= 0){
try {
if(!isReadEnd()){
System.out.println(Thread.currentThread().getName()+": wait() begins");
wait();
System.out.println(Thread.currentThread().getName()+": wait() ends");
}else
break;
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return (queue.size() > 0) ? queue.removeFirst() : null;
}
public synchronized void setXlsData(XlsData data){
queue.addLast(data);
notifyAll();
}
public boolean isReadEnd() {
return readEnd;
}
public void setReadEnd(boolean readEnd) {
this.readEnd = readEnd;
}
public long getStartTime() {
return startTime;
}
}
XlsDataQueue初始化帶入當下開始的時間點,而getXlsData為WriteXlsThread呼叫使用,會
判斷當下的queue是否還有資料,若沒有資料的話會進行防衛的等待,並且進入wait的動作;
如果不進入迴圈的話,則get第一筆資料。
PS. 當迴圈內的isReadEnd()成立,表示read的動作已經結束了,因此直接break,避免該
WriteThread繼續等待! 因此最後回傳null,在WriteThread程式那將結束動作!(一般為無窮迴圈)
另setXlsData為ReadThread呼叫來set一筆資料至queue內,並且做notifyAll的動作來解除所有
thread的wait。
3. ReadXlsThread
public class ReadXlsThread extends Thread{
private XlsDataQueue queue;
private String execName;
public ReadXlsThread(XlsDataQueue queue, String execName){
this.queue = queue;
this.execName = execName;
}
public void run(){
Workbook xlsBook = null;
try {
xlsBook = Workbook.getWorkbook(new File("example.xls"));
Sheet sheet = xlsBook.getSheet(0);
for(int i = 0 ; i < sheet.getRows() ; i++){
int id = Integer.parseInt(sheet.getCell(0, i).getContents());
String name = sheet.getCell(1, i).getContents();
XlsData data = new XlsData(id, name);
queue.setXlsData(data);
}
queue.setReadEnd(true);
} catch (BiffException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally{
xlsBook.close();
}
}
}
在此用jxl套件來寫一簡單的讀取excel檔資料的動作。最後當迴圈執行為後,設定一
readEnd的flag表示讀取動作已結束!
PS. XlsData為一Java Bean,定義了兩個欄位分別為id, name
4. WriteXlsThread
private XlsDataQueue queue;
public WriteXlsThread(XlsDataQueue queue, String execName){
this.queue = queue;
this.execName = execName;
}
public void run(){
try {
Class.forName(driver);
m_conn = DriverManager.getConnection(url, user, password);
...
float time = (System.currentTimeMillis()-queue.getStartTime());
System.out.println("連結資料庫:"+time+"毫秒");
while(true){
XlsData data = queue.getXlsData();
if(data == null)break;
execDelete.setInt(1, data.getId());
execDelete.executeUpdate();
execInsert.setInt(1, data.getId());
execInsert.setString(2, data.getName());
execInsert.executeUpdate();
}
time = (System.currentTimeMillis()-queue.getStartTime());
System.out.println("執行時間:"+time+"毫秒");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally{
try {
m_conn.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
在此主要的code為while迴圈內所執行的動作,當執行queue.getXlsData(),若回傳的為null,
即表示當下readThread已結束動作,此時就跳出無窮迴圈!
最後執行結果如下:
在此讀取的檔案為近5000筆資料,共兩欄
整體看起來,利用three thread做寫入的一方,整個執行時間比讀取完再寫入快一秒鐘左右。
由於當一write thread在執行synchronized method時,其它的write thread無法進入,且要達到
synchronized也應該有一些檢查機制在,多多少少有一些影響吧!
不過,如果資料處理比較複雜的話(excel欄位多,資料量大),寫入時還要做計算處理等!也
許時間處理上會差更多也說不定。
PS. 在此說明的程式為"利用同時讀取寫入執行",另"利用讀取完再寫入執行"的程式主要為
雙方的主要動作合併在一起先後執行,在此就省略。
留言
張貼留言