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