본문 바로가기

Languages/Java

Thread-Safe하지 않은 자바의 SimpleDateFormat

반응형

이미지 출처 : Pixabay

Java의 SimpleDateFormat은 절대 Simple하지 않습니다.

멀티 스레드 환경이면 더더욱이요.

 

다음과 같은 코드를 실행해 봅니다.

import java.text.SimpleDateFormat
import java.util.Date

import scala.concurrent._
import ExecutionContext.Implicits.global

val format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
def fmtFuture(dateStr: String): Future[Date] = Future {
  format.parse(dateStr)
}

val parseList: Seq[Future[Date]] = (1 to 10).map(i => "2021-01-18 20:00:0" + i % 10).map(ts => fmtFuture(ts))
parseList foreach println
Thread.sleep(3000)

 

실행 결과는 다음과 같습니다 : 

import java.text.SimpleDateFormat
import java.util.Date import scala.concurrent._
import ExecutionContext.Implicits.global

format: java.text.SimpleDateFormat = java.text.SimpleDateFormat@4f76f1a0
fmtFuture: (dateStr: String)scala.concurrent.Future[java.util.Date]

parseList: Seq[scala.concurrent.Future[java.util.Date]] = Vector(Future(<not completed>), Future(<not completed>), Future(<not completed>), Future(<not completed>), Future(<not completed>), Future(<not completed>), Future(<not completed>), Future(<not completed>), Future(<not completed>), Future(<not completed>))

Future(Success(Wed Jan 18 07:33:41 KST 2))
Future(Success(Mon Jan 18 20:00:02 KST 2021))
Future(Success(Mon Jan 18 20:00:02 KST 2021))
Future(Success(Wed Jan 18 07:33:41 KST 2))
Future(Success(Mon Jan 18 20:00:05 KST 2021))
Future(Success(Mon Jan 18 20:00:06 KST 2021))
Future(Success(Mon Jan 18 20:00:07 KST 2021))
Future(Failure(java.lang.NumberFormatException: For input string: ""))
Future(Success(Fri Jul 09 20:00:01 KST 202))
Future(Success(Mon Jan 18 20:00:00 KST 2021))

 

띠용 전혀 의도치 않은 결과가 나왔습니다.

2021년 1월 18일 20시 00분 00~09초의 시간대가 입력되었을텐데, 출력물은 온갖 이상한 날짜로 변해있습니다.

 

이는 Java의 SimpleDateFormat이 Thread-Safe하지 않기 때문입니다.

 

SimpleDateFormat (Java Platform SE 7 )

Parses text from a string to produce a Date. The method attempts to parse text starting at the index given by pos. If parsing succeeds, then the index of pos is updated to the index after the last character used (parsing does not necessarily use all charac

docs.oracle.com

Synchronization

Date formats are not synchronized. It is recommended to create separate format instances for each thread. If multiple threads access a format concurrently, it must be synchronized externally.

 

StackOverFlow의 관련 글 : stackoverflow.com/questions/6840803/why-is-javas-simpledateformat-not-thread-safe

 

Why is Java's SimpleDateFormat not thread-safe?

Please tell with a code example why is SimpleDateFormat not threadsafe. What is the problem in this class? Is The problem with format function of SimpleDateFormat? Please give a code which demons...

stackoverflow.com

 

따라서 다중 스레드 환경에서 동시에 하나의 SimpleDateFormat에 접근하는 경우 문제가 발생합니다.

이런 의도치 않은 결과물이 등장하는것을 막기 위해서는 SimpleDateFormat 객체를 매 스레드마다 새로 생성하거나, Java8부터 도입된 DateTimeFormatter나 joda.time의 DateTimeFormat를 사용해야 합니다.

Java8의 DateTimeFormatter 사용

import java.time.LocalDateTime
import java.time.format.DateTimeFormatter

import scala.concurrent._
import ExecutionContext.Implicits.global

val format = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
def fmtFuture(str: String): Future[LocalDateTime] = Future {
  LocalDateTime.parse(str, format)
}

val parseList: Seq[Future[LocalDateTime]] = (1 to 10).map(i => "2021-01-18 20:00:0" + i % 10).map(ts => fmtFuture(ts))
parseList foreach println
Thread.sleep(3000)

joda.time의 DateTimeFormat 사용

import org.joda.time.DateTime
import org.joda.time.format.DateTimeFormat

import scala.concurrent._
import ExecutionContext.Implicits.global

val format = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")
def fmtFuture(str: String): Future[DateTime] = Future {
  DateTime.parse(str, format)
}

val parseList: Seq[Future[DateTime]] = (1 to 10).map(i => "2021-01-18 20:00:0" + i % 10).map(ts => fmtFuture(ts))
parseList foreach println
Thread.sleep(3000)

 

실제 위 두 코드는 정상적인 결과물을 반환합니다.

반응형