I´m building a benchmarkapplication in Java as experiment. The purpose of the tool is to find out how fast a specific database (like Derby, MySQL for example) is in different s开发者_如何转开发ettings.
At the moment I'm trying to find out how fast a database is when executing multiple queries at the same time.
I thought of creating multiple threads where each thread executes multiple queries. But at the moment the queries seems to be executed after the other query is finished instead of concurrently.
I've got the following (simplified) code:
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();
}
}
}
The cpu of mine testsystem has two cores so it should be possible to run two multiple threads at a time, right?
Somebody an idea how to implement this option?
Thank you for your time and help!
UPDATE - added selectData method code:
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;
}
There are two problems here:
- You have to wait until all Threads have finished. This can be done manually, but it is much easier to not use Threads, but an ExecutorService which offers you methods like
invokeAll()
orawaitTermination()
that do this for you. To get anExecutorService
, use the methods from theExecutors
class. This class also offers methods to wrapRunnable
intoCallable
. So in the main method you would create anExecutorService
, submit all the runnables in the for loop, callshutdown()
andawaitTermination()
. Then, print the value of the counter. - You have to take care of adding the times correctly. For this it is important that each
Runnable
instance uses its own stopwatch, so thestopwatch2
variable needs to be a local variable ofrun()
. Also, thecounter
variable cannot be a normal long, but it needs to be an AtomicLong. Otherwise the times of some threads could get lost, because normal addition is not an atomic operation (two threads could try to add their times to the counter variable at the same time, which would probably cause a wrong result).
Here's the code:
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());
}
}
}
If you share the same SQL Connection between the threads then that might be your problem. In general you should avoid sharing the same connection between different threads and use connection pools instead.
The most likely cause of the behaviour you describe is that dataHandler.selectData()
either is synchronized
or relies on a synchronized
method do the work.
To solve this, you either need to remove the synchronization (obviously without breaking things), or have a separate dataHandler
per thread (provided the class in question supports that.)
精彩评论