log4j中看ThreadLocal用法

来源:互联网 发布:云来网络 编辑:程序博客网 时间:2024/06/06 12:28

关于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)

0 0
原创粉丝点击