자바 ThreadLocal 사용 사례 상세 설명
10155 단어 자바ThreadLocal
최근 에 회사 프로젝트 를 정리 하면 서 비교적 나 쁜 부분 을 많이 발견 했다.예 를 들 어 다음 과 같은 것 이다.
public class DateUtil {
private final static SimpleDateFormat sdfyhm = new SimpleDateFormat(
"yyyyMMdd");
public synchronized static Date parseymdhms(String source) {
try {
return sdfyhm.parse(source);
} catch (ParseException e) {
e.printStackTrace();
return new Date();
}
}
}
우선 분석 해 보면:이 함수 parseymdhms()는 synchronized 수식 을 사 용 했 습 니 다.이 동작 은 스 레 드 가 안전 하지 않 기 때문에 동기 화가 필요 합 니 다.스 레 드 가 안전 하지 않 아 도 Simple DateFormat 의 parse()방법 만 사용 할 수 있 습 니 다.다음 소스 코드 를 보 세 요.Simple DateFormat 에 전역 변수 가 있 습 니 다.
protected Calendar calendar;
Date parse() {
calendar.clear();
... // , calendar
calendar.getTime(); // calendar
}
이 clear()작업 은 라인 이 안전 하지 않 습 니 다.또한 synchronized 키 워드 를 사용 하 는 것 은 성능 에 큰 영향 을 미 칩 니 다.특히 다 중 스 레 드 를 사용 할 때 parseymdhms 방법 을 호출 할 때마다 동기 화 판단 을 하고 동기 화 자체 의 비용 이 많이 들 기 때문에 불합리한 해결 방안 입 니 다.
개선 방법
스 레 드 가 안전 하지 않 은 것 은 다 중 스 레 드 가 공유 변 수 를 사용 해서 생 긴 것 입 니 다.그래서 여 기 는 ThreadLocal
/**
* ( ThreadLocal SimpleDateFormat, common-lang)
* @author Niu Li
* @date 2016/11/19
*/
public class DateUtil {
private static Map<String,ThreadLocal<SimpleDateFormat>> sdfMap = new HashMap<String, ThreadLocal<SimpleDateFormat>>();
private static Logger logger = LoggerFactory.getLogger(DateUtil.class);
public final static String MDHMSS = "MMddHHmmssSSS";
public final static String YMDHMS = "yyyyMMddHHmmss";
public final static String YMDHMS_ = "yyyy-MM-dd HH:mm:ss";
public final static String YMD = "yyyyMMdd";
public final static String YMD_ = "yyyy-MM-dd";
public final static String HMS = "HHmmss";
/**
* map key sdf
* @param pattern map key
* @return
*/
private static SimpleDateFormat getSdf(final String pattern){
ThreadLocal<SimpleDateFormat> sdfThread = sdfMap.get(pattern);
if (sdfThread == null){
// , sdfMap put ,
synchronized (DateUtil.class){
sdfThread = sdfMap.get(pattern);
if (sdfThread == null){
logger.debug("put new sdf of pattern " + pattern + " to map");
sdfThread = new ThreadLocal<SimpleDateFormat>(){
@Override
protected SimpleDateFormat initialValue() {
logger.debug("thread: " + Thread.currentThread() + " init pattern: " + pattern);
return new SimpleDateFormat(pattern);
}
};
sdfMap.put(pattern,sdfThread);
}
}
}
return sdfThread.get();
}
/**
* pattern
* @param date date
* @param pattern
* @return date
*/
public static Date parseDate(String date,String pattern){
if(date == null) {
throw new IllegalArgumentException("The date must not be null");
}
try {
return getSdf(pattern).parse(date);
} catch (ParseException e) {
e.printStackTrace();
logger.error(" :"+pattern);
}
return null;
}
/**
* pattern
* @param date date
* @param pattern
* @return
*/
public static String formatDate(Date date,String pattern){
if (date == null){
throw new IllegalArgumentException("The date must not be null");
}else {
return getSdf(pattern).format(date);
}
}
}
테스트주 스 레 드 에서 하 나 를 실행 하고,다른 두 개 는 하위 스 레 드 에서 실행 하 며,모두 같은 pattern 을 사용 합 니 다.
public static void main(String[] args) {
DateUtil.formatDate(new Date(),MDHMSS);
new Thread(()->{
DateUtil.formatDate(new Date(),MDHMSS);
}).start();
new Thread(()->{
DateUtil.formatDate(new Date(),MDHMSS);
}).start();
}
로그 분석
put new sdf of pattern MMddHHmmssSSS to map
thread: Thread[main,5,main] init pattern: MMddHHmmssSSS
thread: Thread[Thread-0,5,main] init pattern: MMddHHmmssSSS
thread: Thread[Thread-1,5,main] init pattern: MMddHHmmssSSS
분석 하 다.sdfpap put 가 한 번 들 어간 것 을 알 수 있 습 니 다.Simple DateFormat 은 new 에 세 번 들 어 갔 습 니 다.코드 에 세 개의 스 레 드 가 있 기 때 문 입 니 다.그렇다면 왜 일 까요?
모든 스 레 드 Thread 에 대해 내부 에 ThreadLocal.ThreadLocalMap threadLocals 의 전역 변수 참조 가 있 습 니 다.ThreadLocal.ThreadLocalMap 에는 이 ThreadLocal 과 대응 하 는 value 가 저장 되 어 있 습 니 다.그림 은 천 마디 를 이 깁 니 다.구조 도 는 다음 과 같 습 니 다.
그럼 sdfpap 에 대해 서 는 구조 도 를 변경 하 였 습 니 다.
1.먼저 DateUtil.formatDate(new Date(),MDHMSS)를 처음 실행 합 니 다.
// DateUtil.formatDate(new Date(),MDHMSS)
private static SimpleDateFormat getSdf(final String pattern){
ThreadLocal<SimpleDateFormat> sdfThread = sdfMap.get(pattern);
// sdfThread null, if
if (sdfThread == null){
synchronized (DateUtil.class){
sdfThread = sdfMap.get(pattern);
//sdfThread null, if
if (sdfThread == null){
//
logger.debug("put new sdf of pattern " + pattern + " to map");
// ThreadLocal , initialValue
sdfThread = new ThreadLocal<SimpleDateFormat>(){
@Override
protected SimpleDateFormat initialValue() {
logger.debug("thread: " + Thread.currentThread() + " init pattern: " + pattern);
return new SimpleDateFormat(pattern);
}
};
// sdfMap
sdfMap.put(pattern,sdfThread);
}
}
}
return sdfThread.get();
}
이 럴 때 ThreadLocal 의 set 방법 을 사용 하지 않 았 습 니 다.그러면 값 은 어떻게 설정 되 어 있 습 니까?sdfThread.get()의 실현 을 봐 야 합 니 다.
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
즉,값 이 존재 하지 않 을 때 setInitialValue()방법 을 사용 합 니 다.이 방법 은 initialValue()방법,즉 우리 가 덮어 쓰 는 방법 을 사용 합 니 다.대응 로그 인쇄.
put new sdf of pattern MMddHHmmssSSS to map
thread: Thread[main,5,main] init pattern: MMddHHmmssSSS
2.두 번 째 부분 스 레 드 에서 DateUtil.formatDate(new Date(),MDHMSS)를 실행 합 니 다.
// `DateUtil.formatDate(new Date(),MDHMSS);`
private static SimpleDateFormat getSdf(final String pattern){
ThreadLocal<SimpleDateFormat> sdfThread = sdfMap.get(pattern);
// sdfThread null, if
if (sdfThread == null){
synchronized (DateUtil.class){
sdfThread = sdfMap.get(pattern);
if (sdfThread == null){
logger.debug("put new sdf of pattern " + pattern + " to map");
sdfThread = new ThreadLocal<SimpleDateFormat>(){
@Override
protected SimpleDateFormat initialValue() {
logger.debug("thread: " + Thread.currentThread() + " init pattern: " + pattern);
return new SimpleDateFormat(pattern);
}
};
sdfMap.put(pattern,sdfThread);
}
}
}
// sdfThread.get()
return sdfThread.get();
}
sdfThread.get 분석()
// `DateUtil.formatDate(new Date(),MDHMSS);`
public T get() {
Thread t = Thread.currentThread();//
ThreadLocalMap map = getMap(t);
// map null, if
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
// , initialValue()
return setInitialValue();
}
대응 로그:Thread[Thread-1,5,main] init pattern: MMddHHmmssSSS
총결산
어떤 장면 에서 ThreadLocal 을 사용 하기에 적합 합 니까?stackoverflow 에서 괜 찮 은 대답 이 나 왔 습 니 다.
When and how should I use a ThreadLocal variable?
One possible (and common) use is when you have some object that is not thread-safe, but you want to avoid synchronizing access to that object (I'm looking at you, SimpleDateFormat). Instead, give each thread its own instance of the object.
참조 코드:
Util-Demo
참고 자료:
https://github.com/nl101531/JavaWEB
알 기 쉬 운 학습 자바 ThreadLocal
이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Is Eclipse IDE dying?In 2014 the Eclipse IDE is the leading development environment for Java with a market share of approximately 65%. but ac...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.