一个比较纠结的问题,SimpleDateFormat这个类是线程不安全的,在多线程的并发下极其容易出现问题,每次使用的时候都要新建一个对象太消耗资源。这段时间正好在使用REDIS,REDIS底层是使用的pool2来实现的,所以考虑使用pool2对SimpleDateFormat做一个缓存池,代码如下。
package tests.pool2;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.apache.commons.pool2.BasePooledObjectFactory;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.impl.DefaultPooledObject;
import org.apache.commons.pool2.impl.GenericObjectPool;
public enum DateFormatEnum {
yyyyMMdd("yyyyMMdd"),
yyyyMMddWithMinus("yyyy-MM-dd"),
yyyyMMddWithSpace("yyyy MM dd");
private GenericObjectPool<SimpleDateFormat> pool;
private DateFormatEnum(final String pattern) {
this.pool = new GenericObjectPool<SimpleDateFormat>(new BasePooledObjectFactory<SimpleDateFormat>(){
@Override
public SimpleDateFormat create() throws Exception {
return new SimpleDateFormat(pattern);
}
@Override
public PooledObject<SimpleDateFormat> wrap(SimpleDateFormat obj) {
return new DefaultPooledObject<SimpleDateFormat>(obj);
}
});
}
public String convert(Date date){
SimpleDateFormat sdf = null;
try {
sdf = pool.borrowObject();
return sdf.format(date);
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
if(null != sdf){
pool.returnObject(sdf);
}
}
}
public Date convert(String date){
SimpleDateFormat sdf = null;
try {
sdf = pool.borrowObject();
return sdf.parse(date);
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
if(null != sdf){
pool.returnObject(sdf);
}
}
}
}
测试代码如下,没有出现报错了。
oldFashion为错误写法,enumStyle为使用枚举连接池写法。
package tests.pool2;
import java.text.SimpleDateFormat;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.junit.Test;
public class DateTest {
protected static final int COUNTERS = 100000;
@Test
public void oldFashion() throws InterruptedException, ExecutionException, TimeoutException {
final SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
final String pattern = "20160504";
ExecutorService exec = Executors.newFixedThreadPool(3);
Runnable runnable = new Runnable() {
@Override
public void run() {
for (int i = 0; i < COUNTERS; i++) {
try {
String pattern2 = sdf.format(sdf.parse(pattern));
if (!pattern.equals(pattern2)) {
throw new RuntimeException("oooooooops...." + pattern2);
}
} catch (Exception e) {
e.printStackTrace();
}
}
System.out.println("======over");
}
};
exec.execute(runnable);
exec.execute(runnable);
exec.execute(runnable);
exec.shutdown();
exec.awaitTermination(10, TimeUnit.MINUTES);
}
@Test
public void enumStyle() throws InterruptedException {
final String pattern = "20160504";
ExecutorService exec = Executors.newFixedThreadPool(10);
Runnable runnable = new Runnable() {
@Override
public void run() {
for (int i = 0; i < COUNTERS; i++) {
String pattern2 = DateFormatEnum.yyyyMMdd.convert(DateFormatEnum.yyyyMMdd.convert(pattern));
if (!pattern.equals(pattern2)) {
throw new RuntimeException("oooooooops...." + pattern2);
}
}
System.out.println("======over");
}
};
exec.execute(runnable);
exec.execute(runnable);
exec.execute(runnable);
exec.execute(runnable);
exec.execute(runnable);
exec.execute(runnable);
exec.execute(runnable);
exec.execute(runnable);
exec.execute(runnable);
exec.execute(runnable);
exec.execute(runnable);
exec.execute(runnable);
exec.execute(runnable);
exec.execute(runnable);
exec.execute(runnable);
exec.execute(runnable);
exec.shutdown();
exec.awaitTermination(10, TimeUnit.MINUTES);
}
}