前言

不知道你有没有在写WEB项目的时候,在Service或DAO里面,想要取得用户信息(如权限验证),而不得不将相关数据做为参数一层一层的传递,这时候你是不是想有一个方式,可以直接得到该信息?

不知道你有没有在成员变量中放置一些公共数据,但又不能保证线程安全的情况呢?

ThreadLocal你值得拥有,它能完美的帮助你完成你前面的设想。

When? Where? Why?

对于ThreadLocal,相信如果没有了解及使用过的朋友,一定会有这3W问题,下面就根据自己遇到的情况,说一下相关的理解。

适用的情况

  1. 当一个对象不是线程安全的,但你又不想用同步锁的时候。
  2. 在同一个线程中,不通过传参,但想取到数据的情况。比如Web项目,一个操作涉及到很多层的很多方法,需要记录操作请求,比如哪个用户操作的,如果作为参数传输当然是可以的,只是有了ThreadLocal,就没必要这样作了。
  3. Spring创建的对象的规则,默认只创建一个实例,在这样的情况下,成员变量就不是线程安全的,如果想在方法中调用成员变量的数据时。

How to use ThreadLocal

codelogger-web为例,我定义了一个MarvelServlet的类:

public class MarvelServlet {

  private HttpServletRequest request;

  private HttpServletResponse response;

    /* getters and setters here */

}

里面存放了Web请求的request和response,然后定义了一个MarvelFilter类:

public abstract class MarvelFilter<T extends MarvelServlet> implements Filter {

  private static ThreadLocal<MarvelServlet> filterThreadLocal = new ThreadLocal<MarvelServlet>();

  public static <T extends MarvelServlet> T getMarvelServlet() {

    @SuppressWarnings("unchecked")
    T marvelServlet = (T) filterThreadLocal.get();
    return marvelServlet;
  }

  public abstract T buildMarvelServlet();

  public abstract void doCustomFilter(ServletRequest request, ServletResponse response,
    FilterChain filterChain) throws IOException, ServletException;

  @Override
  public final void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
    throws IOException, ServletException {

    MarvelServlet marvelServlet = buildMarvelServlet();
    marvelServlet.setRequest((HttpServletRequest) request);
    marvelServlet.setResponse((HttpServletResponse) response);
    filterThreadLocal.set(marvelServlet);
    doCustomFilter(request, response, filterChain);
    filterChain.doFilter(request, response);
  }
}

然后把这个Filter加到web.xml中去:

<filter>
    <filter-name>marvelFilter</filter-name>
    <filter-class>org.codelogger.web.filter.MarvelFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>marvelFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

这样,当用户发送一个请求过来时,我们在Service或DAO中,只需要调用

MarvelServlet marvelServlet = MarvelFilter.getMarvelServlet();

就能得到当前请求的request和response了,这样省去了参数的传递,减少依赖关系和代码维护。

下面再例一个多线程共享同一个成员变量的示例:

import static java.lang.String.format;

public class ThreadLocalTest implements Runnable {

  private static ThreadLocal<Integer> counter = new ThreadLocal<Integer>() {

    @Override
    protected Integer initialValue() {

      return 0;
    }
  };

  @Override
  public void run() {

    for (int i = 0; i < 3; i++) {

      System.out.println(format("%s: %s", Thread.currentThread().getName(), getNumber()));
    }
  }

  public Integer getNumber() {

    counter.set(counter.get() + 1);
    return counter.get();
  }

  public static void main(final String[] args) {

    ThreadLocalTest threadLocalTest = new ThreadLocalTest();

    Thread thread1 = new Thread(threadLocalTest);
    Thread thread2 = new Thread(threadLocalTest);
    Thread thread3 = new Thread(threadLocalTest);

    thread1.start();
    thread2.start();
    thread3.start();
  }
}

输出的结果为:

Thread-0: 1
Thread-1: 1
Thread-0: 2
Thread-0: 3
Thread-2: 1
Thread-1: 2
Thread-1: 3
Thread-2: 2
Thread-2: 3

后记

想想以前为了做到线程安全所做出的种种,如synchronized,static的ConcurrentHashMap,现在看来,也是一种难得的经历,经历过总是有收获的。

平常多看看相关的资讯,能帮助我们少走不少弯路。


这是一个自己实现的伪ThreadLocal,见笑了:

public class MyThreadLocal<T> {

  private Map<Thread, T> container = new ConcurrentHashMap<Thread, T>();

  public void set(final T value) {

    container.put(Thread.currentThread(), value);
  }

  public T get() {

    Thread thread = Thread.currentThread();
    T value = container.get(thread);
    if (value == null && !container.containsKey(thread)) {
      value = initialValue();
      container.put(thread, value);
    }
    return value;
  }

  public void remove() {

    container.remove(Thread.currentThread());
  }

  protected T initialValue() {

    return null;
  }
}

当然,这个MyThreadLocal并不包含Thread的监听并做出相应的处理,所以在Thread被销毁的时候,其中的数据一样存在,并且不会被GC回收,所以要走的路还很远。




如果您觉得这篇文章对您有所帮助, 点我, 可以请我喝杯咖啡。
< 支付宝 | 微信 >
Published with Hexo and Theme by Kael
Flag Counter
X