我们知道,SimpleDateFormat不是线程安全的,为了保证多线程下安全使用,我们一般采用如下几种方式:
1、每次都新建SimpleDateFormat
2、SimpleDateFormat对象的方法增加synchronized修饰
3、使用ThreadLocal
4、借助Apache Common Lang包的FastDateFormat
5、借助Joda Time包下的DateTimeFormatter
6、采用Java8 推出的日期处理类LocalDate
有兴趣的读者的可以看早期的文章SimpleDateFormat线程不安全示例及其解决方法
本文采用池化思想,完成一个简单的Pool,复用对象的同时,保证SimpleDateFormat在多线程环境下的安全使用。
定义变量
为实现简单的Pool,定义几个简单的变量:
1、对象池大小
//对象池大小
private final int poolSize;
2、存放池对象的数组
//存放池对象的数组
private Object[] poolObjs;
3、创建池对象的工厂
public interface Factory {
public Object create();
}
//创建池对象的工厂
private final Factory factory;
4、用于多线程获取对象和放回对象互斥的lock对象
// 互斥对象
private Object LOCK = new Object();
5、下一个可用对象的数组下标
//下一个可用对象的下标
private int nextAvailableIndex;
有了这些简单的变量之后,我们就可以采用构造函数初始化变量,如:
public SimplePool(Factory factory) {
//默认对象池大小为5
this(5, factory);
}
public SimplePool(int poolSize, Factory factory) {
this.poolSize = poolSize;
this.factory = factory;
poolObjs = new Object[poolSize];
}
接下来我们只要完成fetchFromPool和putInPool的方法即可。
fetchFromPool方法
该方法的实现步骤如下:
public Object fetchFromPool() {
Object result;
synchronized (LOCK) {
/**
* 1、判断线程池是否都被占用
*/
while (nextAvailableIndex == poolSize) {
// 等待
try {
LOCK.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* 2、从线程池获取对象
*/
result = poolObjs[nextAvailableIndex++];
if (result == null) {
result = factory.create();
this.putInPool(result);
++nextAvailableIndex;
}
}
return result;
}
putInPool方法
该方法的实现步骤如下:
protected void putInPool(Object object) {
synchronized (LOCK) {
poolObjs[--nextAvailableIndex] = object;
LOCK.notify();
}
}
SimplePool完整代码如下
public class SimplePool {
public interface Factory {
public Object create();
}
//对象池大小
private final int poolSize;
private final Factory factory;
public SimplePool(Factory factory) {
//默认对象池大小为5
this(5, factory);
}
public SimplePool(int poolSize, Factory factory) {
this.poolSize = poolSize;
this.factory = factory;
poolObjs = new Object[poolSize];
}
// 互斥对象
private Object LOCK = new Object();
//存放池对象的数组
private Object[] poolObjs;
//下一个可用对象的下标
private int nextAvailableIndex;
public Object fetchFromPool() {
Object result;
synchronized (LOCK) {
/**
* 1、判断线程池是否都被占用
*/
while (nextAvailableIndex == poolSize) {
// 等待
try {
LOCK.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* 2、从线程池获取对象
*/
result = poolObjs[nextAvailableIndex++];
if (result == null) {
result = factory.create();
this.putInPool(result);
++nextAvailableIndex;
}
}
return result;
}
protected void putInPool(Object object) {
synchronized (LOCK) {
poolObjs[--nextAvailableIndex] = object;
LOCK.notify();
}
}
}
有了SimplePool,并且已经保证了对象获取和放回的安全性。所以一个使用SimplePool完成SimpleDateFormat的操作就很简单了,本文取名为PooledSimpleDateFormat。
初始化Pool
private final SimplePool pool;
public PooledSimpleDateFormat(String formatString, int poolSzie) {
this(formatString, poolSzie, false);
}
public PooledSimpleDateFormat(String formatString, int poolSzie, final boolean lenient) {
pool = new SimplePool(poolSzie, new SimplePool.Factory() {
public Object create() {
SimpleDateFormat dateFormat = new SimpleDateFormat(formatString);
dateFormat.setLenient(lenient);
return dateFormat;
}
});
}
parse和format都使用pool中的对象,用完放回去即可
private DateFormat fetchFromPool() {
DateFormat format = (DateFormat) pool.fetchFromPool();
return format;
}
public String format(Date date) {
DateFormat format = fetchFromPool();
try {
return format.format(date);
} finally {
// 用完放回去
pool.putInPool(format);
}
}
public Date parse(String date) throws ParseException {
DateFormat format = fetchFromPool();
try {
return format.parse(date);
} finally {
pool.putInPool(format);
}
}
完整代码
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public final class PooledSimpleDateFormat {
private final SimplePool pool;
public PooledSimpleDateFormat(String formatString, int poolSzie) {
this(formatString, poolSzie, false);
}
public PooledSimpleDateFormat(String formatString, int poolSzie, final boolean lenient) {
pool = new SimplePool(poolSzie, new SimplePool.Factory() {
public Object create() {
SimpleDateFormat dateFormat = new SimpleDateFormat(formatString);
dateFormat.setLenient(lenient);
return dateFormat;
}
});
}
private DateFormat fetchFromPool() {
DateFormat format = (DateFormat) pool.fetchFromPool();
return format;
}
public String format(Date date) {
DateFormat format = fetchFromPool();
try {
return format.format(date);
} finally {
// 用完放回去
pool.putInPool(format);
}
}
public Date parse(String date) throws ParseException {
DateFormat format = fetchFromPool();
try {
return format.parse(date);
} finally {
pool.putInPool(format);
}
}
}
PooledSimpleDateFormat可以直接使用或者封装在Util工具中使用,都是可以的。如:
private static final PooledSimpleDateFormat PSDF = new PooledSimpleDateFormat("yyyy-MM-dd", 10);
//调用 PSDF.parse("2021-09-28")
或者
import java.text.ParseException;
import java.util.Date;
public final class MyUtils {
private static final PooledSimpleDateFormat SAFE_DATE_FORMAT = new PooledSimpleDateFormat("yyyy-MM-dd", 10);
private MyUtils() {
}
public static String format(Date date) {
return SAFE_DATE_FORMAT.format(date);
}
public static Date parse(String date) throws ParseException {
return SAFE_DATE_FORMAT.parse(date);
}
}
//调用 MyUtils.parse("2021-09-28");
测试一下
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class Main {
private static final PooledSimpleDateFormat PSDF = new PooledSimpleDateFormat("yyyy-MM-dd", 10);
public static void main(String[] args) throws Exception {
//format测试
format();
//parse测试
parse();
}
private static void format() throws Exception {
int availableProcessors = Runtime.getRuntime().availableProcessors();
ExecutorService exec = Executors.newFixedThreadPool(availableProcessors);
List<Future<String>> results = new ArrayList<>();
Callable<String> parseDateTask = new Callable<String>() {
public String call() throws Exception {
return MyUtils.format(new Date());
}
};
for (int i = 0; i < 50; i++) {
results.add(exec.submit(parseDateTask));
}
exec.shutdown();
/**
* 查看结果
*/
for (Future<String> result : results) {
System.out.println(result.get());
}
}
private static void parse() throws Exception{
int availableProcessors = Runtime.getRuntime().availableProcessors();
ExecutorService exec = Executors.newFixedThreadPool(availableProcessors);
List<Future<Date>> results = new ArrayList<>();
Callable<Date> parseDateTask = new Callable<Date>() {
public Date call() throws Exception {
//return MyUtils.parse("2021-09-29");
return PSDF.parse("2021-09-29");
}
};
for (int i = 0; i < 50; i++) {
results.add(exec.submit(parseDateTask));
}
exec.shutdown();
/**
* 查看结果
*/
for (Future<Date> result : results) {
System.out.println(result.get());
}
}
}
执行结果:
2021-09-29
2021-09-29
2021-09-29
2021-09-29
2021-09-29
2021-09-29
2021-09-29
2021-09-29
2021-09-29
2021-09-29
2021-09-29
2021-09-29
2021-09-29
2021-09-29
2021-09-29
2021-09-29
2021-09-29
2021-09-29
2021-09-29
2021-09-29
2021-09-29
2021-09-29
2021-09-29
2021-09-29
2021-09-29
2021-09-29
2021-09-29
2021-09-29
2021-09-29
2021-09-29
2021-09-29
2021-09-29
2021-09-29
2021-09-29
2021-09-29
2021-09-29
2021-09-29
2021-09-29
2021-09-29
2021-09-29
2021-09-29
2021-09-29
2021-09-29
2021-09-29
2021-09-29
2021-09-29
2021-09-29
2021-09-29
2021-09-29
2021-09-29
Wed Sep 29 00:00:00 CST 2021
Wed Sep 29 00:00:00 CST 2021
Wed Sep 29 00:00:00 CST 2021
Wed Sep 29 00:00:00 CST 2021
Wed Sep 29 00:00:00 CST 2021
Wed Sep 29 00:00:00 CST 2021
Wed Sep 29 00:00:00 CST 2021
Wed Sep 29 00:00:00 CST 2021
Wed Sep 29 00:00:00 CST 2021
Wed Sep 29 00:00:00 CST 2021
Wed Sep 29 00:00:00 CST 2021
Wed Sep 29 00:00:00 CST 2021
Wed Sep 29 00:00:00 CST 2021
Wed Sep 29 00:00:00 CST 2021
Wed Sep 29 00:00:00 CST 2021
Wed Sep 29 00:00:00 CST 2021
Wed Sep 29 00:00:00 CST 2021
Wed Sep 29 00:00:00 CST 2021
Wed Sep 29 00:00:00 CST 2021
Wed Sep 29 00:00:00 CST 2021
Wed Sep 29 00:00:00 CST 2021
Wed Sep 29 00:00:00 CST 2021
Wed Sep 29 00:00:00 CST 2021
Wed Sep 29 00:00:00 CST 2021
Wed Sep 29 00:00:00 CST 2021
Wed Sep 29 00:00:00 CST 2021
Wed Sep 29 00:00:00 CST 2021
Wed Sep 29 00:00:00 CST 2021
Wed Sep 29 00:00:00 CST 2021
Wed Sep 29 00:00:00 CST 2021
Wed Sep 29 00:00:00 CST 2021
Wed Sep 29 00:00:00 CST 2021
Wed Sep 29 00:00:00 CST 2021
Wed Sep 29 00:00:00 CST 2021
Wed Sep 29 00:00:00 CST 2021
Wed Sep 29 00:00:00 CST 2021
Wed Sep 29 00:00:00 CST 2021
Wed Sep 29 00:00:00 CST 2021
Wed Sep 29 00:00:00 CST 2021
Wed Sep 29 00:00:00 CST 2021
Wed Sep 29 00:00:00 CST 2021
Wed Sep 29 00:00:00 CST 2021
Wed Sep 29 00:00:00 CST 2021
Wed Sep 29 00:00:00 CST 2021
Wed Sep 29 00:00:00 CST 2021
Wed Sep 29 00:00:00 CST 2021
Wed Sep 29 00:00:00 CST 2021
Wed Sep 29 00:00:00 CST 2021
Wed Sep 29 00:00:00 CST 2021
Wed Sep 29 00:00:00 CST 2021
我们可以看到,一个简单的对象池完成的SimpleDateFormat线性安全的示例就完成了。
在上述示例中,我们采用了synchronized + wait 和notify来完成。在Java中,我们完全可以使用J.U.C并发包的Lock和ReentrantLock完成操作。微调一下代码即可,如:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class SimplePool2 {
public interface Factory {
public Object create();
}
private final int poolSize;
private final Factory factory;
public SimplePool2(int poolSize, Factory factory) {
this.poolSize = poolSize;
this.factory = factory;
poolObjs = new Object[poolSize];
}
private Lock lock = new ReentrantLock();
private Object[] poolObjs;
private int nextAvailableIndex;
public Object fetchFromPool() {
Object result;
lock.lock();
try {
/**
* 1、判断线程池是否都被占用
*/
while (nextAvailableIndex == poolSize) {
// 等待
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* 2、从线程池获取对象
*/
result = poolObjs[nextAvailableIndex++];
if (result == null) {
result = factory.create();
this.putInPool(result);
++nextAvailableIndex;
}
}finally {
lock.unlock();
}
return result;
}
protected void putInPool(Object object) {
lock.lock();
try {
poolObjs[--nextAvailableIndex] = object;
}finally {
lock.unlock();
}
}
}
同样,也能正常运行。
本文给出了一个简单的、自定义的对象池的思想,来完成SimpleDateFormat的parse和format操作,能够在多线程的环境下成功测试调用。但是,没有考虑资源的回收、Dsicard策略等等。
有兴趣的读者,可以去进一步实现~