Deb*_*Roy 21 java concurrency performance thread-local simpledateformat
这是在RHEL上的Java 7(51)上有24个内核我们注意到,当我们增加线程池大小时,包含在本地线程中的java SimpleDateFormat的平均响应时间会增加.这是预期的吗?或者,我只是在做一些愚蠢的事情?
测试程序
public class DateFormatterLoadTest {
private static final Logger LOG = Logger.getLogger(DateFormatterLoadTest .class);
private final static int CONCURRENCY = 10;
public static void main(String[] args) throws Exception {
final AtomicLong total = new AtomicLong(0);
ExecutorService es = Executors.newFixedThreadPool(CONCURRENCY);
final CountDownLatch cdl = new CountDownLatch(CONCURRENCY);
for (int i = 0; i < CONCURRENCY; i++) {
es.execute(new Runnable() {
@Override
public void run() {
try {
int size = 65000;
Date d = new Date();
long time = System.currentTimeMillis();
for (int i = 0; i < size; i++) {
String sd = ISODateFormatter.convertDateToString(d);
assert (sd != null);
}
total.addAndGet((System.currentTimeMillis() - time));
} catch (Throwable t) {
t.printStackTrace();
} finally {
cdl.countDown();
}
}
});
}
cdl.await();
es.shutdown();
LOG.info("TOTAL TIME:" + total.get());
LOG.info("AVERAGE TIME:" + (total.get() / CONCURRENCY));
}
}
Run Code Online (Sandbox Code Playgroud)
DateFormatter类:
public class ISODateFormatter {
private static final Logger LOG = Logger.getLogger(ISODateFormatter.class);
private static ThreadLocal<DateFormat> dfWithTZ = new ThreadLocal<DateFormat>() {
@Override
public DateFormat get() {
return super.get();
}
@Override
protected DateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ",
Locale.ENGLISH);
}
@Override
public void remove() {
super.remove();
}
@Override
public void set(DateFormat value) {
super.set(value);
}
};
public static String convertDateToString(Date date) {
if (date == null) {
return null;
}
try {
return dfWithTZ.get().format(date);
} catch (Exception e) {
LOG.error("!!! Error parsing dateString: " + date, e);
return null;
}
}
}
Run Code Online (Sandbox Code Playgroud)
有人建议取出AtomicLong,所以只是想分享它在增加平均时间方面没有任何作用:
##NOT USING ATOMIC LONG##
2014-02-28 11:03:52,790 [pool-1-thread-1] INFO net.ahm.graph.DateFormatterLoadTest - THREAD TIME:328
2014-02-28 11:03:52,868 [pool-1-thread-6] INFO net.ahm.graph.DateFormatterLoadTest - THREAD TIME:406
2014-02-28 11:03:52,821 [pool-1-thread-2] INFO net.ahm.graph.DateFormatterLoadTest - THREAD TIME:359
2014-02-28 11:03:52,821 [pool-1-thread-8] INFO net.ahm.graph.DateFormatterLoadTest - THREAD TIME:359
2014-02-28 11:03:52,868 [pool-1-thread-4] INFO net.ahm.graph.DateFormatterLoadTest - THREAD TIME:406
2014-02-28 11:03:52,915 [pool-1-thread-5] INFO net.ahm.graph.DateFormatterLoadTest - THREAD TIME:453
2014-02-28 11:03:52,930 [pool-1-thread-7] INFO net.ahm.graph.DateFormatterLoadTest - THREAD TIME:468
2014-02-28 11:03:52,930 [pool-1-thread-3] INFO net.ahm.graph.DateFormatterLoadTest - THREAD TIME:468
2014-02-28 11:03:52,930 [main] INFO net.ahm.graph.DateFormatterLoadTest - CONCURRENCY:8
##USING ATOMIC LONG##
2014-02-28 11:02:53,852 [main] INFO net.ahm.graph.DateFormatterLoadTest - TOTAL TIME:2726
2014-02-28 11:02:53,852 [main] INFO net.ahm.graph.DateFormatterLoadTest - CONCURRENCY:8
2014-02-28 11:02:53,852 [main] INFO net.ahm.graph.DateFormatterLoadTest - AVERAGE TIME:340
##NOT USING ATOMIC LONG##
2014-02-28 11:06:57,980 [pool-1-thread-3] INFO net.ahm.graph.DateFormatterLoadTest - THREAD TIME:312
2014-02-28 11:06:58,339 [pool-1-thread-8] INFO net.ahm.graph.DateFormatterLoadTest - THREAD TIME:671
2014-02-28 11:06:58,339 [pool-1-thread-4] INFO net.ahm.graph.DateFormatterLoadTest - THREAD TIME:671
2014-02-28 11:06:58,307 [pool-1-thread-7] INFO net.ahm.graph.DateFormatterLoadTest - THREAD TIME:639
2014-02-28 11:06:58,261 [pool-1-thread-6] INFO net.ahm.graph.DateFormatterLoadTest - THREAD TIME:593
2014-02-28 11:06:58,105 [pool-1-thread-15] INFO net.ahm.graph.DateFormatterLoadTest - THREAD TIME:437
2014-02-28 11:06:58,089 [pool-1-thread-13] INFO net.ahm.graph.DateFormatterLoadTest - THREAD TIME:421
2014-02-28 11:06:58,073 [pool-1-thread-1] INFO net.ahm.graph.DateFormatterLoadTest - THREAD TIME:405
2014-02-28 11:06:58,073 [pool-1-thread-12] INFO net.ahm.graph.DateFormatterLoadTest - THREAD TIME:405
2014-02-28 11:06:58,042 [pool-1-thread-14] INFO net.ahm.graph.DateFormatterLoadTest - THREAD TIME:374
2014-02-28 11:06:57,995 [pool-1-thread-2] INFO net.ahm.graph.DateFormatterLoadTest - THREAD TIME:327
2014-02-28 11:06:57,995 [pool-1-thread-16] INFO net.ahm.graph.DateFormatterLoadTest - THREAD TIME:327
2014-02-28 11:06:58,385 [pool-1-thread-10] INFO net.ahm.graph.DateFormatterLoadTest - THREAD TIME:717
2014-02-28 11:06:58,385 [pool-1-thread-11] INFO net.ahm.graph.DateFormatterLoadTest - THREAD TIME:717
2014-02-28 11:06:58,417 [pool-1-thread-9] INFO net.ahm.graph.DateFormatterLoadTest - THREAD TIME:749
2014-02-28 11:06:58,418 [pool-1-thread-5] INFO net.ahm.graph.DateFormatterLoadTest - THREAD TIME:750
2014-02-28 11:06:58,418 [main] INFO net.ahm.graph.DateFormatterLoadTest - CONCURRENCY:16
##USING ATOMIC LONG##
2014-02-28 11:07:57,510 [main] INFO net.ahm.graph.DateFormatterLoadTest - TOTAL TIME:9365
2014-02-28 11:07:57,510 [main] INFO net.ahm.graph.DateFormatterLoadTest - CONCURRENCY:16
2014-02-28 11:07:57,510 [main] INFO net.ahm.graph.DateFormatterLoadTest - AVERAGE TIME:585
Run Code Online (Sandbox Code Playgroud)
SimpleDateFormat
不是线程安全的正如Martin Wilson所说的正确答案,实例化SimpleDateFormat相对昂贵.
知道你的第一个想法可能是,"好吧,让我们缓存一个实例以便重复使用." 很好的想法,但要注意:SimpleDateFormat类不是线程安全的.所以在同步标题下的类文档说.
一个更好的解决方案是避免臭名昭着(现在已经过时)的java.util.Date,.Calendar和SimpleDateFormat类.而是使用:
Joda-Time有意构建为线程安全的,主要是通过使用不可变对象.有一些可变类,但通常不使用.
这在计算器上的其他问题解释说,这个DateTimeFormatter
班确实是线程安全的.因此,您可以创建一个实例,对其进行缓存,并让所有线程使用该格式化程序,而无需添加任何额外的同步或其他并发控制.
另一种加速格式化的方法是缓存格式化结果.这考虑了这样一个事实,即格式通常没有那么多不同的日期.如果您拆分日期和时间的格式,它甚至是更好的缓存候选者.
这样做的缺点是,正常的Java缓存实现(如EHCache)会变慢,缓存访问只需要比格式化更长的时间.
还有另一个缓存实现,其访问时间与HashMap相同.在这种情况下,你获得了很好的加速.在这里,您可以找到我的概念验证测试:https://github.com/headissue/cache2k-benchmark/blob/master/zoo/src/test/java/org/cache2k/benchmark/DateFormattingBenchmark.java
也许这可以成为您方案中的解决方案.
免责声明:我正在使用cache2k ....
归档时间: |
|
查看次数: |
10765 次 |
最近记录: |