java learning record --ThreadLocal use cases (rpm)

This article is used by the SimpleDateFormat by concurrent threads unsafe environment optimization case, help understand ThreadLocal.


The company recently finishing the project, found that many relatively bad place to write, such as the following:

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();
        }
    }

}
Under Firstly:
function parseymdhms where the () uses a synchronized modification means that the operation is not thread-safe, so need to be synchronized, thread-safe can only be a SimpleDateFormat parse () method, view the source code, in SimpleDateFormat there is a global variable
protected Calendar calendar; 

a Date the parse () { 

    calendar.clear (); 

  ... // perform some operation, provided calendar date what 

  calendar.getTime (); // Get time calendar 

}

The clear () operation will cause the thread-safe.

In addition the use of synchronized keyword has a significant impact on performance, especially when multiple threads, each call parseymdhms methods will be synchronized judge, and synchronization overhead itself very large, so it is unreasonable solution.


ways to improve

Thread-safe multi-threaded stems from the use of shared variables cause, so here use ThreadLocal <SimpleDateFormat> to create a separate copy of the variable to each thread, first give the code, and then analyze the causes of this problem.

/ ** 
 * date tools (used ThreadLocal obtain SimpleDateFormat, other methods can be copied directly lang-Common) 
 * @author Niu of Li 
 * @date 2016/11/19 
 * / 
public  class the DateUtil { 

    Private  static the Map <String, ThreadLocal < >> = sdfMap the SimpleDateFormat new new the HashMap <String, the ThreadLocal <>> the SimpleDateFormat (); 

    Private  static Logger Logger = LoggerFactory.getLogger (the DateUtil. class ); 

    public  Final  static String MDHMSS = "MMddHHmmssSSS" ;
     public  Final  static String = YMDhms "yyyyMMddHHmmss " ;
     public Final  static String YMDHMS_ = "YYYY-the MM-dd HH: mm: SS" ;
     public  Final  static String YMD = "yyyyMMdd" ;
     public  Final  static String YMD_ = "YYYY-the MM-dd" ;
     public  Final  static String HMS = "HHmmss " ; 

    / ** 
     * the sdf example key in the map to obtain the corresponding thread 
     * @param pattern key in the map 
     * @return this example
      * / 
    Private  static the SimpleDateFormat getSdf ( Final String pattern) { 
        the ThreadLocal <the SimpleDateFormat> = sdfThreadsdfMap.get (pattern);
         IF (sdfThread == null ) {
             // double screening, preventing sdfMap was repeatedly put into value, and double lock single embodiment for the same reason 
            the synchronized (the DateUtil. class ) { 
                sdfThread = sdfMap.get (pattern);
                 IF (sdfThread == null ) { 
                    logger.debug ( "PUT SDF new new pattern of" pattern + + "to Map" ); 
                    sdfThread = new new the ThreadLocal <the SimpleDateFormat> () { 
                        @Override 
                        protected the SimpleDateFormat the initialValue () {
                            logger.debug ("Thread:" Thread.currentThread + () + "the init pattern:" + pattern);
                             return  new new the SimpleDateFormat (pattern); 
                        } 
                    }; 
                    sdfMap.put (pattern, sdfThread); 
                } 
            } 
        } 
        return sdfThread.get (); 
    } 

    / ** 
     * according to a specified pattern resolved date 
     * @param date to be parsed date 
     * @param pattern specified format 
     * @return parsed date instance
      * / 
    public  static a date parseDate (date String, String pattern) {
         IF (DATE == null ) {
             the throw  new new an IllegalArgumentException ( "Not of The DATE MUST BE null" ); 
        } 
        the try {
             return   getSdf (pattern) .parse (DATE); 
        } the catch (a ParseException E) { 
            e.printStackTrace (); 
            logger.error ( "parsed format is not supported:" + pattern); 
        } 
        return  null ; 
    } 
    / ** 
     * format pattern in the specified date 
     * @param dATE dATE to format 
     * @param pattern specified format 
     * @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);
        }
    }
}

test

In performing a main thread, the other two sub-thread execution, the same pattern is used

    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();
    }

Log Analysis

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

analysis

It can be seen sdfMap put into it once, but three times SimpleDateFormat is new, because the code has three threads. So this is why?

For each thread Thread, inside which there is a ThreadLocal.ThreadLocalMap threadLocalsglobal variable references, ThreadLocal.ThreadLocalMap which has a value corresponding to the ThreadLocal and save a picture is worth a thousand words, the structure is as follows:

 

 So for sdfMap, then changed the structure diagram on the next

 

 

? Well log analysis of why this is the case:

1. First, the first executionDateUtil.formatDate(new Date(),MDHMSS);

// first execution DateUtil.formatDate (new Date (), MDHMSS ) Analysis 
    Private  static the SimpleDateFormat getSdf ( Final String pattern) { 
        the ThreadLocal <the SimpleDateFormat> sdfThread = sdfMap.get (pattern);
         // obtained sdfThread is null, into the The if statement 
        if (sdfThread == null ) {
             the synchronized (DateUtil. class ) { 
                sdfThread = sdfMap.get (pattern);
                 // sdfThread still null, enter the if statement 
                if (sdfThread == null ) {
                     // print log
                    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);
                        }
                    };
                    
            }
                }set into such sdfMap//
                    sdfMap.put(pattern,sdfThread);
        }
        return sdfThread.get();
    }

Some may ask at this time, and there is no call set ThreadLocal method, then the value is set into the how of it?
This requires looking sdfThread.get () implementation:

    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();
    }

That is when the value does not exist will call setInitialValue () method, which calls initialValue () method, which is the way we cover.

The corresponding log printing.

put new sdf of pattern MMddHHmmssSSS to map
thread: Thread[main,5,main] init pattern: MMddHHmmssSSS

2. The second sub-thread executionDateUtil.formatDate(new Date(),MDHMSS);

// second child thread executed DateUtil.formatDate `(new new a Date (), MDHMSS);` 
    Private  static the SimpleDateFormat getSdf ( Final String pattern) { 
        the ThreadLocal <the SimpleDateFormat> = sdfThread ; sdfMap.get (pattern)
         // here to give sdfThread is not null, if the block is skipped 
        if (sdfThread == null ) {
             the synchronized (the DateUtil. class ) { 
                sdfThread = sdfMap.get (pattern);
                 if (sdfThread == null ) { 
                    logger.debug ( "PUT new new SDF pattern of "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();
    }

Analysis sdfThread.get ()

// second child thread executed DateUtil.formatDate `(new new a Date (), MDHMSS);` 
    public T GET () { 
        the Thread T ; Thread.currentThread = () // get the current sub-thread 
        ThreadLocalMap Map = the getMap (T );
         // child thread obtained map is null, if the block is skipped 
        if (map =! null ) { 
            ThreadLocalMap.Entry E = map.getEntry ( the this );
             if ! (E = null ) { 
                @SuppressWarnings ( "an unchecked " ) 
                T Result = (T) e.Value;
                 return Result; 
            }
        } 
        // direct initialization is performed, that is, call initialValue our coverage () method 
        return setInitialValue (); 
    }

The corresponding log:

Thread[Thread-0,5,main] init pattern: MMddHHmmssSSS

Similarly the second and third execution is similar to a direct call sdfThread.get (), then calls the initialValue () method, the corresponding log

Thread[Thread-1,5,main] init pattern: MMddHHmmssSSS

to sum up

在什么场景下比较适合使用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.

Reference Code:

https://github.com/nl101531/JavaWEB under Util-Demo

Reference:
http://www.importnew.com/21479.html
http://www.cnblogs.com/zemliu/archive/2013/08/29/3290585.html



Author: Bo abandoned _ This update personal blog
link: https: //www.jianshu.com/p/5675690b351e
Source: Jane books
are copyrighted by the author. Commercial reprint please contact the author authorized, non-commercial reprint please indicate the source.

Guess you like

Origin www.cnblogs.com/muxi0407/p/11818857.html