ThreadLocal简介

ThreadLocal用法

Java中线程的同步机制保证了多线程访问共享变量的安全性,通常我们使用synchronized关键字来实现。在多个线程对共享变量进行读写操作时,同步锁保证了同一时间只有一个线程对共享变量进行操作,概括地说,这是一种“以时间换空间”的解决策略。

在JDK1.2中引入了ThreadLocal类来提供了一种“以空间换时间”的同步解决策略。ThreadLocal内部维护了一份类似Map的静态变量ThreadLocalMap,其中key为当前线程,value为共享变量。JDK1.5引入泛型,ThreadLocal也同时支持泛型。

其具体实现如下

public class ThreadLocal {
  /**
   * ThreadLocals rely on per-thread hash maps attached to each thread
   * (Thread.threadLocals and inheritableThreadLocals).  The ThreadLocal
   * objects act as keys, searched via threadLocalHashCode.  This is a
   * custom hash code (useful only within ThreadLocalMaps) that eliminates
   * collisions in the common case where consecutively constructed
   * ThreadLocals are used by the same threads, while remaining well-behaved
   * in less common cases.
   */
  private final int threadLocalHashCode = nextHashCode();

  /**
   * The next hash code to be given out. Accessed only by like-named method.
   */
  private static int nextHashCode = 0;

  /**
   * The difference between successively generated hash codes - turns
   * implicit sequential thread-local IDs into near-optimally spread
   * multiplicative hash values for power-of-two-sized tables.
   */
  private static final int HASH_INCREMENT = 0x61c88647;

  /**
   * Compute the next hash code. The static synchronization used here
   * should not be a performance bottleneck. When ThreadLocals are
   * generated in different threads at a fast enough rate to regularly
   * contend on this lock, memory contention is by far a more serious
   * problem than lock contention.
   */
  private static synchronized int nextHashCode() {
      int h = nextHashCode;
      nextHashCode = h + HASH_INCREMENT;
      return h;
  }

  /**
   * Creates a thread local variable.
   */
  public ThreadLocal() {
  }

  /**
   * Returns the value in the current thread's copy of this thread-local
   * variable.  Creates and initializes the copy if this is the first time
   * the thread has called this method.
   *
   * @return the current thread's value of this thread-local
   */
  public T get() {
      Thread t = Thread.currentThread();
      ThreadLocalMap map = getMap(t);
      if (map != null)
          return (T)map.get(this);

      // Maps are constructed lazily.  if the map for this thread
      // doesn't exist, create it, with this ThreadLocal and its
      // initial value as its only entry.
      T value = initialValue();
      createMap(t, value);
      return value;
  }

  /**
   * Sets the current thread's copy of this thread-local variable
   * to the specified value.  Many applications will have no need for
   * this functionality, relying solely on the {@link #initialValue}
   * method to set the values of thread-locals.
   *
   * @param value the value to be stored in the current threads' copy of
   *        this thread-local.
   */
  public void set(T value) {
      Thread t = Thread.currentThread();
      ThreadLocalMap map = getMap(t);
      if (map != null)
          map.set(this, value);
      else
          createMap(t, value);
  }

  /**
   * Get the map associated with a ThreadLocal. Overridden in
   * InheritableThreadLocal.
   *
   * @param  t the current thread
   * @return the map
   */
  ThreadLocalMap getMap(Thread t) {
      return t.threadLocals;
  }

  /**
   * Create the map associated with a ThreadLocal. Overridden in
   * InheritableThreadLocal.
   *
   * @param t the current thread
   * @param firstValue value for the initial entry of the map
   * @param map the map to store.
   */
  void createMap(Thread t, T firstValue) {
      t.threadLocals = new ThreadLocalMap(this, firstValue);
  }

  .......

  /**
   * ThreadLocalMap is a customized hash map suitable only for
   * maintaining thread local values. No operations are exported
   * outside of the ThreadLocal class. The class is package private to
   * allow declaration of fields in class Thread.  To help deal with
   * very large and long-lived usages, the hash table entries use
   * WeakReferences for keys. However, since reference queues are not
   * used, stale entries are guaranteed to be removed only when
   * the table starts running out of space.
   */
  static class ThreadLocalMap {

  ........

  }

}

从中很清晰的可以看出,多个线程拥有自己一份单独的ThreadLocalMap,共享变量对于每个线程都是单独的一份,因此不会造成线程的安全问题。

JDBC的ConnectionManager类就是以这种方式来实现数据库连接Connection对象线程隔离。

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public class ConnectionManager {

    private static ThreadLocal connectionHolder = new ThreadLocal() {
        @Override
        protected Connection initialValue() {
            Connection conn = null;
            try {
                conn = DriverManager.getConnection(
                        "jdbc:mysql://localhost:3306/test", "username",
                        "password");
            } catch (SQLException e) {
                e.printStackTrace();
            }
            return conn;
        }
    };

    public static Connection getConnection() {
        return connectionHolder.get();
    }

    public static void setConnection(Connection conn) {
        connectionHolder.set(conn);
    }
}  

但是,有些情况ThreadLocal可能并不适用,例如存储大量数据的共享变量,或共享变量只能被创建一次时,就只能通过synchronized来实现了。

推荐阅读:https://my.oschina.net/lichhao/blog/111362

你可能感兴趣的:(ThreadLocal简介)