SimpleDateFormat thread safety problems and solutions can be solved with localdata

https://www.cnblogs.com/zemliu/p/3290585.html

https://www.cnblogs.com/peida/archive/2013/05/31/3070790.html

1. Cause

The SimpleDateFormat (hereinafter referred sdf) Calendar object class has an internal reference, which is used to store the sdf and related date information, e.g. sdf.parse (dateStr), sdf.format (date) like method parameters related to the incoming date String , Date, etc., are stored in a reference to the Friends Calendar. this will cause a problem if your sdf is static, then this will be shared between multiple sdf thread, but also share the Calendar references, and, observation sdf.parse () method, you will find the following call:

Copy the code

The parse DATE () { 

  calendar.clear (); // Cleanup calendar 

  ... // perform some operations date, the calendar is provided what 

  calendar.getTime (); // Get time calendar 

}

Copy the code

When this can cause problems is, if thread A calls sdf.parse (), and were calendar.clear () has not been executed calendar.getTime (), the thread B calls the sdf.parse (), which when the thread B executed also sdf.clear () method, thus leading to the thread a calendar data is emptied (in fact a, B are simultaneously cleared). or the a executed calendar.clear () when is suspended, this time B starts calling sdf.parse () and end smoothly i, date stored in the calendar this into a and B later date calendar settings

 

2. to reproduce the problem

Copy the code

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * @author zhenwei.liu created on 2013 13-8-29 下午5:35
 * @version $Id$
 */
public class DateFormatTest extends Thread {
    private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");

    private String name;
    private String dateStr;
    private boolean sleep;

    public DateFormatTest(String name, String dateStr, boolean sleep) {
        this.name = name;
        this.dateStr = dateStr;
        this.sleep = sleep;
    }

    @Override
    public void run() {

        Date date = null;

        if (sleep) {
            try {
                TimeUnit.MILLISECONDS.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        try {
            date = sdf.parse(dateStr);
        } catch (ParseException e) {
            e.printStackTrace();
        }

        System.out.println(name + " : date: " + date);
    }

    public static void main(String[] args) throws InterruptedException {

        ExecutorService executor = Executors.newCachedThreadPool (); 

        started sdf.parse () // A will after 2S SLEEP 
        Executor.execute (new new DateFormatTest ( "A", "1991-09-13", to true)); 
        // play B a breakpoint, the method will be stuck in the middle 
        Executor.execute (new new DateFormatTest ( "B", "2013-09-13", to false)); 

        executor.shutdown (); 
    } 
}

Copy the code

Use this code Debug mode execution, and marked with a breakpoint in sdf.parse () method in

Copy the code

the parse () { 

    calendar.clear () 

    // hit a breakpoint where 

    calendar.getTime () 

}

Copy the code

process:

1) A first thread will enter sleep after run up

2) B threads run up the card at the breakpoint

3) A thread wake up, execution calendar.clear (), and the date is set sdf.calendar 1991-09-13, calendar at this time are the AB 1991-09-13

4) Let the breakpoint continue, the following output

A : date: Fri Sep 13 00:00:00 CDT 1991
B : date: Fri Sep 13 00:00:00 CDT 1991

 This is not what we expect results

3. Solution

The most simple solution we can get rid of the static, so that each new thread will have its own sdf example, in order to avoid thread-safety issues

However, using this method, in the case of high concurrency will be a lot of new sdf and destroy sdf, this can be very resource-intensive

In the case of concurrent requests the implementation of tasks and threads about the site can be understood as follows

Such as the maximum number Thread Tomcat thread pool is 4, you need to perform the task now has 1000 (understood to have 1,000 users point to a function of your website),

This 1000 mission will be used in processing class date functions we write

A) If you say date functions like processing method using a new SimpleDateFormat, this will be the creation and destruction of 1000 sdf

B) Java ThreadLocal to provide a solution, the way it works is that each thread will be only one instance, that is to say we finished the implementation of the 1000 mission, a total of only four instances of sdf.

Moreover, it does not issue multiple concurrent threads, because a single thread is definitely the order of tasks, such as Thread # 1 is responsible for the implementation of Task # 1- # 250, then he is sequentially executed Task # 1- # 250

The Thread # 2 has its own sdf instance, he is also the order of tasks Task # 251- # 500, and so on

III. Solution

  1. Create a new instance of time needed:

Copy the code

package com.peidasoft.dateformat;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class DateUtil {
    
    public static  String formatDate(Date date)throws ParseException{
         SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        return sdf.format(date);
    }
    
    public static Date parse(String strDate) throws ParseException{
         SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        return sdf.parse(strDate);
    }
}

Copy the code

  Description: Create a new instance of SimpleDateFormat is used where it is needed, no matter what time, there will be thread-safety issues by the shared object into a local private can avoid multi-threading issues, but also increased the burden of creating an object. In general, this is actually not very significant impact on the performance ratio.

  2. Sync: Synchronization Object SimpleDateFormat

Copy the code

package com.peidasoft.dateformat;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class DateSyncUtil {

    private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
      
    public static String formatDate(Date date)throws ParseException{
        synchronized(sdf){
            return sdf.format(date);
        }  
    }
    
    public static Date parse(String strDate) throws ParseException{
        synchronized(sdf){
            return sdf.parse(strDate);
        }
    } 
}

Copy the code

  Description: When there are many threads, when a thread calls this method, other threads want to call this method will block, to a certain extent multi-threaded large amount of time on performance.

  3. Use ThreadLocal: 

Copy the code

package com.peidasoft.dateformat;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class ConcurrentDateUtil {

    private static ThreadLocal<DateFormat> threadLocal = new ThreadLocal<DateFormat>() {
        @Override
        protected DateFormat initialValue() {
            return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        }
    };

    public static Date parse(String dateStr) throws ParseException {
        return threadLocal.get().parse(dateStr);
    }

    public static String format(Date date) {
        return threadLocal.get().format(date);
    }
}

Copy the code

  Another way to write:

Copy the code

package com.peidasoft.dateformat;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class ThreadLocalDateUtil {
    private static final String date_format = "yyyy-MM-dd HH:mm:ss";
    private static ThreadLocal<DateFormat> threadLocal = new ThreadLocal<DateFormat>(); 
 
    public static DateFormat getDateFormat()   
    {  
        DateFormat df = threadLocal.get();  
        if(df==null){  
            df = new SimpleDateFormat(date_format);  
            threadLocal.set(df);  
        }  
        return df;  
    }  

    public static String formatDate(Date date) throws ParseException {
        return getDateFormat().format(date);
    }

    public static Date parse(String strDate) throws ParseException {
        return getDateFormat().parse(strDate);
    }   
}

Copy the code

  Description: Use ThreadLocal, but also the shared variable becomes exclusive, exclusive thread certainly exclusive method than in a concurrent environment can reduce a lot of the overhead of creating objects. If is relatively high on the performance requirements, it is generally recommended to use this method.

 

Here is an example using ThreadLocal solve the problem of multi-threaded sdf

 

Guess you like

Origin blog.csdn.net/zhuchunyan_aijia/article/details/93618123