자바 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   
}
또한 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();
  }
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();
  }
대응 로그 인쇄.
put new sdf of pattern MMddHHmmssSSS to map
thread: Thread[main,5,main] init pattern: MMddHHmmssSSS
//         `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();
  }
//         `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에 따라 라이센스가 부여됩니다.