Application Scenario Analysis of ThreadLocal in java

  • 2021-08-21 20:27:06
  • OfStack

When it comes to thread safety, we can solve it through ThreadLocal. But as a powerful variable, its application scenarios are far more than that. In various frameworks, we can still use them to manage them. At the same time, when using ThreadLocal, we should pay attention to the problem of memory leakage. Let's analyze these two points and show the corresponding code.

1. Applications in various frameworks

ThreadLocal is used to manage connections in the transaction management of Spring framework, and each thread is a separate connection, which cannot affect the transaction process or result of other threads when the transaction fails. There is also ORM framework that everyone has heard and heard, Mybatis is also managed by ThreadLocal, and so is SqlSession.


//Spring TransactionSynchronizationManager Class 
@Override
protected void doBegin(Object transaction, TransactionDefinition definition) {
  DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
  Connection con = null;
  try {
    // Omitted here N Line code 
    if (txObject.isNewConnectionHolder()) {
      // Bind a database to connect to a thread 
TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());
    }
  }
  catch (Throwable ex) {
    if (txObject.isNewConnectionHolder()) {
      // Remove the connection from the thread when an exception occurs 
      DataSourceUtils.releaseConnection(con, obtainDataSource());
      txObject.setConnectionHolder(null, false);
    }
    throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex);
  }
}

2. Prevent memory leaks

Usually, we use the following way to operate ThreadLocal, and after using threadlocal, we must drop remove to prevent memory leakage.


private static final ThreadLocal<LoginUser> loginUserLocal = new ThreadLocal<LoginUser>();
public static LoginUser getLoginUser() {
  return loginUserLocal.get();
}
public static void setLoginUser(LoginUser loginUser) {
  loginUserLocal.set(loginUser);
}
public static void clear() {
  loginUserLocal.remove();
}
// After use 1 Be sure to clean up to prevent memory leakage 
try{
  loginUserLocal.set(loginUser);
  // Execute other business logic 
}finally{
  loginUserLocal.remove();
}

ThreadLocal instance extension in java:


/**
 *  Date tool class ( Used ThreadLocal Get SimpleDateFormat, Other methods can be copied directly 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";

 /**
  *  According to map In key Object for the corresponding thread sdf Instances 
  * @param pattern map In key
  * @return  The instance 
  */
 private static SimpleDateFormat getSdf(final String pattern){
  ThreadLocal<SimpleDateFormat> sdfThread = sdfMap.get(pattern);
  if (sdfThread == null){
   // Double test , Prevent sdfMap Be repeatedly put Entry value , And double lock singletons are 1 Like 
   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();
 }

 /**
  *  As specified pattern Resolution date 
  * @param date  To parse date
  * @param pattern  Specify format 
  * @return  After parsing date Instances 
  */
 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(" The parsed format is not supported :"+pattern);
  }
  return null;
 }
 /**
  *  As specified pattern Formatting Date 
  * @param date  To format date
  * @param pattern  Specify format 
  * @return  Parsed format 
  */
 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);
  }
 }
}


Related articles: