我正在尝试测量Database Insert
的性能。为此,我编写了一个 StopWatch
类,它将在 executeUpdate
方法之前重置计数器,并在 executeUpdate
方法完成后计算时间。
我试图查看每个线程占用了多少时间,因此我将这些数字保存在 ConcurrentHashMap
中。
下面是我的主课-
public static void main(String[] args) {
final int noOfThreads = 4;
final int noOfTasks = 100;
final AtomicInteger id = new AtomicInteger(1);
ExecutorService service = Executors.newFixedThreadPool(noOfThreads);
for (int i = 0; i < noOfTasks * noOfThreads; i++) {
service.submit(new Task(id));
}
while (!service.isTerminated()) {
}
//printing the histogram
System.out.println(Task.histogram);
}
下面是实现 Runnable 的类,我在其中尝试测量每个线程在插入数据库时的性能,这意味着每个线程插入数据库需要多少时间-
class Task implements Runnable {
private final AtomicInteger id;
private StopWatch totalExecTimer = new StopWatch(Task.class.getSimpleName() + ".totalExec");
public static ConcurrentHashMap<Long, AtomicLong> histogram = new ConcurrentHashMap<Long, AtomicLong>();
public Task(AtomicInteger id) {
this.id = id;
}
@Override
public void run() {
dbConnection = getDBConnection();
preparedStatement = dbConnection.prepareStatement(Constants.INSERT_ORACLE_SQL);
//other preparedStatement
totalExecTimer.resetLap();
preparedStatement.executeUpdate();
totalExecTimer.accumulateLap();
final AtomicLong before = histogram.putIfAbsent(totalExecTimer.getCumulativeTime() / 1000, new AtomicLong(1L));
if (before != null) {
before.incrementAndGet();
}
}
}
下面是秒表类
/**
* A simple stop watch.
*/
protected static class StopWatch {
private final String name;
private long lapStart;
private long cumulativeTime;
public StopWatch(String _name) {
name = _name;
}
/**
* Resets lap start time.
*/
public void resetLap() {
lapStart = System.currentTimeMillis();
}
/**
* Accumulates the lap time and return the current lap time.
*
* @return the current lap time.
*/
public long accumulateLap() {
long lapTime = System.currentTimeMillis() - lapStart;
cumulativeTime += lapTime;
return lapTime;
}
/**
* Gets the current cumulative lap time.
*
* @return
*/
public long getCumulativeTime() {
return cumulativeTime;
}
public String getName() {
return name;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(name);
sb.append("=");
sb.append((cumulativeTime / 1000));
sb.append("s");
return sb.toString();
}
}
运行上面的程序后,我可以看到插入了 400 行。当它打印直方图时,我只看到这样-
{0=400}
这意味着 400 个电话在 0 秒内返回?这当然不可能。
我只是想看看每个线程花费了多少时间来插入记录,然后将这些数字存储在 Map
中并从主线程打印该映射。
我认为我假设的问题是由于这里的线程安全而发生的,这就是我猜它在执行 resetlap
零时被设置为 Map 的原因。
如果是,我怎样才能避免这个问题?并且还需要将 histogram map
从主线程传递给 Task 的构造函数?因为我需要在所有线程完成后打印该 Map 以查看那里有哪些数字。
更新:-
如果我删除除以 1000
以毫秒为单位存储数字,那么我可以看到除 0
之外的一些数字。所以看起来不错。
但我发现的另一件事是数字不一致,如果我总结每个线程时间,我会得到一些数字。而且我还打印了整个程序完成的时间。所以我比较了这两个数字,它们相差很大
最佳答案
为避免秒表出现并发问题,您最好在 Runnable
的 run
方法中创建一个新的秒表作为局部变量。这样每个线程都有自己的秒表。
至于您看到的时间,我绝对希望一个简单的记录插入能在一秒钟内发生。看到 400 次插入每次都在不到一秒内发生,我一点也不惊讶。使用秒表中的毫秒值作为 HashMap 键可能会获得更好的结果。
更新
对于秒表并发问题,我建议如下:
class Task implements Runnable {
private final AtomicInteger id;
// Remove the stopwatch from here
//private StopWatch totalExecTimer = new StopWatch(Task.class.getSimpleName() + ".totalExec");
public static ConcurrentHashMap<Long, AtomicLong> histogram = new ConcurrentHashMap<Long, AtomicLong>();
public Task(AtomicInteger id) {
this.id = id;
}
@Override
public void run() {
// And add it here
StopWatch totalExecTimer = new StopWatch(Task.class.getSimpleName() + ".totalExec");
dbConnection = getDBConnection();
这样每个线程,实际上是每个任务,都有自己的副本,你不必担心并发性。按原样使 StopWatch 线程安全可能比它的值(value)更麻烦。
更新 2
话虽如此,您在评论中提到的方法可能会产生更好的结果,因为计时机制的开销较少。
要回答您有关累积线程时间与程序总运行时间之间差异的问题,我会轻率地说:“您期望什么?”。
这里有两个问题。一个是你没有测量每个线程的总运行时间,只是你正在做数据库插入的那一点。
另一个是测量整个应用程序的运行时间没有考虑线程执行时间的任何重叠。即使您正在测量每个任务的总时间,并假设您在多核机器上运行,我预计累积时间会超过程序执行的耗用时间。这就是并行编程的好处。
关于java - 数据库插入需要零秒才能插入,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14764232/