Java의 SimpleDateFormat이 스레드 세이프가 아닌 이유는 무엇입니까?
코드 예제를 사용하여 SimpleDateFormat이 스레드 세이프가 아닌 이유를 설명하십시오.이 수업의 문제는 무엇입니까?SimpleDateFormat 포맷 기능에 문제가 있습니까?수업 중에 이 고장을 나타내는 코드를 알려주세요.
FastDateFormat은 스레드 세이프입니다.SimpleDateFormat과 FastDateFormat의 차이점은 무엇입니까?
이 문제를 나타내는 코드로 설명해 주시겠습니까?
SimpleDateFormat는 인스턴스 필드에 중간 결과를 저장합니다.따라서 하나의 인스턴스가 2개의 스레드에서 사용되면 서로 결과를 혼동할 수 있습니다.
소스코드를 살펴보면Calendarinstance 필드 - 조작에 의해 사용됩니다.DateFormat/SimpleDateFormat.
예를들면parse(..)콜calendar.clear()처음에 그리고 나서calendar.add(..). 다른 스레드가 호출된 경우parse(..)첫 번째 호출이 완료되기 전에 캘린더가 클리어되지만 다른 호출은 계산의 중간 결과로 채워질 것으로 예상합니다.
스레드 안전성을 바꾸지 않고 날짜 형식을 재사용하는 방법 중 하나는 데이터 형식을ThreadLocal- 일부 라이브러리는 그렇게 합니다.이는 하나의 스레드 내에서 동일한 형식을 여러 번 사용해야 하는 경우입니다.그러나 (스레드 풀이 있는) 서블릿컨테이너를 사용하는 경우 종료 후 스레드 로컬을 청소해야 합니다.
솔직히 왜 인스턴스 필드가 필요한지 이해가 안 되는데, 그게 바로 그런 거예요.joda-time을 사용할 수도 있습니다. DateTimeFormat스레드 세이프죠.
SimpleDateFormat 는 로케일 구분 방식으로 날짜를 포맷 및 해석하기 위한 구체적인 클래스입니다.
에서,
그러나 날짜 형식은 동기화되지 않습니다.각 스레드에 대해 별도의 형식 인스턴스를 생성하는 것이 좋습니다.여러 스레드가 동시에 형식에 액세스할 경우,
it must be synchronized externally.
SimpleDateFormat 클래스를 스레드로 보호하려면 다음 방법을 참조하십시오.
- SimpleDateFormat 인스턴스를 사용해야 할 때마다 새 인스턴스를 만듭니다.이것은 스레드 세이프이지만, 가능한 한 가장 느린 방법입니다.
- 동기화를 사용합니다.서버상에서 스레드를 초크 포인트로 하지 말아 주세요.
- 스레드 로컬을 사용합니다.이것이 3가지 방법 중 가장 빠른 방법입니다(http://www.javacodegeeks.com/2010/07/java-best-practices-dateformat-in.html) 참조).
DateTimeFormatter Java 8은 에 대해 불변하며 스레드 세이프한 대체 수단입니다.
스레드 로컬 + SimpleDateFormat = SimpleDateFormat스레드 세이프
package com.foocoders.text;
import java.text.AttributedCharacterIterator;
import java.text.DateFormatSymbols;
import java.text.FieldPosition;
import java.text.NumberFormat;
import java.text.ParseException;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
public class SimpleDateFormatThreadSafe extends SimpleDateFormat {
private static final long serialVersionUID = 5448371898056188202L;
ThreadLocal<SimpleDateFormat> localSimpleDateFormat;
public SimpleDateFormatThreadSafe() {
super();
localSimpleDateFormat = new ThreadLocal<SimpleDateFormat>() {
protected SimpleDateFormat initialValue() {
return new SimpleDateFormat();
}
};
}
public SimpleDateFormatThreadSafe(final String pattern) {
super(pattern);
localSimpleDateFormat = new ThreadLocal<SimpleDateFormat>() {
protected SimpleDateFormat initialValue() {
return new SimpleDateFormat(pattern);
}
};
}
public SimpleDateFormatThreadSafe(final String pattern, final DateFormatSymbols formatSymbols) {
super(pattern, formatSymbols);
localSimpleDateFormat = new ThreadLocal<SimpleDateFormat>() {
protected SimpleDateFormat initialValue() {
return new SimpleDateFormat(pattern, formatSymbols);
}
};
}
public SimpleDateFormatThreadSafe(final String pattern, final Locale locale) {
super(pattern, locale);
localSimpleDateFormat = new ThreadLocal<SimpleDateFormat>() {
protected SimpleDateFormat initialValue() {
return new SimpleDateFormat(pattern, locale);
}
};
}
public Object parseObject(String source) throws ParseException {
return localSimpleDateFormat.get().parseObject(source);
}
public String toString() {
return localSimpleDateFormat.get().toString();
}
public Date parse(String source) throws ParseException {
return localSimpleDateFormat.get().parse(source);
}
public Object parseObject(String source, ParsePosition pos) {
return localSimpleDateFormat.get().parseObject(source, pos);
}
public void setCalendar(Calendar newCalendar) {
localSimpleDateFormat.get().setCalendar(newCalendar);
}
public Calendar getCalendar() {
return localSimpleDateFormat.get().getCalendar();
}
public void setNumberFormat(NumberFormat newNumberFormat) {
localSimpleDateFormat.get().setNumberFormat(newNumberFormat);
}
public NumberFormat getNumberFormat() {
return localSimpleDateFormat.get().getNumberFormat();
}
public void setTimeZone(TimeZone zone) {
localSimpleDateFormat.get().setTimeZone(zone);
}
public TimeZone getTimeZone() {
return localSimpleDateFormat.get().getTimeZone();
}
public void setLenient(boolean lenient) {
localSimpleDateFormat.get().setLenient(lenient);
}
public boolean isLenient() {
return localSimpleDateFormat.get().isLenient();
}
public void set2DigitYearStart(Date startDate) {
localSimpleDateFormat.get().set2DigitYearStart(startDate);
}
public Date get2DigitYearStart() {
return localSimpleDateFormat.get().get2DigitYearStart();
}
public StringBuffer format(Date date, StringBuffer toAppendTo, FieldPosition pos) {
return localSimpleDateFormat.get().format(date, toAppendTo, pos);
}
public AttributedCharacterIterator formatToCharacterIterator(Object obj) {
return localSimpleDateFormat.get().formatToCharacterIterator(obj);
}
public Date parse(String text, ParsePosition pos) {
return localSimpleDateFormat.get().parse(text, pos);
}
public String toPattern() {
return localSimpleDateFormat.get().toPattern();
}
public String toLocalizedPattern() {
return localSimpleDateFormat.get().toLocalizedPattern();
}
public void applyPattern(String pattern) {
localSimpleDateFormat.get().applyPattern(pattern);
}
public void applyLocalizedPattern(String pattern) {
localSimpleDateFormat.get().applyLocalizedPattern(pattern);
}
public DateFormatSymbols getDateFormatSymbols() {
return localSimpleDateFormat.get().getDateFormatSymbols();
}
public void setDateFormatSymbols(DateFormatSymbols newFormatSymbols) {
localSimpleDateFormat.get().setDateFormatSymbols(newFormatSymbols);
}
public Object clone() {
return localSimpleDateFormat.get().clone();
}
public int hashCode() {
return localSimpleDateFormat.get().hashCode();
}
public boolean equals(Object obj) {
return localSimpleDateFormat.get().equals(obj);
}
}
https://gist.github.com/pablomoretti/9748230
.2 / release .commons-lang 가지다FastDateParser 대체 SimpleDateFormat그레고리력으로.상세한 것에 대하여는, 을 참조해 주세요.
다음은 이상한 오류가 발생하는 예입니다.Google조차 결과를 제공하지 않습니다.
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)
다음 예제에서는 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 스크립트는 여기서 다운로드 할 수 있습니다.
다음은 클래스의 장애를 증명하는 코드 예시입니다.확인했습니다.파스를 사용할 때와 포맷만 사용할 때 문제가 발생합니다.
여러 스레드 간에 동일한 날짜 형식을 사용하려면 이를 정적으로 선언하고 사용할 때 인스턴스 변수를 동기화하십시오.
static private SimpleDateFormat sdf = new SimpleDateFormat("....");
synchronized(sdf)
{
// use the instance here to format a date
}
// The above makes it thread safe
언급URL : https://stackoverflow.com/questions/6840803/why-is-javas-simpledateformat-not-thread-safe
'source' 카테고리의 다른 글
| Deque over Stack을 사용해야 하는 이유 (0) | 2022.08.11 |
|---|---|
| Java 순서 맵 (0) | 2022.08.11 |
| Java 비트맵을 바이트 배열로 변환 (0) | 2022.08.11 |
| Store vuex가 정의되지 않은 속성 'store'를 읽을 수 없습니다." (0) | 2022.08.10 |
| libcurl 없이 C에서 HTTP get 요구를 작성하려면 어떻게 해야 합니까? (0) | 2022.08.10 |