Java 使SimpleDataFormat线程安全
我有许多线程处理Java 使SimpleDataFormat线程安全,java,multithreading,thread-safety,simpledateformat,Java,Multithreading,Thread Safety,Simpledateformat,我有许多线程处理Trade对象,其中我使用RowMapper将数据库列映射到Trade对象 我知道在任何Java中,SimpleDataFormat都不是线程安全的。因此,我在startDate中得到了一些不可预测的结果。例如,我在startDate中也看到了日期endDate 这是我的密码: public class ExampleTradeMapper { private static final SimpleDateFormat DATE_FORMAT = new SimpleD
Trade
对象,其中我使用RowMapper
将数据库列映射到Trade
对象
我知道在任何Java中,SimpleDataFormat
都不是线程安全的。因此,我在startDate
中得到了一些不可预测的结果。例如,我在startDate
中也看到了日期endDate
这是我的密码:
public class ExampleTradeMapper {
private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("dd-MMM-yyyy");
public void map(Trade trade, ResultSet rs, int rowNum) throws SQLException {
trade.setStartDate(getFormattedDate(rs.getDate("START_DATE")));
trade.setEndDate(getFormattedDate(rs.getDate("END_DATE")));
trade.setDescription(rs.getString("DESCRIPTION"));
}
private String getFormattedDate(Date date) {
try {
if (date != null)
return DATE_FORMAT.format(date).toUpperCase();
else
return null;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
public class SomeRowMapper extends TradeMapper implements RowMapper<Trade> {
@Override
public Trade mapRow(ResultSet rs, int rowNum) throws SQLException {
Trade trade = new Trade();
map(trade, rs, rowNum);
return trade;
}
}
公共类示例TradeMapper{
私有静态最终SimpleDataFormat日期\格式=新SimpleDataFormat(“dd MMM yyyy”);
public void映射(Trade、ResultSet rs、int rowNum)抛出SQLException{
交易开始日期(getFormattedDate(rs.getDate(“开始日期”));
trade.setEndDate(getFormattedDate(rs.getDate(“结束日期”));
trade.setDescription(rs.getString(“DESCRIPTION”);
}
私有字符串getFormattedDate(日期){
试一试{
如果(日期!=null)
返回日期_FORMAT.FORMAT(DATE).toUpperCase();
其他的
返回null;
}捕获(例外e){
e、 printStackTrace();
}
返回null;
}
}
公共类SomeRowMapper扩展TradeMapper实现RowMapper{
@凌驾
public Trade mapRow(结果集rs,int rowNum)引发SQLException{
贸易=新贸易();
地图(贸易、遥感、rowNum);
退货贸易;
}
}
对于这个应用程序,我的核心池大小约为20,最大约为50。这些线程可以在某个时间处理来自数据库的大约100条交易记录
使此日期格式化线程安全的最佳方法是什么?我是否应该使用直接替换
有没有更好的替代方法来确保线程安全 你能做到。池中的每个线程都将拥有自己的格式化程序
private static final ThreadLocal<SimpleDateFormat> DATE_FORMAT = new ThreadLocal<SimpleDateFormat>() {
@Override
protected SimpleDateFormat initialValue() {
return new SimpleDateFormat("dd-MMM-yyyy");
}
};
private static final ThreadLocal DATE\u FORMAT=new ThreadLocal(){
@凌驾
受保护的SimpleDataFormat初始值(){
返回新的SimpleDataFormat(“dd-MMM-yyyy”);
}
};
tl;博士
不要使用字符串,而是使用通过JDBC 4.2或更高版本与数据库交换的java.time对象(LocalDate
)
myResultSet.getObject( // Exchange modern java.time objects with your database.
"START_DATE" ,
LocalDate.class
) // Returns a `LocalDate` object.
.format( // Generate a `String` representing textually the content of this `LocalDate`.
DateTimeFormatter.ofPattern( "dd-MMM-uuuu" , Locale.US )
)
2018年1月23日
作为不可变对象,java.time对象在设计上是线程安全的。您可以缓存java.time对象,以便跨线程使用
java.time
使SimpleDataFormat线程安全
不要
使用几年前取代了麻烦的旧遗留日期时间类的现代java.time类,如SimpleDateFormat
、java.util.date
、java.sql.date
和Calendar
time类被设计成线程安全的。它们使用模式,根据原始对象的值返回新对象,而不是“修改”(mutation)原始对象
使用智能对象,而不是哑字符串
我看不出在示例代码中使用字符串的理由:不在数据库访问代码中,也不在业务对象(Trade
)中
JDBC
从JDBC4.2开始,我们可以与数据库交换java.time对象。对于类似于SQL标准DATE
类型的数据库列,请使用类LocalDate
。该类表示一个仅限日期的值,不包含一天中的时间和时区
myPreparedStatement.setObject( … , myLocalDate ) ;
检索
LocalDate myLocalDate = myResultSet.getObject( … , LocalDate.class ) ;
业务对象
您的Trade
类应该将成员变量startDate
和endDate
作为LocalDate
对象而不是字符串来保存
public class Trade {
private LocalDate startDate ;
private LocalDate endDate ;
…
// Getters
public LocalDate getStartDate() {
return this.startDate ;
}
public LocalDate getEndDate() {
return this.endDate;
}
public Period getPeriod() { // Number of years-months-days elapsed.
return Period.between( this.startDate , this.endDate ) ;
}
// Setters
public void setStartDate( LocalDate startDateArg ) {
this.startDate = startDateArg ;
}
public void setEndDate( LocalDate endDateArg ) {
this.endDate = endDateArg ;
}
@Override
public toString() {
"Trade={ " + "startDate=" + this.startDate.toString() …
}
…
}
不需要字符串,也不需要格式化模式
串
要将日期时间值交换或存储为文本,请使用标准格式,而不是问题中显示的自定义格式
在解析/生成字符串时,java.time类默认使用ISO 8601格式。因此,无需指定格式化模式
LocalDate ld = LocalDate.parse( "2018-01-23" ) ; // January 23, 2018.
String s = ld.toString() ; // Outputs 2018-01-23.
对于用户界面中的表示,让java.time自动本地化。要本地化,请指定:
- 确定字符串的长度或缩写
- 确定:
- 翻译日名、月名等的人类语言
- 决定缩写、大写、标点、分隔符等问题的文化规范
Locale l = Locale.CANADA_FRENCH ;
DateTimeFormatter f =
DateTimeFormatter.ofLocalizedDate( FormatStyle.FULL )
.withLocale( l ) ;
String output = ld.format( f ) ;
2018年1月23日
根据设计,DateTimeFormatter
类作为不可变对象是线程安全的。您可以保存一个实例以跨线程使用
关于java.time 该框架内置于Java8及更高版本中。这些类取代了麻烦的旧日期时间类,例如,& 该项目现已启动,建议迁移到类 要了解更多信息,请参阅。并搜索堆栈溢出以获得许多示例和解释。规格是 您可以直接与数据库交换java.time对象。使用兼容的或更高版本。不需要字符串,也不需要
java.sql.*
类
从哪里获得java.time类
- 然后
- 内置的李>
- 标准JavaAPI的一部分,带有捆绑实现
- Java9添加了一些次要功能和修复
- 及
- 大部分java.time功能都在中向后移植到Java6和Java7
-
- 更高版本的Android捆绑包实现了java.time类
- 对于早期的Android(),您可以在这里看到以线程安全方式使用日期格式的最快方法。因为您有3种方法:
- 使用
DateFormat.getDateInstance()
- 与
同步
- 还有本地线程方式,它提供了最好的性能 完整代码示例:
import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class SimpleDateFormatThreadExample { private static String FORMAT = "dd-M-yyyy hh:mm:ss"; private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat(FORMAT); public static void main(String[] args) { final String dateStr = "02-1-2018 06:07:59"; ExecutorService executorService = Executors.newFixedThreadPool(10); Runnable task = new Runnable() { @Override public void run() { parseDate(dateStr); } }; Runnable taskInThread = new Runnable() { @Override public void run() { try { ConcurrentDateFormatAccess concurrentDateFormatAccess = new ConcurrentDateFormatAccess(); System.out.println("Successfully Parsed Date " + concurrentDateFormatAccess.convertStringToDate(dateStr)); // don't forget to use CLEAN because the classloader with keep date format ! concurrentDateFormatAccess.clean(); } catch (ParseException e) { e.printStackTrace(); } } }; for (int i = 0; i < 100; i++) { executorService.submit(task); // remove this comment to use thread safe way ! // executorService.submit(taskInThread); } executorService.shutdown(); } private static void parseDate(String dateStr) { try { Date date = simpleDateFormat.parse(dateStr); System.out.println("Successfully Parsed Date " + date); } catch (ParseException e) { System.out.println("ParseError " + e.getMessage()); } catch (Exception e) { e.printStackTrace(); } } public static class ConcurrentDateFormatAccess { private ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>() { @Override public DateFormat get() { return super.get(); } @Override protected DateFormat initialValue() { return new SimpleDateFormat(FORMAT); } @Override public void remove() { super.remove(); } @Override public void set(DateFormat value) { super.set(value); } }; public void clean() { df.remove(); } public Date convertStringToDate(String dateString) throws ParseException { return df.get().parse(dateString); } } }
导入java.text.DateFormat; 导入java.text.ParseException; 导入java.text.simpleDataFormat; 导入java.util.Date; 导入java.util.concurrent.ExecutorService; 导入java.util