请用一个代码示例说明为什么SimpleDateFormat不是线程安全的。这门课的问题是什么? SimpleDateFormat的格式功能有问题吗? 请给出在课堂上演示此错误的代码。
FastDateFormat是线程安全的。为什么? SimpleDateFormat和FastDateFormat的区别是什么?
请用一个代码说明这个问题?
请用一个代码示例说明为什么SimpleDateFormat不是线程安全的。这门课的问题是什么? SimpleDateFormat的格式功能有问题吗? 请给出在课堂上演示此错误的代码。
FastDateFormat是线程安全的。为什么? SimpleDateFormat和FastDateFormat的区别是什么?
请用一个代码说明这个问题?
当前回答
下面的示例将SimpleDateFormat对象定义为静态字段。当两个或多个线程以不同的日期并发地访问“someMethod”时,它们可能会扰乱彼此的结果。
public class SimpleDateFormatExample {
private static final SimpleDateFormat simpleFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
public String someMethod(Date date) {
return simpleFormat.format(date);
}
}
您可以创建如下所示的服务,并使用jmeter模拟并发用户,使用相同的SimpleDateFormat对象格式化不同的日期,结果将是混乱的。
public class FormattedTimeHandler extends AbstractHandler {
private static final String OUTPUT_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss.SSS";
private static final String INPUT_TIME_FORMAT = "yyyy-MM-ddHH:mm:ss";
private static final SimpleDateFormat simpleFormat = new SimpleDateFormat(OUTPUT_TIME_FORMAT);
// apache commons lang3 FastDateFormat is threadsafe
private static final FastDateFormat fastFormat = FastDateFormat.getInstance(OUTPUT_TIME_FORMAT);
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
response.setContentType("text/html;charset=utf-8");
response.setStatus(HttpServletResponse.SC_OK);
baseRequest.setHandled(true);
final String inputTime = request.getParameter("time");
Date date = LocalDateTime.parse(inputTime, DateTimeFormat.forPattern(INPUT_TIME_FORMAT)).toDate();
final String method = request.getParameter("method");
if ("SimpleDateFormat".equalsIgnoreCase(method)) {
// use SimpleDateFormat as a static constant field, not thread safe
response.getWriter().println(simpleFormat.format(date));
} else if ("FastDateFormat".equalsIgnoreCase(method)) {
// use apache commons lang3 FastDateFormat, thread safe
response.getWriter().println(fastFormat.format(date));
} else {
// create new SimpleDateFormat instance when formatting date, thread safe
response.getWriter().println(new SimpleDateFormat(OUTPUT_TIME_FORMAT).format(date));
}
}
public static void main(String[] args) throws Exception {
// embedded jetty configuration, running on port 8090. change it as needed.
Server server = new Server(8090);
server.setHandler(new FormattedTimeHandler());
server.start();
server.join();
}
}
代码和jmeter脚本可以在这里下载。
其他回答
SimpleDateFormat将中间结果存储在实例字段中。因此,如果一个实例被两个线程使用,它们会混淆彼此的结果。
查看源代码可以发现有一个Calendar实例字段,该字段由DateFormat / SimpleDateFormat上的操作使用。
例如,parse(..)首先调用calendar.clear(),然后调用calendar.add(..)。如果另一个线程在第一次调用完成之前调用parse(..),它将清除日历,但其他调用将期望用计算的中间结果填充日历。
在不考虑线程安全的情况下重用日期格式的一种方法是将它们放在ThreadLocal中——有些库会这样做。这是如果你需要在一个线程中多次使用相同的格式。但如果您使用的是servlet容器(有线程池),请记得在完成后清理线程本地。
说实话,我不明白为什么他们需要实例字段,但这就是它的方式。你也可以使用线程安全的joda-time DateTimeFormat。
SimpleDateFormat是一个具体类,用于以区域设置敏感的方式格式化和解析日期。
在JavaDoc中,
但是日期格式是不同步的。建议创建 为每个线程分离格式实例。如果多个线程访问 一种并发格式,它必须在外部同步。
要使SimpleDateFormat类线程安全,请查看以下方法:
每次需要使用SimpleDateFormat实例时,都创建一个新的SimpleDateFormat实例。虽然这是线程安全的,但它是最慢的方法。 使用同步。这是一个坏主意,因为您不应该在服务器上阻塞线程。 使用ThreadLocal。这是3种方法中最快的(见http://www.javacodegeeks.com/2010/07/java-best-practices-dateformat-in.html)。
下面的示例将SimpleDateFormat对象定义为静态字段。当两个或多个线程以不同的日期并发地访问“someMethod”时,它们可能会扰乱彼此的结果。
public class SimpleDateFormatExample {
private static final SimpleDateFormat simpleFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
public String someMethod(Date date) {
return simpleFormat.format(date);
}
}
您可以创建如下所示的服务,并使用jmeter模拟并发用户,使用相同的SimpleDateFormat对象格式化不同的日期,结果将是混乱的。
public class FormattedTimeHandler extends AbstractHandler {
private static final String OUTPUT_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss.SSS";
private static final String INPUT_TIME_FORMAT = "yyyy-MM-ddHH:mm:ss";
private static final SimpleDateFormat simpleFormat = new SimpleDateFormat(OUTPUT_TIME_FORMAT);
// apache commons lang3 FastDateFormat is threadsafe
private static final FastDateFormat fastFormat = FastDateFormat.getInstance(OUTPUT_TIME_FORMAT);
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
response.setContentType("text/html;charset=utf-8");
response.setStatus(HttpServletResponse.SC_OK);
baseRequest.setHandled(true);
final String inputTime = request.getParameter("time");
Date date = LocalDateTime.parse(inputTime, DateTimeFormat.forPattern(INPUT_TIME_FORMAT)).toDate();
final String method = request.getParameter("method");
if ("SimpleDateFormat".equalsIgnoreCase(method)) {
// use SimpleDateFormat as a static constant field, not thread safe
response.getWriter().println(simpleFormat.format(date));
} else if ("FastDateFormat".equalsIgnoreCase(method)) {
// use apache commons lang3 FastDateFormat, thread safe
response.getWriter().println(fastFormat.format(date));
} else {
// create new SimpleDateFormat instance when formatting date, thread safe
response.getWriter().println(new SimpleDateFormat(OUTPUT_TIME_FORMAT).format(date));
}
}
public static void main(String[] args) throws Exception {
// embedded jetty configuration, running on port 8090. change it as needed.
Server server = new Server(8090);
server.setHandler(new FormattedTimeHandler());
server.start();
server.join();
}
}
代码和jmeter脚本可以在这里下载。
Java 8中的DateTimeFormatter是不可变的线程安全的SimpleDateFormat的替代品。
下面这个例子会导致一个奇怪的错误。甚至谷歌也没有给出结果:
public class ExampleClass {
private static final Pattern dateCreateP = Pattern.compile("Дата подачи:\\s*(.+)");
private static final SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss dd.MM.yyyy");
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(100);
while (true) {
executor.submit(new Runnable() {
@Override
public void run() {
workConcurrently();
}
});
}
}
public static void workConcurrently() {
Matcher matcher = dateCreateP.matcher("Дата подачи: 19:30:55 03.05.2015");
Timestamp startAdvDate = null;
try {
if (matcher.find()) {
String dateCreate = matcher.group(1);
startAdvDate = new Timestamp(sdf.parse(dateCreate).getTime());
}
} catch (Throwable th) {
th.printStackTrace();
}
System.out.print("OK ");
}
}
结果是:
OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK java.lang.NumberFormatException: For input string: ".201519E.2015192E2"
at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:2043)
at sun.misc.FloatingDecimal.parseDouble(FloatingDecimal.java:110)
at java.lang.Double.parseDouble(Double.java:538)
at java.text.DigitList.getDouble(DigitList.java:169)
at java.text.DecimalFormat.parse(DecimalFormat.java:2056)
at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1869)
at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1514)
at java.text.DateFormat.parse(DateFormat.java:364)
at com.nonscalper.webscraper.processor.av.ExampleClass.workConcurrently(ExampleClass.java:37)
at com.nonscalper.webscraper.processor.av.ExampleClass$1.run(ExampleClass.java:25)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)