2011-03-09 57 views
1

我在Java中構建了一個基準測試應用程序作爲實驗。該工具的目的是找出特定數據庫(例如Derby,MySQL)在不同設置下的速度。Java併發查詢

目前我正試圖找出在同一時間執行多個查詢時數據庫有多快。

我想創建多個線程,其中每個線程執行多個查詢。但目前查詢似乎在另一個查詢完成後執行,而不是同時執行。

我已經得到了以下(簡化)代碼:

Runner testCase = new Runner(); 

for (int i = 0; i < THREAD_COUNT; i++) { 
    Thread testThread = new Thread(testCase); 
    testThread.start(); 
} 

public class Runner implements Runnable { 

    public void run() { 

     for (int i = 0; i < Benchmarker.QUERY_COUNT; i++) { 

     stopwatch2.start(); 
     List<Person> selectData = dataHandler.selectData(); 
     stopwatch2.stop(); 
     counter += stopwatch2.getStopwatchValue(); 

     } 
    } 
} 

礦的CPU testsystem有兩個核心,因此應該可以運行在兩個時間多線程,對不對?

有人想法如何實現這個選項?

謝謝你的時間和幫助!

更新 - 添加selectData方法代碼

public List<Person> selectData() { 

List<Person> data = new ArrayList<Person>(); 

try { 
    // Select all persons from the database 
    ResultSet resultSet = connection.prepareStatement("SELECT * FROM PERSON ORDER BY name").executeQuery(); 

    // Add all the persons to a arraylist 
    while (resultSet.next()) { 
    data.add(new Person(resultSet.getString("name"))); 
    } 

    // Close the resultset 
    resultSet.close(); 

} catch (SQLException ex) { 
    Logger.getLogger(Derby.class.getName()).log(Level.SEVERE, null, ex); 
} 

return data; 
} 
+0

如果你把'System.out.println'了'for'之後,沒有它打印你得到的查詢結果之前? – bluefoot 2011-03-09 11:04:47

+0

是的,但在for循環之後,我輸出了線程的總執行時間。而且由於線程尚未完成,時間不正確。 當我在for循環中添加testThread.join()時,在所有已執行的線程都準備好之後打印時間,但線程等待每個其他完成。因此線程1啓動並在線程2啓動之前完成。 – NickGreen 2011-03-09 11:52:50

+0

您是否嘗試過使用eclipse調試基準測試應用程序,並找出在等待第一個線程完成時其他線程被阻塞的方法? – 2011-03-09 12:10:48

回答

4

有兩個問題在這裏:

  1. 你必須等待,直到所有線程都完成。這可以手動完成,但不使用線程要容易得多,但ExecutorService可以爲您提供像invokeAll()awaitTermination()這樣的方法。要獲得ExecutorService,請使用Executors類中的方法。此課程還提供了將Runnable包裝爲Callable的方法。因此,在主要方法中,您將創建ExecutorService,提交for循環中的所有可運行參數,請撥打shutdown()awaitTermination()。然後,打印計數器的值。
  2. 您必須注意正確添加時間。爲此,每個Runnable實例都使用自己的秒錶很重要,因此stopwatch2變量需要是局部變量run()。此外,counter變量不能是一個正常的長,但它需要是一個AtomicLong。否則,某些線程的時間可能會丟失,因爲正常的添加不是原子操作(兩個線程可能會嘗試將它們的時間同時添加到計數器變量中,這可能會導致錯誤的結果)。

下面的代碼:

void runTests() { 
    Runner testCase = new Runner(); 
    ExecutorService executor = Executors.newCachedThreadPool(); 

    for (int i = 0; i < THREAD_COUNT; i++) { 
    executor.execute(testCase); 
    } 
    executor.shutdown(); 
    executor.awaitTermination(60, TimeUnit.SECONDS); 
    System.out.println(counter.toString()); 
} 

private AtomicLong counter = new AtomicLong(); 

public class Runner implements Runnable { 

    public void run() { 
     StopWatch stopwatch2 = ... // get a stopwatch instance here 

     for (int i = 0; i < Benchmarker.QUERY_COUNT; i++) { 

     stopwatch2.start(); // this needs to reset the stopwatch to 0 
     List<Person> selectData = dataHandler.selectData(); 
     stopwatch2.stop(); 
     counter.addAndGet(stopwatch2.getStopwatchValue()); 

     } 
    } 
} 
+0

謝謝,我會嘗試,並將其作爲接受的答案,當它的作品:)!還有一個問題:當線程進入等待狀態時,如何停止線程的秒錶,並在稍後恢復,因爲我不想添加線程實際未運行的時間。 – NickGreen 2011-03-09 12:31:43

+0

@NickGreen這取決於你的秒錶的執行情況。你從哪裏得到它的?也許它有一個暫停()方法或類似的東西? – 2011-03-09 12:46:07

+0

這是我自己的類..但問題是:我只想得到'dataHandler.selectData();'的執行時間爲每個亞軍。而且因爲線程可以被推到等待狀態,並且以後可以重新開始,所以秒錶中的測量時間比它應該高,因爲它增加了Runner的非活動時間。 – NickGreen 2011-03-09 13:45:36

0

您所描述的行爲的最可能的原因是,無論是dataHandler.selectData()是​​或依賴於​​方法做的工作。

爲了解決這個問題,就需要取出同步(顯然沒有打破的東西),或具有每個線程獨立dataHandler(提供有問題的類支持。)

+0

我添加了selectData代碼。另外,每個線程都有它自己的dataHandler。 – NickGreen 2011-03-09 11:47:35

+0

這些數據處理程序是否共享相同的SQL連接? – NPE 2011-03-09 11:50:57

+0

不,每個都有自己的連接:connection = DriverManager.getConnection(this.getServerUrl());在serverurl如下所示的情況下:jdbc:derby:// localhost:port/db; create = false – NickGreen 2011-03-09 12:03:34

3

如果您共享相同的SQL連接在線程之間,那可能是你的問題。一般而言,您應避免在不同線程之間共享相同的連接並改用連接池。

+0

另外,在這種情況下請注意最大化連接池並限制吞吐量。 – Robin 2011-03-09 12:14:34

+0

我在Runner for-loop之前打開連接,並在for循環之後關閉連接。連接用以下代碼打開:DriverManager.getConnection(this.getServerUrl()); getServerUrl()返回JDBC連接url。 – NickGreen 2011-03-09 12:15:19