log4j中看ThreadLocal用法

转载 2016年06月01日 18:22:21

关于ThreadLocal的帖子很多,说法也有好多不一致,每次举例都是hibernate里面的session,这周使用log4j做一些东东,发现log4j的代码用这个ThreadLocal可是清晰多了,而且很有意思。

首先从log4j里的MDC说起,这个是个键值对存储的容器,在里面加你的键值对,在配置文件配置你输出的格式,需要输出的内容时候取里面的键
如:

MDC.put("usr_id", usr_id);    
MDC.put("log_title", "网站访问记录");    
MDC.put("log_type", "记录");    
MDC.put("log_title", "网站访问记录");    
MDC.put("log_datetime", format.format(now));    
MDC.put("log_ip", log_ip); 

配置文件里面

log4j.appender.project-util-db.sql=insert into user_log (usr_id,log_title,log_category,log_type,log_datetime,log_ip) 
VALUES ('%X{usr_id}','%X{log_title}','%X{log_type}','%X{log_title}','%X{log_datetime}','%X{log_ip}') 

这个实例MDC.put那个logger实例也MDC.put,里面不是会很崩溃,会覆盖一些东西,越来越大之类的

/* 
 * Licensed to the Apache Software Foundation (ASF) under one or more 
 * contributor license agreements.  See the NOTICE file distributed with 
 * this work for additional information regarding copyright ownership. 
 * The ASF licenses this file to You under the Apache License, Version 2.0 
 * (the "License"); you may not use this file except in compliance with 
 * the License.  You may obtain a copy of the License at 
 *  
 *      http://www.apache.org/licenses/LICENSE-2.0 
 *  
 * Unless required by applicable law or agreed to in writing, software 
 * distributed under the License is distributed on an "AS IS" BASIS, 
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
 * See the License for the specific language governing permissions and 
 * limitations under the License. 
 */  

package org.apache.log4j;  

import java.util.Hashtable;  
import org.apache.log4j.helpers.Loader;  
import org.apache.log4j.helpers.ThreadLocalMap;  

/** 
   The MDC class is similar to the {@link NDC} class except that it is 
   based on a map instead of a stack. It provides <em>mapped 
   diagnostic contexts</em>. A <em>Mapped Diagnostic Context</em>, or 
   MDC in short, is an instrument for distinguishing interleaved log 
   output from different sources. Log output is typically interleaved 
   when a server handles multiple clients near-simultaneously. 

   <p><b><em>The MDC is managed on a per thread basis</em></b>. A 
   child thread automatically inherits a <em>copy</em> of the mapped 
   diagnostic context of its parent. 

   <p>The MDC class requires JDK 1.2 or above. Under JDK 1.1 the MDC 
   will always return empty values but otherwise will not affect or 
   harm your application. 

   @since 1.2 

   @author Ceki G&uuml;lc&uuml; */  
public class MDC {  

  final static MDC mdc = new MDC();  

  static final int HT_SIZE = 7;  

  boolean java1;  

  Object tlm;  

  private  
  MDC() {  
    java1 = Loader.isJava1();  
    if(!java1) {  
      tlm = new ThreadLocalMap();  
    }  
  }  

  /** 
     Put a context value (the <code>o</code> parameter) as identified 
     with the <code>key</code> parameter into the current thread's 
     context map. 

     <p>If the current thread does not have a context map it is 
     created as a side effect. 

   */  
  static  
  public  
  void put(String key, Object o) {  
     if (mdc != null) {  
         mdc.put0(key, o);  
     }  
  }  

  /** 
     Get the context identified by the <code>key</code> parameter. 

     <p>This method has no side effects. 
   */  
  static   
  public  
  Object get(String key) {  
    if (mdc != null) {  
        return mdc.get0(key);  
    }  
    return null;  
  }  

  /** 
     Remove the the context identified by the <code>key</code> 
     parameter. 

  */  
  static   
  public  
  void remove(String key) {  
    if (mdc != null) {  
        mdc.remove0(key);  
    }  
  }  


  /** 
   * Get the current thread's MDC as a hashtable. This method is 
   * intended to be used internally.   
   * */  
  public static Hashtable getContext() {  
    if (mdc != null) {  
        return mdc.getContext0();  
    } else {  
        return null;  
    }  
  }  

  /** 
   *  Remove all values from the MDC. 
   *  @since 1.2.16 
  */  
  public static void clear() {  
    if (mdc != null) {  
        mdc.clear0();  
    }  
  }  


  private  
  void put0(String key, Object o) {  
    if(java1 || tlm == null) {  
      return;  
    } else {  
      Hashtable ht = (Hashtable) ((ThreadLocalMap)tlm).get();  
      if(ht == null) {  
        ht = new Hashtable(HT_SIZE);  
        ((ThreadLocalMap)tlm).set(ht);  
      }      
      ht.put(key, o);  
    }  
  }  

  private  
  Object get0(String key) {  
    if(java1 || tlm == null) {  
      return null;  
    } else {         
      Hashtable ht = (Hashtable) ((ThreadLocalMap)tlm).get();  
      if(ht != null && key != null) {  
        return ht.get(key);  
      } else {  
        return null;  
      }  
    }  
  }  

  private  
  void remove0(String key) {  
    if(!java1 && tlm != null) {  
      Hashtable ht = (Hashtable) ((ThreadLocalMap)tlm).get();  
      if(ht != null) {  
        ht.remove(key);  
      }   
    }  
  }  


  private  
  Hashtable getContext0() {  
     if(java1 || tlm == null) {  
      return null;  
    } else {         
      return (Hashtable) ((ThreadLocalMap)tlm).get();  
    }  
  }  

  private  
  void clear0() {  
    if(!java1 && tlm != null) {  
      Hashtable ht = (Hashtable) ((ThreadLocalMap)tlm).get();  
      if(ht != null) {  
        ht.clear();  
      }   
    }  
  }  

}  

重点看里面的put方法

private  
void put0(String key, Object o) {  
  if(java1 || tlm == null) {  
    return;  
  } else {  
    Hashtable ht = (Hashtable) ((ThreadLocalMap)tlm).get();  
    if(ht == null) {  
      ht = new Hashtable(HT_SIZE);  
      ((ThreadLocalMap)tlm).set(ht);  
    }      
    ht.put(key, o);  
  }  
}

先调用ThreadLocalMap.get()获取,注意此处ThreadLocalMap是log4j自己写的继承自ThreadLocal的子类,但get,set方法还是没有覆盖的。
如下代码所示:

public T get() {  

     Thread t = Thread.currentThread();  

     ThreadLocalMap map = getMap(t);  
     if (map != null) {  
         ThreadLocalMap.Entry e = map.getEntry(this);  
         if (e != null)  
             return (T)e.value;  
     }  
     return setInitialValue();  
 }  

Thread t = Thread.currentThread();–>>取出当前线程
ThreadLocalMap map = getMap(t);—>>根据当前线程取出里面的变量return t.threadLocals;

ThreadLocalMap getMap(Thread t) {  
      return t.threadLocals;  
}  

Thread类里面定义

ThreadLocal.ThreadLocalMap threadLocals = null;  

可以看出ThreadLocalMap是ThreadLocal的一个内部类,不过这个六百多行的内部类确实不容易看
map.getEntry(this)–>> 在map不为空时候,取出里面存放的实体,为什么传this?
到这一步,已经从当前线程 当前对象 这两个纬度锁定到了这个map,对static class ThreadLocalMap这样一个静态内部类来说, 已经可以控制每个线程分配一个独立占用的虚拟内存地带了,如果其它线程要操作这个,就用这两个纬度去获取

再理一下这种方式的思路:
每个线程有一个ThreadLocalMap,这个是在线程本身就定义的,Thread里面有:

ThreadLocal.ThreadLocalMap threadLocals = null;  

那么每个线程里面可根据当前对象取出里面的实体。

这个实体看你自己使用什么就可以指定什么,反正是个object,MDC里面指定为hashtable

最终调用

public void set(T value) {  
      Thread t = Thread.currentThread();  
      ThreadLocalMap map = getMap(t);  
      if (map != null)  
          map.set(this, value);  
      else  
          createMap(t, value);  
  } 

log4j使用上述方式,保证调用MDC的线程实例,通过二个纬度确定里面有唯一一份object(hashtable)

Java学习记录--ThreadLocal使用案例

java学习记录–ThreadLocal使用案例标签(空格分隔): java最近整理公司项目,发现不少写的比较糟糕的地方,比如下面这个:public class DateUtil { priva...
  • u012706811
  • u012706811
  • 2016年11月19日 16:26
  • 1607

ThreadLocal的正确用法

ThreaLocal的JDK文档中说明:ThreadLocal instances are typically private static fields in classes that wish t...
  • vking_wang
  • vking_wang
  • 2013年11月06日 12:36
  • 38002

彻底理解ThreadLocal

ThreadLocal是什么 早在JDK 1.2的版本中就提供java.lang.ThreadLocal,ThreadLocal为解决多线程程序的并发问题提供了一种新的思路。使用这个工具类可以很简洁...
  • lufeng20
  • lufeng20
  • 2014年04月22日 16:59
  • 341288

ThreadLocal实现方式&使用介绍---无锁化线程封闭 (r)

http://blog.csdn.net/xieyuooo/article/details/8599266      关注哈下面的提问 虽然现在可以说很多程序员会用ThreadLocal,但是...
  • wangtaomtk
  • wangtaomtk
  • 2016年09月05日 16:54
  • 495

ThreadLocal的使用与代码示例(译)

原文:http://java.dzone.com/articles/java-thread-local-%E2%80%93-how-use  [翻墙看] Thread Local是一个有趣且实用的概念...
  • mylovepan
  • mylovepan
  • 2014年02月26日 09:21
  • 1900

ThreadLocal使用及原理解析

1.ThreadLocal干什么的?       我们知道,在多线程程序中,同一个线程在某个时间段只能处理一个任务.我们希望在这个时间段内,任务的某些变量能够和处理它的线程进行绑定,在任务需要使...
  • u011516800
  • u011516800
  • 2015年01月23日 15:07
  • 1380

log4j用法,太详细了,顶下!!!

我们先看一个最简单的示例: 【示例1】 项目结构: 【注:由于这里的多个项目公用一个jar档,我们可以创建一个专门放置jar档的Java工程,然后将jar档放到lib目录下。...
  • zjlolife
  • zjlolife
  • 2013年04月15日 21:16
  • 5146

在web项目中使用 ThreadLocal 要谨慎,使用不当容易造成内存溢出

这里使用的servlet容器是 tomcat如果在web项目中,使用 ThreadLocal 不当,会造成 OutOfMemoryError。说明原因前 1:先讲一下 ThreadLocal,The...
  • liang_love_java
  • liang_love_java
  • 2016年09月29日 13:56
  • 2162

ThreadLocal用法与实现原理

ThreadLocal用法与实现原理1. 对ThreadLocal的理解Java API中对ThreadLocal的描述是:该类提供了线程局部 (thread-local) 变量。这些变量不同于它们的...
  • zhliro
  • zhliro
  • 2015年07月20日 17:18
  • 1576

log4j使用方法及实例

使用log4j需要一个jar包 log4j-1.2.14.jar 选择一个任何一个版本的,只要不会在你的项目中造成版本冲突     使用log4j的一般步骤: 1.配置及读取配置文件  lo...
  • freehandme
  • freehandme
  • 2014年10月18日 09:34
  • 303
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:log4j中看ThreadLocal用法
举报原因:
原因补充:

(最多只允许输入30个字)